website: big update with samples and vf stuff
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"head": {
|
||||
"checkSumAdjustment": 3496267821,
|
||||
"created": 3563720514,
|
||||
"checkSumAdjustment": 2438927683,
|
||||
"created": 3621707122,
|
||||
"flags": 27,
|
||||
"fontDirectionHint": 2,
|
||||
"fontRevision": 3.0,
|
||||
@ -12,16 +12,16 @@
|
||||
"macStyle": [],
|
||||
"macStyle_raw": 0,
|
||||
"magicNumber": 1594834165,
|
||||
"modified": 3620507819,
|
||||
"modified": 3621707136,
|
||||
"tableVersion": 1.0,
|
||||
"unitsPerEm": 2816,
|
||||
"xMax": 3756,
|
||||
"xMax": 4524,
|
||||
"xMin": -2080,
|
||||
"yMax": 3012,
|
||||
"yMin": -1080
|
||||
},
|
||||
"hhea": {
|
||||
"advanceWidthMax": 4004,
|
||||
"advanceWidthMax": 4912,
|
||||
"ascent": 2708,
|
||||
"caretOffset": 0,
|
||||
"caretSlopeRise": 1,
|
||||
@ -31,18 +31,18 @@
|
||||
"metricDataFormat": 0,
|
||||
"minLeftSideBearing": -2080,
|
||||
"minRightSideBearing": -1440,
|
||||
"numberOfHMetrics": 2183,
|
||||
"numberOfHMetrics": 2283,
|
||||
"tableVersion": 65536,
|
||||
"xMaxExtent": 3756
|
||||
"xMaxExtent": 4524
|
||||
},
|
||||
"id": "Inter UI Regular:2018:b54a3a10",
|
||||
"id": "Inter UI Regular:2018:32cdb6ede",
|
||||
"name": "InterUI-Regular",
|
||||
"names": {
|
||||
"copyright": "Copyright 2018 The Inter UI project authors",
|
||||
"designer": "Rasmus Andersson",
|
||||
"designerURL": "https://rsms.me/",
|
||||
"familyName": "Inter UI",
|
||||
"fontId": "Inter UI Regular:2018:b54a3a10",
|
||||
"fontId": "Inter UI Regular:2018:32cdb6ede",
|
||||
"fullName": "Inter UI Regular",
|
||||
"licenseDescription": "OFL 1.1 (SIL Open Font License, Version 1.1)",
|
||||
"licenseURL": "http://scripts.sil.org/OFL",
|
||||
@ -51,7 +51,7 @@
|
||||
"subfamilyName": "Regular",
|
||||
"trademark": "Inter UI is a trademark of rsms.",
|
||||
"vendorURL": "https://rsms.me/",
|
||||
"version": "3.0;b54a3a10"
|
||||
"version": "3.0;32cdb6ede"
|
||||
},
|
||||
"os/2": {
|
||||
"achVendID": "RSMS",
|
||||
@ -95,7 +95,7 @@
|
||||
"usWinAscent": 2708,
|
||||
"usWinDescent": 660,
|
||||
"version": 4,
|
||||
"xAvgCharWidth": 1697,
|
||||
"xAvgCharWidth": 1675,
|
||||
"yStrikeoutPosition": 1024,
|
||||
"yStrikeoutSize": 256,
|
||||
"ySubscriptXOffset": 0,
|
||||
|
57
docs/_includes/ctxedit.html
Normal file
@ -0,0 +1,57 @@
|
||||
{%
|
||||
|
||||
capture url_root
|
||||
%}{% if site.safe == false %}/{% else %}/inter/{% endif
|
||||
%}{%
|
||||
endcapture %}{%
|
||||
|
||||
for file in site.static_files %}{%
|
||||
assign _path = file.path | remove_first: "/inter" %}{%
|
||||
if _path == "/res/ctxedit.js" %}{%
|
||||
assign ctxedit_js_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
|
||||
elsif _path == "/res/ctxedit.css" %}{%
|
||||
assign ctxedit_css_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
|
||||
endif %}{%
|
||||
endfor
|
||||
|
||||
%}
|
||||
<link rel="stylesheet" href="{{url_root}}res/ctxedit.css?v={{ ctxedit_css_v }}">
|
||||
<div id="ctxedit-ui" class="styled-inputs-neg" contenteditable="false" tabindex="1">
|
||||
<div class="wrapper">
|
||||
<div class="control popup">
|
||||
<select data-binding="style">
|
||||
<option value="regular" default>Regular</option>
|
||||
<option value="italic">Italic</option>
|
||||
<option value="medium">Medium</option>
|
||||
<option value="medium-italic">Medium Italic</option>
|
||||
<option value="semi-bold">Semi Bold</option>
|
||||
<option value="semi-bold-italic">Semi Bold Italic</option>
|
||||
<option value="bold">Bold</option>
|
||||
<option value="bold-italic">Bold Italic</option>
|
||||
<option value="extra-bold">Extra Bold</option>
|
||||
<option value="extra-bold-italic">Extra Bold Italic</option>
|
||||
<option value="black">Black</option>
|
||||
<option value="black-italic">Black Italic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="control range">
|
||||
<img title="Size" class="icon" src="icons/font-size.svg">
|
||||
<input type="range" min="8" max="150" step="1" data-binding="size">
|
||||
</div>
|
||||
<div class="control range">
|
||||
<img title="Tracking/letter-spacing in EM" class="icon" src="icons/letter-spacing.svg">
|
||||
<input type="range" min="-0.1" max="0.1" step="0.001" data-binding="tracking">
|
||||
</div>
|
||||
<div class="control range">
|
||||
<img title="Line height" class="icon" src="icons/line-height.svg">
|
||||
<input type="range" value="1.2" min="0.7" max="2" step="0.01" data-binding="lineHeight">
|
||||
</div>
|
||||
<!-- <div class="control color">
|
||||
<input type="color" value="#111" data-binding="color">
|
||||
</div> -->
|
||||
<!-- <div class="control button features-button" title="Features..."></div> -->
|
||||
<div class="control button reset-button" title="Reset"></div>
|
||||
<div class="control button dismiss-button" title="Close editor"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="{{url_root}}res/ctxedit.js?v={{ ctxedit_js_v }}"></script>
|
@ -59,6 +59,13 @@ endfor
|
||||
<meta property="og:type" content="product">
|
||||
<meta property="og:locale" content="en_US" />
|
||||
</head>
|
||||
<script>
|
||||
(function(u){
|
||||
if (u.indexOf('Safari/') != -1 && u.indexOf('Chrome/') == -1 && u.indexOf('Chromium/') == -1) {
|
||||
document.documentElement.classList.add('safari')
|
||||
}
|
||||
})(navigator.userAgent);
|
||||
</script>
|
||||
<body>
|
||||
<div id="hud-notification"><div class="msg">Hello</div></div>
|
||||
<script src="{{url_root}}res/base.js?v={{ base_js_v }}"></script>
|
||||
|
@ -2,6 +2,11 @@
|
||||
set -e
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
if [ "$1" == "-h" ]; then
|
||||
echo "usage: $0 [<bindaddr>]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -s lab/fonts ]; then
|
||||
rm -f lab/fonts
|
||||
ln -fs ../../build/fonts lab/fonts
|
||||
@ -15,4 +20,9 @@ fi
|
||||
# For live testing with fonts, you'll instead want to use docs/lab/serve.py
|
||||
rm -rf _site
|
||||
|
||||
jekyll serve --limit_posts 20 --watch --host 127.0.0.1 --port 3002
|
||||
BINDADDR=127.0.0.1
|
||||
if [ "$1" != "" ]; then
|
||||
BINDADDR=$1
|
||||
fi
|
||||
|
||||
jekyll serve --limit_posts 20 --watch --host "$BINDADDR" --port 3002
|
||||
|
@ -89,8 +89,8 @@ endfor
|
||||
<option value="italic">Italic</option>
|
||||
<option value="medium" default>Medium</option>
|
||||
<option value="medium-italic">Medium Italic</option>
|
||||
<option value="semi-bold" default>Extra Bold</option>
|
||||
<option value="semi-bold-italic">Extra Bold Italic</option>
|
||||
<option value="semi-bold" default>Semi Bold</option>
|
||||
<option value="semi-bold-italic">Semi Bold Italic</option>
|
||||
<option value="bold" default>Bold</option>
|
||||
<option value="bold-italic">Bold Italic</option>
|
||||
<option value="extra-bold" default>Extra Bold</option>
|
||||
|
BIN
docs/font-files/Inter-UI-italic.var.ttf
Normal file
BIN
docs/font-files/Inter-UI-italic.var.woff2
Normal file
BIN
docs/font-files/Inter-UI-upright.var.ttf
Normal file
BIN
docs/font-files/Inter-UI-upright.var.woff2
Normal file
BIN
docs/font-files/Inter-UI.var.ttf
Normal file
230997
docs/glyphs/metrics.json
108
docs/index-var.css
Normal file
@ -0,0 +1,108 @@
|
||||
.variable-sample {
|
||||
--var-size: 128px;
|
||||
--var-weight: 600;
|
||||
--var-slant: 0;
|
||||
--var-letterSpacing: -0.03em;
|
||||
--var-lineHeight: 1.1;
|
||||
text-align: center;
|
||||
}
|
||||
@supports (font-variation-settings: normal) {
|
||||
.variable-sample {
|
||||
font-family: 'Inter UI var', sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
.variable {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.variable label {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.variable .unsupported-message {
|
||||
opacity: 0.3;
|
||||
border: 2px solid #999;
|
||||
border-radius:4px;
|
||||
text-align: center;
|
||||
padding: 1em 1em;
|
||||
display: none;
|
||||
margin: 1em auto;
|
||||
width: 60%;
|
||||
}
|
||||
.variable .unsupported-message.active {
|
||||
display: block;
|
||||
}
|
||||
.variable .unsupported-message p {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.variable .ctrl {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: stretch;
|
||||
/*border: 1px solid #ddd; border-radius:4px;*/
|
||||
/*border-top: 1px solid #ddd;*/
|
||||
border-bottom: 1px solid #ddd;
|
||||
color: #333;
|
||||
padding:20px 20px 20px 20px;
|
||||
}
|
||||
|
||||
.variable .unsupported-message.active + .ctrl {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.variable .ctrl .ctrlrow {
|
||||
flex: 1 0 auto;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding:5px;
|
||||
}
|
||||
.variable .ctrl input {
|
||||
margin: 0 8px 0 0;
|
||||
}
|
||||
.variable .ctrl input[type="range"] {
|
||||
margin-left: 8px;
|
||||
flex: 0 0 auto;
|
||||
width: 150px;
|
||||
}
|
||||
.variable .ctrl label {
|
||||
display: flex;
|
||||
flex: 0 1 auto;
|
||||
align-items: center;
|
||||
margin-right: 20px;
|
||||
/*background:lightpink;*/
|
||||
}
|
||||
.variable .ctrl label.disabled {
|
||||
color: rgba(0,0,0,0.3);
|
||||
}
|
||||
.variable .ctrl label.tight {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
.variable .ctrl label:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
.variable .ctrl label .label {
|
||||
min-width: 65px;
|
||||
}
|
||||
.variable .ctrl label .label.var {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.variable-sample-row {
|
||||
padding-top:0;
|
||||
}
|
||||
.variable-sample-row.black .variable-sample {
|
||||
color: white;
|
||||
}
|
||||
.variable-sample {
|
||||
color: #030508;
|
||||
padding-top: 40px;
|
||||
max-width: 100%;
|
||||
font-size: var(--var-size);
|
||||
letter-spacing: var(--var-letterSpacing);
|
||||
line-height: var(--var-lineHeight);
|
||||
font-variation-settings: 'wght' var(--var-weight), 'slnt' var(--var-slant);
|
||||
}
|
191
docs/index-var.js
Normal file
@ -0,0 +1,191 @@
|
||||
(function(){
|
||||
|
||||
var root = document.querySelector('div.variable')
|
||||
var sample = document.querySelector('.variable-sample')
|
||||
var animateCheckbox = document.querySelector('[name="animate"]')
|
||||
|
||||
|
||||
var ui = {
|
||||
state: {
|
||||
weight: 0,
|
||||
slant: 0,
|
||||
size: 0, // px
|
||||
letterSpacing: 0, // em
|
||||
lineHeight: 0,
|
||||
},
|
||||
|
||||
variable: {
|
||||
weight: true,
|
||||
slant: true,
|
||||
},
|
||||
|
||||
formatters: {
|
||||
size(v) { return `${v}px` },
|
||||
letterSpacing(v) { return `${v}em` },
|
||||
},
|
||||
|
||||
inputs: {
|
||||
// populated by init()
|
||||
},
|
||||
|
||||
init() {
|
||||
let s = getComputedStyle(sample)
|
||||
|
||||
// We test for variable-font support by, in CSS, conditionally using
|
||||
// a different font family name when VF is supported. Then we test for
|
||||
// what font family name is in effect. If it's the "var" one, we can be
|
||||
// fairly certain that variable fonts are supported by the user agent.
|
||||
let supportsVF = s.fontFamily.indexOf('Inter UI var') != -1
|
||||
// supportsVF = false // XXX
|
||||
|
||||
// hook up input controls
|
||||
for (let k in this.state) {
|
||||
let value = parseFloat(s.getPropertyValue(`--var-${k}`))
|
||||
this.state[k] = value
|
||||
let input = root.querySelector(`[name="${k}"]`)
|
||||
if (input) {
|
||||
this.inputs[k] = input
|
||||
input.value = value
|
||||
if (!supportsVF && this.variable[k]) {
|
||||
input.disabled = true
|
||||
input.parentElement.classList.add('disabled')
|
||||
} else if (input.type == 'range') {
|
||||
this.bindRangeControl(input, v => {
|
||||
this.state[k] = v
|
||||
this.update()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!supportsVF) {
|
||||
animateCheckbox.disabled = true
|
||||
animateCheckbox.parentElement.classList.add('disabled')
|
||||
let unsupportedMessage = root.querySelector(`.unsupported-message`)
|
||||
if (unsupportedMessage) {
|
||||
unsupportedMessage.classList.add('active')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
bindRangeControl(rangeInput, handler) {
|
||||
rangeInput.addEventListener('input',
|
||||
rangeInput.valueAsNumber !== undefined ? ev => {
|
||||
handler(rangeInput.valueAsNumber)
|
||||
} : ev => {
|
||||
handler(parseFloat(rangeInput.value))
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
setState(props) {
|
||||
for (let k in props) {
|
||||
if (k in this.state) {
|
||||
this.state[k] = props[k]
|
||||
}
|
||||
}
|
||||
this.update()
|
||||
},
|
||||
|
||||
update() {
|
||||
let s = sample.style
|
||||
for (let k in this.state) {
|
||||
let f = this.formatters[k]
|
||||
s.setProperty(`--var-${k}`, f ? f(this.state[k]) : this.state[k])
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// monotime() :float milliseconds
|
||||
//
|
||||
var monotime = (
|
||||
window.performance !== undefined && window.performance.now ? function() {
|
||||
return window.performance.now()
|
||||
} : Date.now ? function() {
|
||||
return Date.now()
|
||||
} : function() {
|
||||
return (new Date()).getTime()
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
var isAnimating = false
|
||||
function startAnimation() {
|
||||
if (isAnimating) {
|
||||
return
|
||||
}
|
||||
ui.inputs.weight.disabled = true
|
||||
ui.inputs.slant.disabled = true
|
||||
isAnimating = true
|
||||
let v = 0
|
||||
let wmin = parseFloat(ui.inputs.weight.min)
|
||||
, wmax = parseFloat(ui.inputs.weight.max)
|
||||
, imin = parseFloat(ui.inputs.slant.min)
|
||||
, imax = parseFloat(ui.inputs.slant.max)
|
||||
, wspeed = 200 // lower is faster; time divisor
|
||||
, ispeed = 800
|
||||
, clamp = 0.001
|
||||
, startTime = monotime()
|
||||
function update() {
|
||||
let r = 0, v = 0
|
||||
|
||||
r = (1 + Math.sin((monotime() - startTime) / wspeed)) * 0.5
|
||||
v = (wmin * (1 - clamp)) + (((wmax * (1 + clamp)) - (wmin * (1 - clamp))) * r)
|
||||
v = Math.max(wmin, Math.min(wmax, v))
|
||||
ui.state.weight = v
|
||||
ui.inputs.weight.value = v
|
||||
|
||||
r = (1 + Math.sin((monotime() - startTime) / ispeed)) * 0.5
|
||||
v = (imin * (1 - clamp)) + (((imax * (1 + clamp)) - (imin * (1 - clamp))) * r)
|
||||
v = Math.max(imin, Math.min(imax, v))
|
||||
ui.state.slant = v
|
||||
ui.inputs.slant.value = v
|
||||
|
||||
ui.update()
|
||||
|
||||
if (isAnimating) {
|
||||
requestAnimationFrame(update)
|
||||
}
|
||||
}
|
||||
update()
|
||||
}
|
||||
|
||||
function stopAnimation() {
|
||||
isAnimating = false
|
||||
ui.inputs.weight.disabled = false
|
||||
ui.inputs.slant.disabled = false
|
||||
ui.inputs.weight.value = String(ui.state.weight)
|
||||
ui.inputs.slant.value = String(ui.state.slant)
|
||||
}
|
||||
|
||||
// UI control: animate
|
||||
if (!window.requestAnimationFrame) {
|
||||
animateCheckbox.disabled = true
|
||||
animateCheckbox.title = "Not supported by browser"
|
||||
} else {
|
||||
animateCheckbox.addEventListener('change', ev => {
|
||||
if (animateCheckbox.checked) {
|
||||
startAnimation()
|
||||
} else {
|
||||
stopAnimation()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// UI control: invert colors ("Black/White")
|
||||
document.querySelector('[name="invert"]').addEventListener('change', ev => {
|
||||
if (ev.target.checked) {
|
||||
sample.parentElement.classList.add('black')
|
||||
sample.parentElement.classList.remove('white')
|
||||
} else {
|
||||
sample.parentElement.classList.remove('black')
|
||||
sample.parentElement.classList.add('white')
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
ui.init()
|
||||
|
||||
|
||||
})();
|
177
docs/index.css
@ -59,3 +59,180 @@
|
||||
.dynmet-calc #dynmet-unit:active {
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
|
||||
tablex in, tablex to, tablex out {
|
||||
display: table-cell;
|
||||
width: 5%;
|
||||
white-space: pre;
|
||||
padding-bottom:0.5em;
|
||||
}
|
||||
/*tablex to {
|
||||
width:0;
|
||||
}*/
|
||||
tablex to::after {
|
||||
-moz-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
font-feature-settings: 'calt' 1, 'case' 1;
|
||||
content: " → ";
|
||||
color: rgba(0,0,0,0.2);
|
||||
}
|
||||
tablex in, tablex out {
|
||||
color: rgba(0,0,0,0.8);
|
||||
}
|
||||
tablex r name {
|
||||
color: rgba(0,0,0,0.4);
|
||||
}
|
||||
tablex in {
|
||||
-moz-font-feature-settings: 'calt' 0;
|
||||
-ms-font-feature-settings: 'calt' 0;
|
||||
-o-font-feature-settings: 'calt' 0;
|
||||
-webkit-font-feature-settings: 'calt' 0;
|
||||
font-feature-settings: 'calt' 0;
|
||||
}
|
||||
tablex out {
|
||||
-moz-font-feature-settings: 'calt' 1;
|
||||
-ms-font-feature-settings: 'calt' 1;
|
||||
-o-font-feature-settings: 'calt' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1;
|
||||
font-feature-settings: 'calt' 1;
|
||||
}
|
||||
tablex em {
|
||||
font-style: inherit;
|
||||
background: #e4fdef;
|
||||
color: rgba(0,0,0,1);
|
||||
}
|
||||
|
||||
tablex out.zero {
|
||||
-moz-font-feature-settings: 'calt' 1, 'zero' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'zero' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'zero' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'zero' 1;
|
||||
font-feature-settings: 'calt' 1, 'zero' 1;
|
||||
}
|
||||
tablex out.tnum {
|
||||
-moz-font-feature-settings: 'calt' 1, 'tnum' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'tnum' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'tnum' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'tnum' 1;
|
||||
font-feature-settings: 'calt' 1, 'tnum' 1;
|
||||
}
|
||||
tablex out.case {
|
||||
-moz-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
font-feature-settings: 'calt' 1, 'case' 1;
|
||||
}
|
||||
tablex out.frac {
|
||||
-moz-font-feature-settings: 'calt' 1, 'frac' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'frac' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'frac' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'frac' 1;
|
||||
font-feature-settings: 'calt' 1, 'frac' 1;
|
||||
}
|
||||
tablex out.numr {
|
||||
-moz-font-feature-settings: 'calt' 1, 'numr' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'numr' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'numr' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'numr' 1;
|
||||
font-feature-settings: 'calt' 1, 'numr' 1;
|
||||
}
|
||||
tablex out.dnom {
|
||||
-moz-font-feature-settings: 'calt' 1, 'dnom' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'dnom' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'dnom' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'dnom' 1;
|
||||
font-feature-settings: 'calt' 1, 'dnom' 1;
|
||||
}
|
||||
tablex out.sups {
|
||||
-moz-font-feature-settings: 'calt' 1, 'sups' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'sups' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'sups' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'sups' 1;
|
||||
font-feature-settings: 'calt' 1, 'sups' 1;
|
||||
}
|
||||
tablex out.subs {
|
||||
-moz-font-feature-settings: 'calt' 1, 'subs' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'subs' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'subs' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'subs' 1;
|
||||
font-feature-settings: 'calt' 1, 'subs' 1;
|
||||
}
|
||||
tablex out.ss01 {
|
||||
-moz-font-feature-settings: 'calt' 1, 'ss01' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'ss01' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'ss01' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'ss01' 1;
|
||||
font-feature-settings: 'calt' 1, 'ss01' 1;
|
||||
}
|
||||
tablex out.ss02 {
|
||||
-moz-font-feature-settings: 'calt' 1, 'ss02' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'ss02' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'ss02' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'ss02' 1;
|
||||
font-feature-settings: 'calt' 1, 'ss02' 1;
|
||||
}
|
||||
tablex out.cpsp {
|
||||
-moz-font-feature-settings: 'calt' 1, 'cpsp' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'cpsp' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'cpsp' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'cpsp' 1;
|
||||
font-feature-settings: 'calt' 1, 'cpsp' 1;
|
||||
}
|
||||
tablex out.dlig {
|
||||
-moz-font-feature-settings: 'calt' 1, 'dlig' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'dlig' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'dlig' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'dlig' 1;
|
||||
font-feature-settings: 'calt' 1, 'dlig' 1;
|
||||
}
|
||||
tablex out.cv01 {
|
||||
-moz-font-feature-settings: 'calt' 1, 'cv01' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'cv01' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'cv01' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'cv01' 1;
|
||||
font-feature-settings: 'calt' 1, 'cv01' 1;
|
||||
}
|
||||
tablex out.cv02 {
|
||||
-moz-font-feature-settings: 'calt' 1, 'cv02' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'cv02' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'cv02' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'cv02' 1;
|
||||
font-feature-settings: 'calt' 1, 'cv02' 1;
|
||||
}
|
||||
tablex out.cv03 {
|
||||
-moz-font-feature-settings: 'calt' 1, 'cv03' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'cv03' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'cv03' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'cv03' 1;
|
||||
font-feature-settings: 'calt' 1, 'cv03' 1;
|
||||
}
|
||||
tablex out.cv04 {
|
||||
-moz-font-feature-settings: 'calt' 1, 'cv04' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'cv04' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'cv04' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'cv04' 1;
|
||||
font-feature-settings: 'calt' 1, 'cv04' 1;
|
||||
}
|
||||
tablex out.cv05 {
|
||||
-moz-font-feature-settings: 'calt' 1, 'cv05' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'cv05' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'cv05' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'cv05' 1;
|
||||
font-feature-settings: 'calt' 1, 'cv05' 1;
|
||||
}
|
||||
tablex out.cv06 {
|
||||
-moz-font-feature-settings: 'calt' 1, 'cv06' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'cv06' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'cv06' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'cv06' 1;
|
||||
font-feature-settings: 'calt' 1, 'cv06' 1;
|
||||
}
|
||||
|
||||
boxes.features box h3 + p {
|
||||
margin-top:0.5em;
|
||||
}
|
||||
|
262
docs/index.html
@ -12,11 +12,16 @@ for file in site.static_files %}{%
|
||||
assign _path = file.path | remove_first: "/inter" %}{%
|
||||
if _path == "/index.css" %}{%
|
||||
assign index_css_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
|
||||
elsif _path == "/index-var.css" %}{%
|
||||
assign index_var_css_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
|
||||
elsif _path == "/index-var.js" %}{%
|
||||
assign index_var_js_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
|
||||
endif %}{%
|
||||
endfor
|
||||
|
||||
%}
|
||||
<link rel="stylesheet" href="index.css?v={{ index_css_v }}">
|
||||
<link rel="stylesheet" href="index-var.css?v={{ index_var_css_v }}">
|
||||
<input type="text" id="hidden-text-input">
|
||||
|
||||
<div class="row"><div>
|
||||
@ -88,57 +93,213 @@ endfor
|
||||
</div></div>
|
||||
|
||||
<div class="row"><div>
|
||||
<h2><a id="weights" href="#weights">Weights & Styles</a></h2>
|
||||
<p>
|
||||
There are currently four <a id="weights" href="#weights">weights</a>
|
||||
There are six weights, each with italic counterparts,
|
||||
making a total of 12 styles.
|
||||
</p>
|
||||
<img src="res/weights-and-styles.svg" style="opacity:0.76;width:100%;display:block;margin:2em 0 3em 0">
|
||||
<img src="res/weights-and-styles.svg" style="opacity:0.88;width:100%;display:block;margin:3em 0 3em 0">
|
||||
</div></div>
|
||||
|
||||
|
||||
|
||||
<div class="row white variable"><div>
|
||||
<h2><a id="variable" href="#variable">Variable</a></h2>
|
||||
|
||||
<p>
|
||||
Inter UI is offered as both traditional constant font files
|
||||
(one per style, e.g. Bold Italic, Medium, etc.) as well as a
|
||||
<a href="https://en.wikipedia.org/wiki/Variable_fonts">Variable Font</a>
|
||||
file which contains all styles in a much smaller file size.
|
||||
Additionally, a variable font is ...variable! You can mix and match weight
|
||||
and italic angle as you please, forming theoretically infinite variations.
|
||||
</p>
|
||||
|
||||
<div class="unsupported-message">
|
||||
<p>Variable Fonts not supported by this web browser</p>
|
||||
</div>
|
||||
|
||||
<div class="ctrl">
|
||||
<div class="ctrlrow">
|
||||
<label title="Weight in the range of 400 to 900">
|
||||
<span class="label var">Weight:</span>
|
||||
<input type="range" value="400" min="400" max="900" name="weight">
|
||||
</label>
|
||||
<label title="Slant angle in the range of 0° to 10°">
|
||||
<span class="label var">Slant:</span>
|
||||
<input type="range" value="0" min="0" max="10" step="0.01" name="slant">
|
||||
</label>
|
||||
<label class="tight">
|
||||
<input type="checkbox" name="animate"> Animate weight & slant
|
||||
</label>
|
||||
<label class="tight">
|
||||
<input type="checkbox" name="invert"> Negative
|
||||
</label>
|
||||
</div>
|
||||
<div class="ctrlrow">
|
||||
<label title="Font size">
|
||||
<span class="label">Size:</span>
|
||||
<input type="range" value="96" min="11" max="400" name="size">
|
||||
</label>
|
||||
<label title="Space between letters">
|
||||
<span class="label">Tracking:</span>
|
||||
<input type="range" value="0" min="-0.1" max="0.1" step="0.001" name="letterSpacing">
|
||||
</label>
|
||||
<label>
|
||||
<span class="label">Line height:</span>
|
||||
<input type="range" value="1.2" min="0.7" max="2" step="0.01" name="lineHeight">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div></div>
|
||||
<div class="row white variable-sample-row">
|
||||
<div class="variable-sample" contenteditable>
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890
|
||||
?!()[]{}&*^%$#@~
|
||||
</div>
|
||||
</div>
|
||||
<script src="index-var.js?v={{ index_var_js_v }}"></script>
|
||||
<div class="row white"><div>
|
||||
<p>
|
||||
Variable fonts is a new technology and support is somewhat in flux at
|
||||
the time of writing this (fall 2018.)
|
||||
<a href="var-test.html">This test</a> can be used to discover
|
||||
what a web browser is capable of.
|
||||
</p>
|
||||
</div></div>
|
||||
|
||||
|
||||
|
||||
<div class="row"><div>
|
||||
<h2><a id="features" href="#features">Features</a></h2>
|
||||
<boxes>
|
||||
<p>
|
||||
Inter UI comes with many OpenType features that can be used to
|
||||
tailor functionality and aesthetics to your specific needs.
|
||||
Some of these features can be combined to form a great number of
|
||||
alternative variations.
|
||||
</p>
|
||||
<boxes class="features">
|
||||
<box>
|
||||
<h3>Contextual alternates (<q title='OpenType feature ID'>calt</q>)</h3>
|
||||
<h3>Contextual alternates <q title='OpenType feature ID'>calt</q></h3>
|
||||
<p>
|
||||
This feature is enabled by default and causes certain characters to
|
||||
adjust themselves or be replaced depending on the surrounding context.
|
||||
</p>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r><in>12<em>:</em>34, FE<em>—</em>X</in><to></to><out>12:34, FE—X</out></r>
|
||||
<r><in>4<em>.</em>2</in><to></to><out>4.2</out></r>
|
||||
<r><in><em>(</em>SEMI<em>)</em>PERMANENT</in><to></to><out>(SEMI)PERMANENT</out></r>
|
||||
<r><in>SFO <em>-></em> STO</in><to></to><out>SFO -> STO</out></r>
|
||||
<r><in>IIA <em>—></em> OGG</in><to></to><out>IIA —> OGG</out></r>
|
||||
<r><in>ARN <em><--></em> OGG</in><to></to><out>ARN <--> OGG</out></r>
|
||||
<r><in>M<em>@</em>N m@n</in><to></to><out>M@N m@n</out></r>
|
||||
<r><in>Smile <em>:-)</em></in><to></to><out>Smile :-)</out></r>
|
||||
</t></tablex>
|
||||
<p>
|
||||
There are many more contextual alternates.
|
||||
</p>
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Tabular numbers (<q title='OpenType feature ID'>tnum</q>)</h3>
|
||||
<h3>Tabular numbers <q title='OpenType feature ID'>tnum</q></h3>
|
||||
<p>
|
||||
Fixed-width numbers are useful for tabular data, where comparing
|
||||
columns across rows is desired.
|
||||
</p>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r><in><em>1</em>23456<em>7</em>890</in><to></to><out class="tnum">1234567890</out></r>
|
||||
<r><in>1234567890</in><to></to><out class="tnum">1234567890</out></r>
|
||||
<r><in>1131711<em> </em></in><to></to><out class="tnum">1131711<em> </em></out></r>
|
||||
<r><in>0040900<em> </em></in><to></to><out class="tnum">0040900<em> </em></out></r>
|
||||
<r><in>11:31,711<em> </em></in><to></to><out class="tnum">11:31,711<em> </em></out></r>
|
||||
<r><in>00:40.900<em> </em></in><to></to><out class="tnum">00:40.900<em> </em></out></r>
|
||||
<r><in>0.45, 0.91, +0.08<em> </em></in><to></to><out class="tnum">0.45, 0.91, +0.08<em> </em></out></r>
|
||||
<r><in>1.00, 9.44, −0.13<em> </em></in><to></to><out class="tnum">1.00, 9.44, −0.13<em> </em></out></r>
|
||||
<r><in>0.00, 1.13, +7.12<em> </em></in><to></to><out class="tnum">0.00, 1.13, +7.12<em> </em></out></r>
|
||||
</t></tablex>
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Slashed zero (<q title='OpenType feature ID'>zero</q>)</h3>
|
||||
<h3>Numerators <q title='OpenType feature ID'>numr</q></h3>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r><in><em>0</em>123</in><to></to><out class="zero">0123</out></r>
|
||||
<r><in>Hello <em>0123</em></in><to></to><out class="numr">Hello 0123</out></r>
|
||||
</t></tablex>
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Fractions (<q title='OpenType feature ID'>frac</q>)</h3>
|
||||
<h3>Denominators <q title='OpenType feature ID'>dnom</q></h3>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r><in>Hello <em>0123</em></in><to></to><out class="dnom">Hello 0123</out></r>
|
||||
</t></tablex>
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Superscript <q title='OpenType feature ID'>sups</q></h3>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r><in>H<em>ello 0123</em></in><to></to><out class="sups">Hello 0123</out></r>
|
||||
<r><in><em>abcdefghijklm</em></in><to></to><out class="sups">abcdefghijklm</out></r>
|
||||
<r><in><em>nopqrstuvwxyz</em></in><to></to><out class="sups">nopqrstuvwxyz</out></r>
|
||||
</t></tablex>
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Subscript <q title='OpenType feature ID'>subs</q></h3>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r><in>H<em>ello 0123</em></in><to></to><out class="subs">Hello 0123</out></r>
|
||||
<r><in><em>abcdefghijklm</em></in><to></to><out class="subs">abcdefghijklm</out></r>
|
||||
<r><in><em>nopqrstuvwxyz</em></in><to></to><out class="subs">nopqrstuvwxyz</out></r>
|
||||
</t></tablex>
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Fractions <q title='OpenType feature ID'>frac</q></h3>
|
||||
<p>
|
||||
This feature is contextually sensitive and will convert "words" of
|
||||
numbers separated by forward slash into proper fractions.
|
||||
This feature is dynamic and allows for any fractions.
|
||||
Note that the digits used for fractions are custom-made for their
|
||||
small size, and are even made separately from the slightly larger
|
||||
Superscript and Subscript numbers.
|
||||
</p>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r>
|
||||
<in><em>1/3</em> <em>22/9</em> <em>3/4/5</em></in>
|
||||
<to></to><out class="frac">1/3 22/9 3/4/5</out>
|
||||
<in><em>1/3</em> <em>3/4</em> <em>1/5</em> <em>1337/5910</em></in>
|
||||
<to></to><out class="frac">1/3 3/4 1/5 1337/5910</out>
|
||||
</r>
|
||||
</t></tablex>
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Stylistic set 1: Alternate digits (<q title='OpenType feature ID'>ss01</q>)</h3>
|
||||
<h3>Case alternates <q title='OpenType feature ID'>case</q></h3>
|
||||
<p>
|
||||
Switches out some glyphs to work better with capital letters and numbers.
|
||||
</p>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r>
|
||||
<in><em>(</em>Hello<em>)</em> <em>[</em>World<em>]</em> <em>{</em>9000<em>}</em></in>
|
||||
<to></to><out class="case">(Hello) [World] {9000}</out>
|
||||
</r>
|
||||
<r><in>SCHOOL <em>@</em> RUN</in><to></to><out class="case">SCHOOL @ RUN</out></r>
|
||||
<r><in>3 <em>+</em> 9 <em>=</em> 12 <em>*</em> 1</in><to></to><out class="case">3 + 9 = 12 * 1</out></r>
|
||||
<r><in><em>*</em> <em>+</em> <em>÷</em> <em>±</em> <em>×</em> <em>=</em> <em>≠</em> <em>•</em></in><to></to><out class="case">* + ÷ ± × = ≠ •</out></r>
|
||||
<r><in><em>→</em> <em>←</em> <em>⟶</em> <em>⟵</em> <em>−</em> <em>-</em> <em>–</em> <em>—</em> <em>:</em></in><to></to><out class="case">→ ← ⟶ ⟵ − - – — :</out></r>
|
||||
</t></tablex>
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Stylistic set 1: Alternate digits <q title='OpenType feature ID'>ss01</q></h3>
|
||||
<p>
|
||||
An alternate style of digits.
|
||||
Note that individual digit styles can be cherry-picked using the cvXX
|
||||
features.
|
||||
</p>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r>
|
||||
@ -153,27 +314,74 @@ endfor
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Case alternates (<q title='OpenType feature ID'>case</q>)</h3>
|
||||
<h3>Stylistic set 2: Disambiguation <q title='OpenType feature ID'>ss02</q></h3>
|
||||
<p>
|
||||
Alternate glyph set that decreases ambiguity.
|
||||
</p>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r>
|
||||
<in><em>(</em>Hello<em>)</em> <em>[</em>World<em>]</em> <em>{</em>9000<em>}</em></in>
|
||||
<to></to><out class="case">(Hello) [World] {9000}</out>
|
||||
</r>
|
||||
<r><in>SCHOOL <em>@</em> RUN</in><to></to><out class="case">SCHOOL @ RUN</out></r>
|
||||
<r><in>3 <em>+</em> 9 <em>=</em> 12 <em>*</em> 1</in><to></to><out class="case">3 + 9 = 12 * 1</out></r>
|
||||
<r><in><em>*</em> <em>+</em> <em>÷</em> <em>±</em> <em>×</em> <em>=</em> <em>≠</em> <em>•</em></in><to></to><out class="case">* + ÷ ± × = ≠ •</out></r>
|
||||
<r><in><em>→</em> <em>←</em> <em>⟶</em> <em>⟵</em> <em>−</em> <em>-</em> <em>–</em> <em>—</em> <em>:</em></in><to></to><out class="case">→ ← ⟶ ⟵ − - – — :</out></r>
|
||||
<r><in>WP<em>0</em>AC2A9XJS1<em>0</em>12O9</in><to></to><out class="ss02">WP0AC2A9XJS1012O9</out></r>
|
||||
<r><in>I<em>ll</em>ega<em>l</em></in><to></to><out class="ss02">Illegal</out></r>
|
||||
</t></tablex>
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Discretionary ligatures <q title='OpenType feature ID'>dlig</q></h3>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r><in><em>¡¿</em>What<em>?!</em></in><to></to><out class="dlig">¡¿What?!</out></r>
|
||||
<r><in><em>¿¡</em>What<em>!?</em></in><to></to><out class="dlig">¿¡What!?</out></r>
|
||||
</t></tablex>
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Slashed zero <q title='OpenType feature ID'>zero</q></h3>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r><in><em>0</em>123</in><to></to><out class="zero">0123</out></r>
|
||||
</t></tablex>
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Character variants <q title='OpenType feature ID'>cvXX</q></h3>
|
||||
<p>
|
||||
Allows cherry-picking alternate characters.
|
||||
</p>
|
||||
<tablex><t>
|
||||
<h><name>Feature </name><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r><name>cv01</name><in>1</in><to></to><out class="cv01">1</out></r>
|
||||
<r><name>cv02</name><in>4</in><to></to><out class="cv02">4</out></r>
|
||||
<r><name>cv03</name><in>6</in><to></to><out class="cv03">6</out></r>
|
||||
<r><name>cv04</name><in>9</in><to></to><out class="cv04">9</out></r>
|
||||
<r><name>cv05</name><in>l ł ƚ ɫ ɬ ŀ ĺ ļ ľ ḷ ḹ ḻ ḽ</in><to></to><out class="cv05">l ł ƚ ɫ ɬ ŀ ĺ ļ ľ ḷ ḹ ḻ ḽ</out></r>
|
||||
<r><name>cv06</name><in>r ɽ ɍ ɼ ŕ ŗ ř ȑ ȓ ṙ ṛ ṝ ṟ</in><to></to><out class="cv06">r ɽ ɍ ɼ ŕ ŗ ř ȑ ȓ ṙ ṛ ṝ ṟ</out></r>
|
||||
</t></tablex>
|
||||
</box>
|
||||
|
||||
<box>
|
||||
<h3>Capital Spacing <q title='OpenType feature ID'>cpsp</q></h3>
|
||||
<p>
|
||||
Slightly increases letter spacing for increased legibility of
|
||||
capital letters. Note: It's usually better to set letter-spacing
|
||||
(aka tracking) manually instead of using this feature. It's here
|
||||
mainly for completeness.
|
||||
</p>
|
||||
<tablex><t>
|
||||
<h><in>Disabled</in><to></to><out>Enabled</out></h>
|
||||
<r><in>FOREVER IMMATERIAL</in><to></to><out class="cpsp">FOREVER IMMATERIAL</out></r>
|
||||
<r><in>TORSCHLUẞPANIK</in><to></to><out class="cpsp">TORSCHLUẞPANIK</out></r>
|
||||
</t></tablex>
|
||||
</box>
|
||||
|
||||
</boxes>
|
||||
|
||||
<p>
|
||||
Also includes some
|
||||
Localized Forms (<q title='OpenType feature ID'>locl</q>),
|
||||
Numerators (<q title='OpenType feature ID'>numr</q>) and
|
||||
Denominators (<q title='OpenType feature ID'>dnom</q>).
|
||||
Additional features, not highlighted above:
|
||||
<q title='OpenType feature ID'>locl</q>,
|
||||
<q title='OpenType feature ID'>aalt</q>,
|
||||
<q title='OpenType feature ID'>ccmp</q>,
|
||||
<q title='OpenType feature ID'>ordn</q> and
|
||||
<q title='OpenType feature ID'>salt</q>,
|
||||
</p>
|
||||
|
||||
<p> </p>
|
||||
@ -213,8 +421,8 @@ endfor
|
||||
|
||||
<h2><a id="status" href="#status">Current status & usability</a></h2>
|
||||
<p>
|
||||
Inter UI works great for English-language text, and pretty well for other
|
||||
Latin and Cyrillic languages. There's still a lot of work to be done, and
|
||||
Inter UI works great for Latin text and pretty well for
|
||||
Cyrillic. There's still some work to be done on Cyrillic and
|
||||
<a href="https://github.com/rsms/inter/blob/master/CONTRIBUTING.md">contributions are warmly welcomed</a>. The playground contains <a href="lab/?sample=Body%20text%201&size=16">a lot of samples</a>, including some common <a href="lab/?sample=Kerning%20body%20multi-lang&size=16">non English-language words in the playground.</a>
|
||||
</p>
|
||||
|
||||
|
@ -116,6 +116,11 @@ html {
|
||||
}
|
||||
}
|
||||
|
||||
BUGS:
|
||||
- Safari 12.0 will default to italic instead of regular when font-weight
|
||||
is provided in a @font-face declaration.
|
||||
Workaround: Use "Inter UI var safari-fix" for Safari, or explicitly set
|
||||
`font-variation-settings:"slnt" DEGREE`.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Inter UI var';
|
||||
@ -124,3 +129,9 @@ html {
|
||||
src: url("font-files/Inter-UI.var.woff2?v=3.0") format("woff2-variations"),
|
||||
url("font-files/Inter-UI.var.woff2?v=3.0") format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter UI var safari-fix';
|
||||
font-style: oblique 0deg 10deg;
|
||||
src: url("font-files/Inter-UI.var.woff2?v=3.0") format("woff2-variations"),
|
||||
url("font-files/Inter-UI.var.woff2?v=3.0") format("woff2");
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ label {
|
||||
</label>
|
||||
</div>
|
||||
<div class="sample" contenteditable>
|
||||
Inter UI 3.0 is variable and flexible<br>
|
||||
Inter UI 3.0<br>
|
||||
Variable weight axis<br>
|
||||
Variable slant/oblique axis<br>
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ<br>
|
||||
|
@ -2,7 +2,7 @@
|
||||
html { }
|
||||
body {
|
||||
background-color: #f4f4f4;
|
||||
color: #414141;
|
||||
color: #111;
|
||||
font: 15px/22px 'Inter UI', system-ui, sans-serif;
|
||||
|
||||
font-size: 15px;
|
||||
@ -25,6 +25,30 @@ body {
|
||||
font-feature-settings:"kern" 1, "liga" 1;
|
||||
}
|
||||
|
||||
/* Font style classifiers used by samples and dynmetrics */
|
||||
/* Note: font-variation-settings is needed for Safari. */
|
||||
.font-style-regular { font-variation-settings: "wght" 400, "slnt" 0 !important; font-weight:400 !important; font-style:normal !important; }
|
||||
.font-style-italic { font-variation-settings: "wght" 400, "slnt" 100 !important; font-weight:400 !important; font-style:oblique !important; }
|
||||
.font-style-medium { font-variation-settings: "wght" 500, "slnt" 0 !important; font-weight:500 !important; font-style:normal !important; }
|
||||
.font-style-medium-italic { font-variation-settings: "wght" 500, "slnt" 100 !important; font-weight:500 !important; font-style:oblique !important; }
|
||||
.font-style-semi-bold { font-variation-settings: "wght" 600, "slnt" 0 !important; font-weight:600 !important; font-style:normal !important; }
|
||||
.font-style-semi-bold-italic { font-variation-settings: "wght" 600, "slnt" 100 !important; font-weight:600 !important; font-style:oblique !important; }
|
||||
.font-style-bold { font-variation-settings: "wght" 700, "slnt" 0 !important; font-weight:700 !important; font-style:normal !important; }
|
||||
.font-style-bold-italic { font-variation-settings: "wght" 700, "slnt" 100 !important; font-weight:700 !important; font-style:oblique !important; }
|
||||
.font-style-extra-bold { font-variation-settings: "wght" 800, "slnt" 0 !important; font-weight:800 !important; font-style:normal !important; }
|
||||
.font-style-extra-bold-italic { font-variation-settings: "wght" 800, "slnt" 100 !important; font-weight:800 !important; font-style:oblique !important; }
|
||||
.font-style-black { font-variation-settings: "wght" 900, "slnt" 0 !important; font-weight:900 !important; font-style:normal !important; }
|
||||
.font-style-black-italic { font-variation-settings: "wght" 900, "slnt" 100 !important; font-weight:900 !important; font-style:oblique !important; }
|
||||
|
||||
@supports (font-variation-settings: normal) {
|
||||
body {
|
||||
font-family: 'Inter UI var', system-ui, sans-serif;
|
||||
}
|
||||
html.safari body {
|
||||
font-family: 'Inter UI var safari-fix', system-ui, sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
:target:before {
|
||||
content:"";
|
||||
display: block;
|
||||
@ -108,8 +132,7 @@ num { /* number */
|
||||
}
|
||||
|
||||
em, i, .italic {
|
||||
font-style: italic;
|
||||
/*font-variation-settings: 'slnt' 10;*/
|
||||
font-style: oblique;
|
||||
}
|
||||
|
||||
small {
|
||||
@ -133,8 +156,9 @@ h1 {
|
||||
font-weight: 700;
|
||||
}
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
letter-spacing: -0.02em;
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.015em;
|
||||
line-height: 30px;
|
||||
margin-bottom: 25px;
|
||||
margin-top: 10px;
|
||||
@ -172,7 +196,12 @@ h2.banner {
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: inherit;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
h3 q {
|
||||
font-weight: 400;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
h1 > a, h2 > a, h3 > a {
|
||||
@ -182,20 +211,6 @@ h1 > a, h2 > a, h3 > a {
|
||||
background: none;
|
||||
}
|
||||
|
||||
/* font style classifiers used by samples and dynmetrics */
|
||||
.font-style-regular { font-variation-settings: "wght" 400, "ital" 0 !important; font-weight:400 !important; font-style:normal !important; }
|
||||
.font-style-italic { font-variation-settings: "wght" 400, "ital" 100 !important; font-weight:400 !important; font-style:italic !important; }
|
||||
.font-style-medium { font-variation-settings: "wght" 500, "ital" 0 !important; font-weight:500 !important; font-style:normal !important; }
|
||||
.font-style-medium-italic { font-variation-settings: "wght" 500, "ital" 100 !important; font-weight:500 !important; font-style:italic !important; }
|
||||
.font-style-semi-bold { font-variation-settings: "wght" 600, "ital" 0 !important; font-weight:800 !important; font-style:normal !important; }
|
||||
.font-style-semi-bold-italic { font-variation-settings: "wght" 600, "ital" 100 !important; font-weight:800 !important; font-style:italic !important; }
|
||||
.font-style-bold { font-variation-settings: "wght" 700, "ital" 0 !important; font-weight:700 !important; font-style:normal !important; }
|
||||
.font-style-bold-italic { font-variation-settings: "wght" 700, "ital" 100 !important; font-weight:700 !important; font-style:italic !important; }
|
||||
.font-style-extra-bold { font-variation-settings: "wght" 800, "ital" 0 !important; font-weight:800 !important; font-style:normal !important; }
|
||||
.font-style-extra-bold-italic { font-variation-settings: "wght" 800, "ital" 100 !important; font-weight:800 !important; font-style:italic !important; }
|
||||
.font-style-black { font-variation-settings: "wght" 900, "ital" 0 !important; font-weight:900 !important; font-style:normal !important; }
|
||||
.font-style-black-italic { font-variation-settings: "wght" 900, "ital" 100 !important; font-weight:900 !important; font-style:italic !important; }
|
||||
|
||||
.row {
|
||||
padding: 50px;
|
||||
display: flex;
|
||||
@ -279,19 +294,26 @@ h1 > a, h2 > a, h3 > a {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.row.dark {
|
||||
.row.dark,
|
||||
.row.black {
|
||||
background: #2b2b2b;
|
||||
color: #99999b;
|
||||
}
|
||||
.row.dark a {
|
||||
.row.black {
|
||||
background: #010101;
|
||||
}
|
||||
.row.dark a,
|
||||
.row.black a {
|
||||
text-decoration-color: rgba(255, 255, 255, 0.2);
|
||||
color: #aaa;
|
||||
}
|
||||
.row.dark a:hover {
|
||||
.row.dark a:hover,
|
||||
.row.black a:hover {
|
||||
color: rgb(95, 170, 255);
|
||||
text-decoration: underline rgb(95, 170, 255);
|
||||
}
|
||||
.row.dark h2, .row.dark h2 > a {
|
||||
.row.dark h2, .row.dark h2 > a,
|
||||
.row.black h2, .row.black h2 > a {
|
||||
color: #ccc;
|
||||
background: none;
|
||||
}
|
||||
@ -379,81 +401,6 @@ tablex {
|
||||
text-decoration: none;
|
||||
display: table-row;
|
||||
}
|
||||
tablex in, tablex to, tablex out {
|
||||
display: table-cell;
|
||||
width: 5%;
|
||||
white-space: pre;
|
||||
padding-bottom:0.5em;
|
||||
}
|
||||
/*tablex to {
|
||||
width:0;
|
||||
}*/
|
||||
tablex to::after {
|
||||
-moz-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
font-feature-settings: 'calt' 1, 'case' 1;
|
||||
content: " → ";
|
||||
color: rgba(0,0,0,0.2);
|
||||
}
|
||||
tablex in, tablex out {
|
||||
color: rgba(0,0,0,0.8);
|
||||
}
|
||||
tablex in {
|
||||
-moz-font-feature-settings: 'calt' 0;
|
||||
-ms-font-feature-settings: 'calt' 0;
|
||||
-o-font-feature-settings: 'calt' 0;
|
||||
-webkit-font-feature-settings: 'calt' 0;
|
||||
font-feature-settings: 'calt' 0;
|
||||
}
|
||||
tablex out {
|
||||
-moz-font-feature-settings: 'calt' 1;
|
||||
-ms-font-feature-settings: 'calt' 1;
|
||||
-o-font-feature-settings: 'calt' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1;
|
||||
font-feature-settings: 'calt' 1;
|
||||
}
|
||||
tablex out.zero {
|
||||
-moz-font-feature-settings: 'calt' 1, 'zero' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'zero' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'zero' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'zero' 1;
|
||||
font-feature-settings: 'calt' 1, 'zero' 1;
|
||||
}
|
||||
tablex out.tnum {
|
||||
-moz-font-feature-settings: 'calt' 1, 'tnum' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'tnum' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'tnum' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'tnum' 1;
|
||||
font-feature-settings: 'calt' 1, 'tnum' 1;
|
||||
}
|
||||
tablex out.case {
|
||||
-moz-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'case' 1;
|
||||
font-feature-settings: 'calt' 1, 'case' 1;
|
||||
}
|
||||
tablex out.frac {
|
||||
-moz-font-feature-settings: 'calt' 1, 'frac' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'frac' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'frac' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'frac' 1;
|
||||
font-feature-settings: 'calt' 1, 'frac' 1;
|
||||
}
|
||||
tablex out.ss01 {
|
||||
-moz-font-feature-settings: 'calt' 1, 'ss01' 1;
|
||||
-ms-font-feature-settings: 'calt' 1, 'ss01' 1;
|
||||
-o-font-feature-settings: 'calt' 1, 'ss01' 1;
|
||||
-webkit-font-feature-settings: 'calt' 1, 'ss01' 1;
|
||||
font-feature-settings: 'calt' 1, 'ss01' 1;
|
||||
}
|
||||
tablex em {
|
||||
font-style: inherit;
|
||||
background: #FBE9A3;
|
||||
color: rgba(0,0,0,1);
|
||||
}
|
||||
|
||||
|
||||
boxes {
|
||||
|
@ -86,6 +86,13 @@ function InterUIDynamicTracking(fontSize) {
|
||||
return a + b * Math.pow(Math.E, c * fontSize)
|
||||
}
|
||||
|
||||
// InterUIDynamicLineHeight produces the line height for the given font size
|
||||
//
|
||||
function InterUIDynamicLineHeight(fontSize) {
|
||||
var l = 1.4
|
||||
return Math.round(fontSize * l)
|
||||
}
|
||||
|
||||
|
||||
// Mac or not? Maybe even a buggy Safari?
|
||||
var isMac = false
|
||||
|
@ -2,6 +2,14 @@
|
||||
|
||||
function passThrough(v) { return v }
|
||||
|
||||
function valueGetter(el) {
|
||||
return (
|
||||
'valueAsNumber' in el ? () => el.valueAsNumber :
|
||||
(el.type == 'number' || el.type == 'range') ? () => parseFloat(el.value) :
|
||||
() => el.value
|
||||
)
|
||||
}
|
||||
|
||||
function Binding(name){
|
||||
this.name = name
|
||||
this.value = undefined
|
||||
@ -15,8 +23,9 @@ function Binding(name){
|
||||
|
||||
Binding.prototype.addInput = function(el) {
|
||||
var binding = this
|
||||
var _onInput = function(ev) {
|
||||
binding.setValue(el.value, el)
|
||||
var getValue = valueGetter(el)
|
||||
var _onInput = ev => {
|
||||
binding.setValue(getValue(), el)
|
||||
}
|
||||
var input = {
|
||||
el: el,
|
||||
@ -24,13 +33,18 @@ Binding.prototype.addInput = function(el) {
|
||||
}
|
||||
this.inputs.push(input)
|
||||
if (this.value === undefined) {
|
||||
this.value = el.value
|
||||
this.value = getValue()
|
||||
} else {
|
||||
input.el.value = this.formatter(this.value)
|
||||
}
|
||||
el.addEventListener('input', _onInput, {passive:true})
|
||||
if (el.tagName == 'SELECT' || el.type == 'checkbox') {
|
||||
el.addEventListener('change', _onInput, {passive:true})
|
||||
} else {
|
||||
el.addEventListener('input', _onInput, {passive:true})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binding.prototype.addOutput = function(el) {
|
||||
this.outputs.push(el)
|
||||
if (this.value !== undefined) {
|
||||
@ -71,12 +85,13 @@ Binding.prototype.setValue = function(nextval, origin) {
|
||||
})
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
function Bindings() {
|
||||
this.bindings = {}
|
||||
}
|
||||
|
||||
Bindings.prototype.getBinding = function(name, input) {
|
||||
Bindings.prototype.getBinding = function(name) {
|
||||
var binding = this.bindings[name]
|
||||
if (!binding) {
|
||||
binding = new Binding(name)
|
||||
@ -188,3 +203,40 @@ Bindings.prototype.configure = function(name, value, parser, listener) {
|
||||
binding.parser = parser
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Bindings.prototype.allBindings = (
|
||||
typeof Object.values == 'function' ? function() {
|
||||
return Object.values(this.bindings)
|
||||
} : function() {
|
||||
let v = []
|
||||
for (let name in this.bindings) {
|
||||
v.push(this.bindings[name])
|
||||
}
|
||||
return v
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Bindings.prototype.getValues = function() {
|
||||
let values = {}
|
||||
for (let name in this.bindings) {
|
||||
values[name] = this.bindings[name].value
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
|
||||
Bindings.prototype.setValues = function(values) {
|
||||
Object.keys(values).forEach(name => {
|
||||
let b = this.bindings[name]
|
||||
if (!b) {
|
||||
if (console.warn) {
|
||||
console.warn('Bindings.setValues: ignoring unknown "' + name + '"')
|
||||
}
|
||||
return
|
||||
}
|
||||
// console.log(`bindings setValue ${name} => ${values[name]}`)
|
||||
b.setValue(values[name])
|
||||
})
|
||||
}
|
||||
|
1
docs/res/close.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 8.707l4.646 4.647.708-.708L8.707 8l4.647-4.646-.708-.708L8 7.293 3.354 2.646l-.708.708L7.293 8l-4.647 4.646.708.708L8 8.707z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 232 B |
319
docs/res/ctxedit.css
Normal file
@ -0,0 +1,319 @@
|
||||
:root {
|
||||
--strip-height: 40px;
|
||||
}
|
||||
|
||||
#ctxedit-ui {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 0 0;
|
||||
height: var(--strip-height);
|
||||
|
||||
background: #111;
|
||||
color: #ccc;
|
||||
font-size: 12px;
|
||||
/*border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;*/
|
||||
|
||||
padding: 0 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
opacity:0;
|
||||
transition: 90ms all cubic-bezier(0.25, 0.47, 0.44, 0.93);
|
||||
transform: translate(0, var(--strip-height));
|
||||
}
|
||||
#ctxedit-ui.visible {
|
||||
opacity:1;
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
#ctxedit-ui .wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
max-width: 888px; /* same as base.css .row > * */
|
||||
flex: 1 0 100%;
|
||||
}
|
||||
#ctxedit-ui .control {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
min-height: 30px;
|
||||
margin: 0 16px;
|
||||
}
|
||||
#ctxedit-ui .control:first-child {
|
||||
margin-left:0;
|
||||
}
|
||||
#ctxedit-ui .control.range {
|
||||
flex: 1 1 auto;
|
||||
max-width: 200px;
|
||||
}
|
||||
#ctxedit-ui .control > * {
|
||||
flex: 1 1 auto;
|
||||
margin:0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#ctxedit-ui .control > :last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
#ctxedit-ui .control > select {
|
||||
min-width: 8em;
|
||||
flex: 0 1 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
#ctxedit-ui .control > input,
|
||||
#ctxedit-ui .control > select {
|
||||
width: 0;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
}
|
||||
#ctxedit-ui .control.popup {
|
||||
margin-right: 0px;
|
||||
}
|
||||
#ctxedit-ui .control > select {
|
||||
font: inherit;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
background-color: #111;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: var(--strip-height);
|
||||
background-image: url(popup.svg);
|
||||
background-repeat: no-repeat;
|
||||
width: 120px;
|
||||
color: white;
|
||||
opacity: 0.6;
|
||||
background-position: left center;
|
||||
padding-left: 20px;
|
||||
line-height: var(--strip-height);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
#ctxedit-ui .control > select::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
#ctxedit-ui .control > select:-moz-focusring {
|
||||
color: transparent;
|
||||
text-shadow: 0 0 0 #fff;
|
||||
background-color: #111;
|
||||
font: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
#ctxedit-ui .control > select:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
#ctxedit-ui .control > input[type="number"],
|
||||
#ctxedit-ui .control > input[type="text"] {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 4px 0;
|
||||
font-size: inherit;
|
||||
/*border-radius: 2px;*/
|
||||
}
|
||||
#ctxedit-ui .control > input[type="number"] {
|
||||
max-width: 48px;
|
||||
text-align: left;
|
||||
-moz-appearance: textfield;
|
||||
-moz-font-feature-settings: 'tnum' 1;
|
||||
-ms-font-feature-settings: 'tnum' 1;
|
||||
-o-font-feature-settings: 'tnum' 1;
|
||||
-webkit-font-feature-settings: 'tnum' 1;
|
||||
font-feature-settings: 'tnum' 1;
|
||||
}
|
||||
#ctxedit-ui .control > input[type=number]::-webkit-inner-spin-button,
|
||||
#ctxedit-ui .control > input[type=number]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
#ctxedit-ui .control > input[type="range"] {
|
||||
/*max-width: 80%;*/
|
||||
flex: 1 1 auto;
|
||||
display: block;
|
||||
}
|
||||
#ctxedit-ui .control > input[type="color"] {
|
||||
width:20px;
|
||||
height:20px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
#ctxedit-ui .control > img.icon,
|
||||
#ctxedit-ui .control > label {
|
||||
user-select: none;
|
||||
font-family: georgia, serif;
|
||||
font-style: italic;
|
||||
color: white;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex: 0 0 auto;
|
||||
margin-right: 16px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
#ctxedit-ui .button {
|
||||
width: var(--strip-height);
|
||||
height: var(--strip-height);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
opacity: 0.6;
|
||||
margin:0;
|
||||
}
|
||||
#ctxedit-ui .button:hover {
|
||||
opacity: 1;
|
||||
background-color: #292929;
|
||||
}
|
||||
#ctxedit-ui .button:active {
|
||||
background-color: #444;
|
||||
}
|
||||
#ctxedit-ui .button.features-button { background-image: url(settings.svg); }
|
||||
#ctxedit-ui .button.reset-button { background-image: url(reset.svg); }
|
||||
#ctxedit-ui .button.dismiss-button { background-image: url(dismiss.svg); }
|
||||
|
||||
|
||||
.font-preload {
|
||||
position: fixed;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
.slider-value-tip {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
width: 100px;
|
||||
/*background: hotpink;*/
|
||||
left: 500px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
z-index: 9;
|
||||
transition: opacity 120ms ease-out;
|
||||
filter: drop-shadow(0px 2px 6px rgba(0,0,0,0.2));
|
||||
--fgcolor: #fff;
|
||||
--bgcolor: #444;
|
||||
}
|
||||
.slider-value-tip .value {
|
||||
padding: 4px 10px;
|
||||
border-radius: 2px;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.013em;
|
||||
color: var(--fgcolor);
|
||||
background: var(--bgcolor);
|
||||
-moz-font-feature-settings: 'tnum' 1;
|
||||
-ms-font-feature-settings: 'tnum' 1;
|
||||
-o-font-feature-settings: 'tnum' 1;
|
||||
-webkit-font-feature-settings: 'tnum' 1;
|
||||
font-feature-settings: 'tnum' 1;
|
||||
z-index: 1;
|
||||
}
|
||||
.slider-value-tip .callout {
|
||||
background: var(--bgcolor);
|
||||
width:10px;
|
||||
height:10px;
|
||||
transform: translate(0, -6px) rotate(45deg);
|
||||
z-index: 0;
|
||||
}
|
||||
.slider-value-tip.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.styled-inputs-neg {
|
||||
--track-thickness: 2px;
|
||||
--track-color: rgba(255, 255, 255, 0.3);
|
||||
|
||||
--thumb-outline-thickness: 2px;
|
||||
--thumb-outline-color: #111; /* match control strip */
|
||||
--thumb-diameter: 10px;
|
||||
--thumb-color: white;
|
||||
|
||||
--active-color: rgb(95, 170, 255);
|
||||
}
|
||||
|
||||
.styled-inputs-neg input[type=range] {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: var(--strip-height);
|
||||
background: transparent;
|
||||
}
|
||||
.styled-inputs-neg input[type=range]:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.styled-inputs-neg input[type=range]::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
animate: 0.2s;
|
||||
background: var(--track-color);
|
||||
height: var(--track-thickness);
|
||||
}
|
||||
.styled-inputs-neg input[type=range]:focus::-webkit-slider-runnable-track {
|
||||
background: var(--active-color);
|
||||
}
|
||||
.styled-inputs-neg input[type=range]::-moz-range-track {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
animate: 0.2s;
|
||||
background: var(--track-color);
|
||||
height: var(--track-thickness);
|
||||
}
|
||||
.styled-inputs-neg input[type=range]:focus::-moz-range-track {
|
||||
background: var(--active-color);
|
||||
}
|
||||
|
||||
.styled-inputs-neg input[type=range]::-webkit-slider-thumb {
|
||||
--thumb-diameter2: calc(var(--thumb-diameter) + var(--thumb-outline-thickness) * 2);
|
||||
width: var(--thumb-diameter2);
|
||||
height: var(--thumb-diameter2);
|
||||
border: var(--thumb-outline-thickness) solid var(--thumb-outline-color);
|
||||
border-radius: 100%;
|
||||
background: var(--thumb-color);
|
||||
-webkit-appearance: none;
|
||||
margin-top: calc(((var(--thumb-diameter2) / 2) - (var(--track-thickness) / 2)) * -1);
|
||||
}
|
||||
.styled-inputs-neg input[type=range]::-moz-range-thumb {
|
||||
width: var(--thumb-diameter);
|
||||
height: var(--thumb-diameter);
|
||||
border: var(--thumb-outline-thickness) solid var(--thumb-outline-color);
|
||||
border-radius: 100%;
|
||||
background: var(--thumb-color);
|
||||
margin-top: calc(((var(--thumb-diameter) / 2) - (var(--track-thickness) / 2)) * -1);
|
||||
}
|
||||
|
||||
/* MS Edge -- note that we can't use CSS variables here */
|
||||
.styled-inputs-neg input[type=range]::-ms-track {
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
border-width: 0 0;
|
||||
color: transparent; /* markings */
|
||||
height: 2px;
|
||||
animate: 0.2s;
|
||||
margin: 0;
|
||||
}
|
||||
.styled-inputs-neg input[type=range]::-ms-fill-lower,
|
||||
.styled-inputs-neg input[type=range]::-ms-fill-upper {
|
||||
background: white;
|
||||
opacity: 0.3;
|
||||
}
|
||||
.styled-inputs-neg input[type=range]::-ms-thumb {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
/*border: 2px solid rgba(0,0,0,0.8);*/
|
||||
box-shadow: 0px 0px 0px 2px #111;
|
||||
border-radius: 100%;
|
||||
background: white;
|
||||
}
|
||||
/* Note: :focus selectors are buggy in Edge, so we skip that */
|
566
docs/res/ctxedit.js
Normal file
@ -0,0 +1,566 @@
|
||||
var CtxEdit = (function(){
|
||||
|
||||
|
||||
function getLocalObject(key) {
|
||||
let s = sessionStorage.getItem(key)
|
||||
if (s) {
|
||||
try {
|
||||
return JSON.parse(s)
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`failed to parse sessionStorage value "${s}" for key ${key}`,
|
||||
err.stack || String(err)
|
||||
)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
function setLocalObject(key, value) {
|
||||
let json = JSON.stringify(value)
|
||||
sessionStorage.setItem(key, json)
|
||||
}
|
||||
|
||||
|
||||
function rmLocalObject(key) {
|
||||
sessionStorage.removeItem(key)
|
||||
}
|
||||
|
||||
|
||||
class FloatProp {
|
||||
constructor(cssProp, unitSuffix) {
|
||||
this.cssProp = cssProp
|
||||
this.unitSuffix = unitSuffix
|
||||
}
|
||||
|
||||
valueInStyle(s) {
|
||||
let v = s[this.cssProp]
|
||||
return v !== undefined ? parseFloat(v) : v
|
||||
}
|
||||
|
||||
applyStyle(el, value) {
|
||||
el.style[this.cssProp] = value + this.unitSuffix
|
||||
}
|
||||
}
|
||||
|
||||
class FontStyleProp {
|
||||
valueInStyle(s) {
|
||||
let italic = s['font-style'] == 'italic' || s['font-style'].indexOf('oblique') != -1
|
||||
let weight = parseFloat(s['font-weight'])
|
||||
if (isNaN(weight)) {
|
||||
weight = s['font-weight']
|
||||
if (weight == 'normal') { return italic ? 'italic' : 'regular' }
|
||||
if (weight == 'medium') { return italic ? 'medium-italic' : 'medium' }
|
||||
if (weight == 'semi-bold') { return italic ? 'semi-bold-italic' : 'semi-bold' }
|
||||
if (weight == 'bold') { return italic ? 'bold-italic' : 'bold' }
|
||||
if (weight == 'extra-bold') { return italic ? 'extra-bold-italic' : 'extra-bold' }
|
||||
} else {
|
||||
if (weight <= 450) { return italic ? 'italic' : 'regular' }
|
||||
if (weight <= 550) { return italic ? 'medium-italic' : 'medium' }
|
||||
if (weight <= 650) { return italic ? 'semi-bold-italic' : 'semi-bold' }
|
||||
if (weight <= 750) { return italic ? 'bold-italic' : 'bold' }
|
||||
if (weight <= 850) { return italic ? 'extra-bold-italic' : 'extra-bold' }
|
||||
}
|
||||
return italic ? 'black-italic' : 'black'
|
||||
}
|
||||
|
||||
applyStyle(el, value) {
|
||||
let cl = el.classList
|
||||
for (let k of Array.from(cl.values())) {
|
||||
if (k.indexOf('font-style-') == 0) {
|
||||
cl.remove(k)
|
||||
}
|
||||
}
|
||||
cl.add('font-style-' + value)
|
||||
}
|
||||
}
|
||||
|
||||
class LineHeightProp {
|
||||
valueInStyle(s) {
|
||||
let v = s['line-height']
|
||||
if (v === undefined) {
|
||||
return 1.0
|
||||
}
|
||||
if (v.lastIndexOf('px') == v.length - 2) {
|
||||
// compute
|
||||
return parseFloat(
|
||||
(parseFloat(v) / parseFloat(s['font-size'])).toFixed(3)
|
||||
)
|
||||
}
|
||||
v = parseFloat(v)
|
||||
return isNaN(v) ? 1.0 : v
|
||||
}
|
||||
|
||||
applyStyle(el, value) {
|
||||
el.style['line-height'] = String(value)
|
||||
}
|
||||
}
|
||||
|
||||
class TrackingProp {
|
||||
valueInStyle(s) {
|
||||
let v = s['letter-spacing']
|
||||
if (v === undefined) {
|
||||
return 0
|
||||
}
|
||||
if (v.lastIndexOf('px') == v.length - 2) {
|
||||
// compute
|
||||
return parseFloat(
|
||||
(parseFloat(v) / parseFloat(s['font-size'])).toFixed(3)
|
||||
)
|
||||
}
|
||||
v = parseFloat(v)
|
||||
return isNaN(v) ? 0 : v
|
||||
}
|
||||
|
||||
applyStyle(el, value) {
|
||||
el.style['letter-spacing'] = value.toFixed(3) + 'em'
|
||||
}
|
||||
}
|
||||
|
||||
const Props = {
|
||||
size: new FloatProp('font-size', 'px'),
|
||||
tracking: new TrackingProp(),
|
||||
lineHeight: new LineHeightProp(),
|
||||
style: new FontStyleProp(),
|
||||
}
|
||||
|
||||
function valuesFromStyle(s) {
|
||||
let values = {}
|
||||
for (let name in Props) {
|
||||
let p = Props[name]
|
||||
values[name] = p.valueInStyle(s)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
|
||||
class Editable {
|
||||
constructor(el, key) {
|
||||
this.el = el
|
||||
this.key = key
|
||||
this.defaultValues = valuesFromStyle(getComputedStyle(this.el))
|
||||
// console.log('default values:', this.defaultValues)
|
||||
this.values = Object.assign({}, this.defaultValues)
|
||||
this.explicitTracking = false
|
||||
this.explicitTrackingKey = this.key + ":etracking"
|
||||
this.loadValues()
|
||||
this.updateSizeDependantProps()
|
||||
}
|
||||
|
||||
resetValues() {
|
||||
this.values = Object.assign({}, this.defaultValues)
|
||||
let style = this.el.style
|
||||
for (let name in this.values) {
|
||||
Props[name].applyStyle(this.el, this.values[name])
|
||||
}
|
||||
rmLocalObject(this.key)
|
||||
rmLocalObject(this.explicitTrackingKey)
|
||||
this.explicitTracking = false
|
||||
this.updateSizeDependantProps()
|
||||
}
|
||||
|
||||
setExplicitTracking(explicitTracking) {
|
||||
if (this.explicitTracking !== explicitTracking) {
|
||||
this.explicitTracking = explicitTracking
|
||||
if (!this.explicitTracking) {
|
||||
this.updateSizeDependantProps()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setValue(name, value) {
|
||||
this.values[name] = value
|
||||
Props[name].applyStyle(this.el, value)
|
||||
if (name == 'size') {
|
||||
this.updateSizeDependantProps()
|
||||
}
|
||||
}
|
||||
|
||||
updateSizeDependantProps() {
|
||||
let size = this.values.size
|
||||
|
||||
// dynamic tracking
|
||||
if (!this.explicitTracking) {
|
||||
this.setValue('tracking', InterUIDynamicTracking(size))
|
||||
}
|
||||
|
||||
// left indent
|
||||
let leftMargin = 0
|
||||
// TODO: Find a formula for this: F(size) -> leftMargin
|
||||
// TODO: Consider making this part of dynamic metrics.
|
||||
if (size >= 162) { leftMargin = -10; }
|
||||
else if (size >= 146) { leftMargin = -9; }
|
||||
else if (size >= 130) { leftMargin = -8; }
|
||||
else if (size >= 114) { leftMargin = -7; }
|
||||
else if (size >= 98) { leftMargin = -6; }
|
||||
else if (size >= 79) { leftMargin = -5; }
|
||||
else if (size >= 64) { leftMargin = -4; }
|
||||
else if (size >= 48) { leftMargin = -3; }
|
||||
else if (size >= 32) { leftMargin = -2; }
|
||||
else if (size >= 16) { leftMargin = -1; }
|
||||
if (leftMargin == 0) {
|
||||
this.el.style.marginLeft = null
|
||||
} else {
|
||||
this.el.style.marginLeft = leftMargin + 'px'
|
||||
}
|
||||
}
|
||||
|
||||
loadValues() {
|
||||
let values = getLocalObject(this.key)
|
||||
if (values && typeof values == 'object') {
|
||||
for (let name in values) {
|
||||
if (name in this.values) {
|
||||
let value = values[name]
|
||||
this.values[name] = value
|
||||
Props[name].applyStyle(this.el, value)
|
||||
} else if (console.warn) {
|
||||
console.warn(`Editable.loadValues ignoring unknown "${name}"`)
|
||||
}
|
||||
}
|
||||
// console.log(`loaded values for ${this}:`, values)
|
||||
}
|
||||
let etr = getLocalObject(this.explicitTrackingKey)
|
||||
this.explicitTracking = false
|
||||
if (etr) {
|
||||
this.explicitTracking = true
|
||||
}
|
||||
}
|
||||
|
||||
isDefaultValues() {
|
||||
for (let k in this.values) {
|
||||
if (this.values[k] !== this.defaultValues[k]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
saveValues() {
|
||||
if (this.isDefaultValues()) {
|
||||
rmLocalObject(this.key)
|
||||
rmLocalObject(this.explicitTrackingKey)
|
||||
} else {
|
||||
setLocalObject(this.key, this.values)
|
||||
setLocalObject(this.explicitTrackingKey, this.explicitTracking ? "1" : "0")
|
||||
}
|
||||
// console.log(`saved values for ${this}`)
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `Editable(${this.key})`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var supportsFocusTrick = (u =>
|
||||
u.indexOf('Firefox/') == -1
|
||||
)(navigator.userAgent)
|
||||
|
||||
|
||||
class CtxEdit {
|
||||
constructor() {
|
||||
this.bindings = new Bindings()
|
||||
this.keyPrefix = 'ctxedit:' + document.location.pathname + ':'
|
||||
this.editables = new Map()
|
||||
this.ui = $('#ctxedit-ui')
|
||||
this.currEditable = null
|
||||
this._saveValuesTimer = null
|
||||
this.isChangingBindings = true
|
||||
this.bindings = new Bindings()
|
||||
this.initBindings()
|
||||
this.initUI()
|
||||
this.addAllEditables()
|
||||
this.isChangingBindings = false
|
||||
this.preloadFonts()
|
||||
|
||||
if (supportsFocusTrick) {
|
||||
this.ui.addEventListener('focus', ev => {
|
||||
if (this.currEditable) {
|
||||
ev.preventDefault()
|
||||
ev.stopImmediatePropagation()
|
||||
this.currEditable.el.focus() // breaks Firefox
|
||||
}
|
||||
}, {capture:true, passive:false})
|
||||
}
|
||||
}
|
||||
|
||||
initUI() {
|
||||
$('.reset-button', this.ui).addEventListener('click', ev => this.reset())
|
||||
$('.dismiss-button', this.ui).addEventListener('click', ev => this.stopEditing())
|
||||
this.initRangeSliders()
|
||||
}
|
||||
|
||||
initRangeSliders() {
|
||||
this._sliderTimers = new Map()
|
||||
$$('input[type="range"]', this.ui).forEach(input => {
|
||||
var binding = this.bindings.getBinding(input.dataset.binding)
|
||||
|
||||
// create and hook up value tip
|
||||
let valtip = document.createElement('div')
|
||||
let valtipval = document.createElement('div')
|
||||
let valtipcallout = document.createElement('div')
|
||||
valtip.className = 'slider-value-tip'
|
||||
valtipval.className = 'value'
|
||||
valtipcallout.className = 'callout'
|
||||
valtipval.innerText = '0'
|
||||
valtip.appendChild(valtipval)
|
||||
valtip.appendChild(valtipcallout)
|
||||
binding.addOutput(valtipval)
|
||||
document.body.appendChild(valtip)
|
||||
|
||||
let inputBounds = {}
|
||||
let min = parseFloat(input.getAttribute('min'))
|
||||
let max = parseFloat(input.getAttribute('max'))
|
||||
if (isNaN(min)) {
|
||||
min = 0
|
||||
}
|
||||
if (isNaN(max)) {
|
||||
max = 1
|
||||
}
|
||||
const sliderThumbWidth = 12
|
||||
const valtipYOffset = 14
|
||||
|
||||
let updateValTipXPos = () => {
|
||||
let r = (binding.value - min) / (max - min)
|
||||
let sliderWidth = inputBounds.width - sliderThumbWidth
|
||||
let x = ((inputBounds.x + (sliderThumbWidth / 2)) + (sliderWidth * r)) - (valtip.clientWidth / 2)
|
||||
valtip.style.left = x + 'px'
|
||||
}
|
||||
|
||||
binding.addListener(updateValTipXPos)
|
||||
|
||||
let shownCounter = 0
|
||||
let showValTip = () => {
|
||||
if (++shownCounter == 1) {
|
||||
valtip.classList.add('visible')
|
||||
inputBounds = input.getBoundingClientRect()
|
||||
valtip.style.top = (inputBounds.y - valtip.clientHeight + valtipYOffset) + 'px'
|
||||
updateValTipXPos()
|
||||
}
|
||||
}
|
||||
let hideValTip = () => {
|
||||
if (--shownCounter == 0) {
|
||||
valtip.classList.remove('visible')
|
||||
}
|
||||
}
|
||||
|
||||
input.addEventListener('pointerdown', showValTip)
|
||||
input.addEventListener('pointerup', hideValTip)
|
||||
|
||||
let timer = null
|
||||
input.addEventListener('input', ev => {
|
||||
if (timer === null) {
|
||||
showValTip()
|
||||
} else {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
timer = null
|
||||
hideValTip()
|
||||
}, 400)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
initBindings() {
|
||||
let b = this.bindings
|
||||
|
||||
// let updateTracking = fontSize => {
|
||||
// if (!this.currEditable.explicitTracking) {
|
||||
// var tracking = InterUIDynamicTracking(fontSize)
|
||||
// this.isChangingBindings = true
|
||||
// b.setValue('tracking', tracking)
|
||||
// this.isChangingBindings = false
|
||||
// }
|
||||
// }
|
||||
|
||||
b.configure('tracking', 0, 'float', tracking => {
|
||||
if (!this.isChangingBindings && !this.currEditable.explicitTracking) {
|
||||
// console.log('enabled explicit tracking')
|
||||
this.currEditable.setExplicitTracking(true)
|
||||
this.setNeedsSaveValues()
|
||||
}
|
||||
})
|
||||
b.setFormatter('tracking', v => v.toFixed(3))
|
||||
|
||||
b.configure('size', 0, 'float', size => {
|
||||
let ed = this.currEditable
|
||||
if (ed) {
|
||||
setTimeout(() => {
|
||||
// HERE BE DRAGONS! Feedback loop from Editable
|
||||
if (!ed.explicitTracking) {
|
||||
this.isChangingBindings = true
|
||||
b.setValue('tracking', ed.values.tracking)
|
||||
this.isChangingBindings = false
|
||||
}
|
||||
}, 10)
|
||||
}
|
||||
})
|
||||
|
||||
b.configure('lineHeight', 1, 'float')
|
||||
|
||||
b.bindAllInputs($$('.control input', this.ui))
|
||||
b.bindAllInputs($$('.control select', this.ui))
|
||||
|
||||
$('.control input[data-binding="tracking"]').addEventListener("dblclick", ev => {
|
||||
let ed = this.currEditable
|
||||
setTimeout(() => {
|
||||
ed.setExplicitTracking(false)
|
||||
this.setNeedsSaveValues()
|
||||
this.isChangingBindings = true
|
||||
b.setValue('tracking', ed.values.tracking)
|
||||
this.isChangingBindings = false
|
||||
}, 50)
|
||||
})
|
||||
|
||||
for (let binding of b.allBindings()) {
|
||||
binding.addListener(() => this.bindingChanged(binding))
|
||||
}
|
||||
}
|
||||
|
||||
preloadFonts() {
|
||||
// Note: This has no effect on systems supporting variable fonts.
|
||||
[
|
||||
"regular",
|
||||
"italic",
|
||||
"medium",
|
||||
"medium-italic",
|
||||
"semi-bold",
|
||||
"semi-bold-italic",
|
||||
"bold",
|
||||
"bold-italic",
|
||||
"extra-bold",
|
||||
"extra-bold-italic",
|
||||
"black",
|
||||
"black-italic",
|
||||
].forEach(style => {
|
||||
let e = document.createElement('div')
|
||||
e.className = 'font-preload font-style-' + style
|
||||
e.innerText = 'a'
|
||||
document.body.appendChild(e)
|
||||
})
|
||||
}
|
||||
|
||||
bindingChanged(binding) {
|
||||
if (this.isChangingBindings) {
|
||||
// Note: this.isChangingBindings is true when binding values are
|
||||
// changed internally, in which case we do nothing here.
|
||||
return
|
||||
}
|
||||
if (this.currEditable) {
|
||||
this.currEditable.setValue(binding.name, binding.value)
|
||||
}
|
||||
this.setNeedsSaveValues()
|
||||
}
|
||||
|
||||
reset() {
|
||||
for (let ed of this.editables.values()) {
|
||||
ed.resetValues()
|
||||
}
|
||||
this.updateBindingValues()
|
||||
}
|
||||
|
||||
updateBindingValues() {
|
||||
if (this.currEditable) {
|
||||
this.isChangingBindings = true
|
||||
this.bindings.setValues(this.currEditable.values)
|
||||
this.isChangingBindings = false
|
||||
}
|
||||
}
|
||||
|
||||
saveValues() {
|
||||
if (this._saveValuesTimer !== null) {
|
||||
clearTimeout(this._saveValuesTimer)
|
||||
this._saveValuesTimer = null
|
||||
}
|
||||
if (this.currEditable) {
|
||||
this.currEditable.saveValues()
|
||||
}
|
||||
}
|
||||
|
||||
setNeedsSaveValues() {
|
||||
if (this._saveValuesTimer !== null) {
|
||||
clearTimeout(this._saveValuesTimer)
|
||||
}
|
||||
this._saveValuesTimer = setTimeout(() => this.saveValues(), 300)
|
||||
}
|
||||
|
||||
setCurrEditable(ed) {
|
||||
if (this._saveValuesTimer !== null &&
|
||||
this.currEditable &&
|
||||
!this.isChangingBindings)
|
||||
{
|
||||
this.saveValues()
|
||||
}
|
||||
this.currEditable = ed
|
||||
this.updateBindingValues()
|
||||
if (this.currEditable) {
|
||||
this.showUI()
|
||||
} else {
|
||||
this.hideUI()
|
||||
}
|
||||
}
|
||||
|
||||
onEditableReceivedFocus(ed) {
|
||||
// console.log(`onEditableReceivedFocus ${ed}`)
|
||||
clearTimeout(this._deselectTimer)
|
||||
this.setCurrEditable(ed)
|
||||
}
|
||||
|
||||
onEditableLostFocus(ed) {
|
||||
// console.log(`onEditableLostFocus ${ed}`)
|
||||
// this.setCurrEditable(null)
|
||||
if (supportsFocusTrick) {
|
||||
this._deselectTimer = setTimeout(() => this.setCurrEditable(null), 10)
|
||||
}
|
||||
}
|
||||
|
||||
showUI() {
|
||||
this.ui.classList.add('visible')
|
||||
}
|
||||
|
||||
hideUI() {
|
||||
this.ui.classList.remove('visible')
|
||||
}
|
||||
|
||||
stopEditing() {
|
||||
if (this.currEditable) {
|
||||
this.currEditable.el.blur()
|
||||
this.setCurrEditable(null)
|
||||
}
|
||||
}
|
||||
|
||||
addAllEditables() {
|
||||
for (let el of $$('[data-ctxedit]')) {
|
||||
this.addEditable(el)
|
||||
}
|
||||
}
|
||||
|
||||
addEditable(el) {
|
||||
let key = this.keyPrefix + el.dataset.ctxedit
|
||||
let existing = this.editables.get(key)
|
||||
if (existing) {
|
||||
throw new Error(`duplicate editable ${key}`)
|
||||
}
|
||||
let ed = new Editable(el, key)
|
||||
this.editables.set(key, ed)
|
||||
this.initEditable(ed)
|
||||
// this.showUI() // XXX
|
||||
}
|
||||
|
||||
initEditable(ed) {
|
||||
// filter paste
|
||||
ed.el.addEventListener('paste', ev => {
|
||||
ev.preventDefault()
|
||||
let text = ev.clipboardData.getData("text/plain")
|
||||
document.execCommand("insertHTML", false, text)
|
||||
}, {capture:true,passive:false})
|
||||
|
||||
ed.el.addEventListener('focus', ev => this.onEditableReceivedFocus(ed))
|
||||
ed.el.addEventListener('blur', ev => this.onEditableLostFocus(ed))
|
||||
}
|
||||
}
|
||||
|
||||
return CtxEdit
|
||||
})();
|
1
docs/res/dismiss.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 9l-.354.354.354.353.354-.353L8 9zm.354.354l5-5-.708-.708-5 5 .708.708zm0-.708l-5-5-.708.708 5 5 .708-.708z" fill="#fff"/><path stroke="#fff" d="M2 12.5h12"/></svg>
|
After Width: | Height: | Size: 250 B |
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 266 KiB |
1
docs/res/popup.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 3.293l3.354 3.353-.708.708L8 4.707 5.354 7.354l-.708-.708L8 3.293zm0 8L5.354 8.646l-.708.708L8 12.707l3.354-3.353-.708-.708L8 11.293z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 281 B |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 64 KiB |
1
docs/res/reset.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.993.893L6.95 2.817l1.272.187A6 6 0 1 1 2 9h1a5 5 0 1 0 5.928-4.914l-.005.032-.79-.116A5.078 5.078 0 0 0 8 4v-.018L6.684 3.79l2.214 2.908-.796.606-3.287-4.319L9.459.047l.534.846z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 285 B |
Before Width: | Height: | Size: 497 KiB After Width: | Height: | Size: 398 KiB |
1
docs/res/settings.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M14 4H6V3h8v1zM4 4H2V3h2v1zm10 3H6V6h8v1zM4 7H2V6h2v1zm10 3H6V9h8v1zM4 10H2V9h2v1zm10 3H6v-1h8v1zM4 13H2v-1h2v1z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 257 B |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 171 KiB |
@ -1 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>font-size</title><desc>Created using Figma</desc><use xlink:href="#a" transform="translate(1.191 6.91)"/><use xlink:href="#b" transform="translate(7.307 3.273)"/><defs><path id="a" d="M1.054 5.09l.37-1.083h1.95l.37 1.084h1.051L2.918 0H1.877L0 5.09h1.054zm1.315-3.852h.06l.67 1.964h-1.4l.671-1.964z"/><path id="b" d="M1.108 8.727l.822-2.335h3.64l.822 2.335H7.5L4.295 0h-1.09L0 8.727h1.108zm2.59-7.346h.103l1.436 4.074H2.263L3.699 1.38z"/></defs></svg>
|
||||
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M2.245 12l.37-1.084h1.95L4.935 12h1.052L4.11 6.91H3.068L1.191 12h1.054zM3.56 8.147h.06l.671 1.964H2.89l.671-1.964zM8.415 12l.822-2.335h3.64L13.698 12h1.108l-3.205-8.727h-1.09L7.306 12h1.108zm2.59-7.347h.103l1.436 4.074H9.57l1.436-4.074z" fill="#fff"/></svg>
|
Before Width: | Height: | Size: 583 B After Width: | Height: | Size: 341 B |
@ -1 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>letter-spacing</title><desc>Created using Figma</desc><use xlink:href="#a" transform="translate(4.307 3.273)"/><use xlink:href="#b" transform="rotate(90 -.5 1.5)"/><use xlink:href="#b" transform="rotate(90 7 9)"/><defs><path id="a" d="M1.108 8.727l.822-2.335h3.64l.822 2.335H7.5L4.295 0h-1.09L0 8.727h1.108zm2.59-7.346h.103l1.436 4.074H2.263L3.699 1.38z"/><path id="b" fill-rule="evenodd" d="M12 1H0V0h12v1z"/></defs></svg>
|
||||
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.415 12l.822-2.335h3.64L10.698 12h1.108L8.602 3.273h-1.09L4.306 12h1.108zm2.59-7.347h.103l1.436 4.074H6.57l1.436-4.074z" fill="#fff"/><path fill-rule="evenodd" clip-rule="evenodd" d="M0 14V2h1v12H0zm15 0V2h1v12h-1z" fill="#fff"/></svg>
|
Before Width: | Height: | Size: 556 B After Width: | Height: | Size: 321 B |
1
docs/samples/icons/line-height.svg
Executable file
@ -0,0 +1 @@
|
||||
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.297 12l.762-2.14h3.32L10.14 12h1.015L8.22 4h-1l-2.94 8h1.016zm1.066-3l1.324-3.734h.063L9.074 9h-2.71z" fill="#fff"/><path fill-rule="evenodd" clip-rule="evenodd" d="M15 2H1V1h14v1zm0 13H1v-1h14v1z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 304 B |
@ -1 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>style</title><desc>Created using Figma</desc><use xlink:href="#a" transform="rotate(90 -1 2)"/><use xlink:href="#b" transform="rotate(90 1 4)"/><use xlink:href="#c" transform="rotate(90 3.5 6.5)"/><use xlink:href="#d" transform="rotate(90 6.5 9.5)"/><defs><path id="a" fill-rule="evenodd" d="M10 1H0V0h10v1z"/><path id="b" fill-rule="evenodd" d="M10 2H0V0h10v2z"/><path id="c" fill-rule="evenodd" d="M10 3H0V0h10v3z"/><path id="d" fill-rule="evenodd" d="M10 4H0V0h10v4z"/></defs></svg>
|
||||
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 13V3h1v10H0zm3 0V3h2v10H3zm4 0V3h3v10H7zm5 0V3h4v10h-4z" fill="#fff"/></svg>
|
Before Width: | Height: | Size: 618 B After Width: | Height: | Size: 203 B |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 177 KiB After Width: | Height: | Size: 175 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 237 KiB After Width: | Height: | Size: 233 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 253 KiB After Width: | Height: | Size: 241 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 241 KiB After Width: | Height: | Size: 244 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 137 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 52 KiB |