website: dynamic metrics
This commit is contained in:
parent
edfd488300
commit
bd425b8fb8
4 changed files with 172 additions and 37 deletions
|
|
@ -34,6 +34,7 @@ formula {
|
|||
overflow: hidden;
|
||||
margin-right: 1em;
|
||||
margin-bottom: 1em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.row.white formula {
|
||||
background: #f5f5f5;
|
||||
|
|
@ -49,6 +50,9 @@ formula.code {
|
|||
formula > * {
|
||||
margin: 0 0.2em 0 0.2em;
|
||||
}
|
||||
formula > g {
|
||||
display: inline-block;
|
||||
}
|
||||
formula > const {
|
||||
margin-bottom: 0.11em;
|
||||
}
|
||||
|
|
@ -63,17 +67,28 @@ formula.code {
|
|||
overflow: auto;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-word;
|
||||
padding: 50px 0 0 50px; /* note: samples have 50px right margin */
|
||||
}
|
||||
.samples .sample {
|
||||
/*background: lightpink;*/
|
||||
color: #111;
|
||||
flex: 0 1 auto;
|
||||
outline: none;
|
||||
margin-right: 50px;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.samples .sample .di {
|
||||
.samples .sample > * {
|
||||
display: block;
|
||||
outline: none;
|
||||
}
|
||||
.samples .sample .content {
|
||||
padding-left: 10px;
|
||||
border-left: 2px solid transparent;
|
||||
margin-left: -12px;
|
||||
}
|
||||
.samples .sample.selected .content {
|
||||
border-left-color: rgb(45, 143, 255);
|
||||
}
|
||||
.samples .sample .di {
|
||||
background-color: #ccc;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
|
|
@ -118,7 +133,6 @@ formula.code {
|
|||
}
|
||||
.row.with-sidebar > *:first-child {
|
||||
flex: 1 1 auto;
|
||||
padding: 50px 0 0 50px; /* note: samples have 50px right margin */
|
||||
}
|
||||
.row.with-sidebar > .sidebar {
|
||||
flex: 0 0 auto;
|
||||
|
|
@ -159,9 +173,17 @@ div.controls > h3 {
|
|||
div.controls > textarea {
|
||||
border: none;
|
||||
padding:16px;
|
||||
height: 400px;
|
||||
height: 220px;
|
||||
font-family: "SFMono-Regular", Menlo, Consolas, Inconsolata, monospace;
|
||||
outline: none;
|
||||
resize: vertical;
|
||||
color: #999;
|
||||
}
|
||||
div.controls > textarea:focus {
|
||||
color: inherit;
|
||||
}
|
||||
div.controls textarea#code-output {
|
||||
height: 50px;
|
||||
}
|
||||
div.controls .control > * {
|
||||
/*max-width: 50%;*/
|
||||
|
|
@ -229,20 +251,19 @@ div.controls .control > label {
|
|||
div.controls canvas {
|
||||
height: 200px;
|
||||
}
|
||||
div.controls .when-selection {
|
||||
display: none;
|
||||
}
|
||||
div.controls.has-selected-sample .when-selection {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.row.small-window {
|
||||
.row.narrow-window {
|
||||
margin-top:0;
|
||||
padding-top:0;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 541px) {
|
||||
.small-window {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 540px) {
|
||||
|
||||
@media only screen and (max-width: 565px) {
|
||||
.row.with-sidebar {
|
||||
overflow: auto;
|
||||
}
|
||||
|
|
@ -250,15 +271,16 @@ div.controls canvas {
|
|||
div.controls {
|
||||
display: none;
|
||||
}
|
||||
div.controls .graphplot,
|
||||
div.controls hr.without-top-margin,
|
||||
div.controls h3,
|
||||
div.controls #ideal-values
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.row.with-sidebar {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
/* small devices (<= iPhone 6+) */
|
||||
@media only screen and (max-device-width: 414px) {
|
||||
.samples {
|
||||
padding-left: 20px;
|
||||
padding-right: 50px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ endfor
|
|||
<p>
|
||||
There's of course no absolute right or wrong when it comes to expressing
|
||||
yourself with typography, but Inter UI Dynamic Metrics provides guidelines
|
||||
for <em>good</em> typography.
|
||||
You simply provide the optical font size, and the tracking and leading
|
||||
(letter spacing and line height) will be calculated using the following
|
||||
for how to best use Inter UI.
|
||||
You simply provide the optical font size,
|
||||
and the tracking and leading is calculated for you through the following
|
||||
formula:
|
||||
</p>
|
||||
<p>
|
||||
|
|
@ -44,31 +44,34 @@ endfor
|
|||
<formula>
|
||||
leading = <num>1.4</num> × fontSize
|
||||
</formula>
|
||||
<formula>
|
||||
<const title="Constant a">a</const> = <num data-binding="var-a">-0.016</num>
|
||||
<const title="Constant b">b</const> = <num data-binding="var-b">0.21</num>
|
||||
<const title="Constant c">c</const> = <num data-binding="var-c">-0.18</num>
|
||||
<const title="Base of natural logarithm">e</const> ≈ <num>2.718</num>
|
||||
<formula title="Values for Inter UI">
|
||||
<g><const title="Constant a">a</const> = <num data-binding="var-a">-0.016</num></g>
|
||||
<g><const title="Constant b">b</const> = <num data-binding="var-b">0.21</num></g>
|
||||
<g><const title="Constant c">c</const> = <num data-binding="var-c">-0.18</num></g>
|
||||
<g class="wide-window"><const title="Base of natural logarithm">e</const> ≈ <num>2.718</num></g>
|
||||
</formula>
|
||||
</p>
|
||||
<p class="small-window">
|
||||
<p class="wide-window">
|
||||
The controls below can be used to play around with the
|
||||
<const>a</const>, <const>b</const> and <const>c</const> constants to discover
|
||||
how they affect tracking.
|
||||
</p>
|
||||
<p class="narrow-window">
|
||||
<small>View this on a larger screen to enable interactive control.</small>
|
||||
</p>
|
||||
</div></div>
|
||||
|
||||
<!-- <div class="row small-window"><div>
|
||||
Hello
|
||||
</div></div> -->
|
||||
|
||||
<div class="white full-width row with-sidebar">
|
||||
|
||||
<div class="samples">
|
||||
<p contenteditable class="sample">
|
||||
<p class="sample">
|
||||
<span class="di"><span></span></span>
|
||||
<span class="info"
|
||||
title="size, tracking, (ideal tracking) — progress bar shows distance from ideal"
|
||||
>15 0.0 ( 0.0 )</span>
|
||||
<span contenteditable class="content">
|
||||
Such a riot of sea and wind strews the whole extent of beach with whatever has been lost or thrown overboard, or torn out of sunken ships. Many a man has made a good week’s work in a single day by what he has found while walking along the Beach when the surf was down.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -121,6 +124,11 @@ endfor
|
|||
</div>
|
||||
<hr class="without-bottom-margin">
|
||||
<canvas class="graphplot">Canvas not Supported</canvas>
|
||||
|
||||
<hr class="when-selection without-top-margin">
|
||||
<h3 class="when-selection">CSS</h3>
|
||||
<textarea class="when-selection" readonly id="code-output"></textarea>
|
||||
|
||||
<hr class="without-top-margin">
|
||||
<h3>Ideal values</h3>
|
||||
<textarea id="ideal-values"></textarea>
|
||||
|
|
@ -137,6 +145,10 @@ endfor
|
|||
var baseTracking = 0
|
||||
var weightClass = 400
|
||||
|
||||
function round(num, prec) {
|
||||
return parseFloat(num.toFixed(prec))
|
||||
}
|
||||
|
||||
// Ideal values designed one by one, by hand.
|
||||
// font size => tracking
|
||||
var idealValues = {}
|
||||
|
|
@ -267,6 +279,7 @@ var NPOS = Number.MAX_SAFE_INTEGER
|
|||
function Sample(fontSize) {
|
||||
this.rootEl = sampleTemplate.cloneNode(true)
|
||||
this.infoEl = $('.info', this.rootEl)
|
||||
this.contentEl = $('.content', this.rootEl)
|
||||
this.diEl = $('.di', this.rootEl)
|
||||
this.diEl.classList.add('unavailable')
|
||||
this.style = this.rootEl.style
|
||||
|
|
@ -278,8 +291,24 @@ function Sample(fontSize) {
|
|||
this.matchesIdeal = false
|
||||
this.setFontSize(fontSize)
|
||||
this.render()
|
||||
|
||||
// listen for focus events on the editable content
|
||||
var s = this
|
||||
this.contentEl.addEventListener(
|
||||
'focus',
|
||||
function(){ s.onReceivedFocus() },
|
||||
{passive:true, capture:false}
|
||||
)
|
||||
this.contentEl.addEventListener(
|
||||
'blur',
|
||||
function(){ s.onLostFocus() },
|
||||
{passive:true, capture:false}
|
||||
)
|
||||
}
|
||||
|
||||
Sample.prototype.onReceivedFocus = function() {}
|
||||
Sample.prototype.onLostFocus = function() {}
|
||||
|
||||
Sample.prototype.idealDistance = function(fontSize) {
|
||||
// returns the distance from the ideal (or NPOS if no "ideal" data available)
|
||||
if (this.idealTracking == NPOS) {
|
||||
|
|
@ -322,6 +351,14 @@ Sample.prototype.setFontSize = function(fontSize) {
|
|||
}
|
||||
}
|
||||
|
||||
Sample.prototype.cssProperties = function() {
|
||||
return {
|
||||
fontSize: round(this.fontSize, 3) + 'px',
|
||||
letterSpacing: round(this.tracking, 3) + 'em',
|
||||
lineHeight: round(this.lineHeight, 3) + 'px',
|
||||
}
|
||||
}
|
||||
|
||||
Sample.prototype.render = function() {
|
||||
this.style.fontSize = this.fontSize + 'px'
|
||||
this.style.letterSpacing = this.tracking + 'em'
|
||||
|
|
@ -346,8 +383,10 @@ Sample.prototype.render = function() {
|
|||
|
||||
|
||||
var bindings = new Bindings()
|
||||
var samplesEl = $('.samples')
|
||||
var sampleTemplate
|
||||
var samples = [] // Sample[]
|
||||
var focusedSample = null // Sample | null
|
||||
var graph = new GraphPlot($('canvas.graphplot'))
|
||||
graph.setOrigin(-0.2, 0.8)
|
||||
graph.setScale(0.049, 5)
|
||||
|
|
@ -362,7 +401,6 @@ function addChildren(targetNode, children) {
|
|||
}
|
||||
|
||||
function initSamples() {
|
||||
var samplesEl = $('.samples')
|
||||
sampleTemplate = $('.sample', samplesEl)
|
||||
samplesEl.removeChild(sampleTemplate)
|
||||
|
||||
|
|
@ -380,10 +418,28 @@ function initSamples() {
|
|||
samples.push(new Sample(30))
|
||||
samples.push(new Sample(40))
|
||||
|
||||
// connect focus events
|
||||
var onSampleReceivedFocus = function() { setSelectedSample(this) }
|
||||
samples.forEach(function(s) {
|
||||
s.onReceivedFocus = onSampleReceivedFocus
|
||||
})
|
||||
|
||||
// add to dom in one go
|
||||
addChildren(samplesEl, samples.map(function(s) { return s.rootEl }))
|
||||
}
|
||||
|
||||
function setSelectedSample(sample) {
|
||||
if (focusedSample !== sample) {
|
||||
if (focusedSample) {
|
||||
focusedSample.rootEl.classList.remove('selected')
|
||||
}
|
||||
if (sample) {
|
||||
sample.rootEl.classList.add('selected')
|
||||
}
|
||||
focusedSample = sample
|
||||
updateSelection()
|
||||
}
|
||||
}
|
||||
|
||||
function updateSample(sample) {
|
||||
sample.setFontSize(sample.fontSize) // updates derived values
|
||||
|
|
@ -394,6 +450,7 @@ function updateSamples() {
|
|||
samples.forEach(updateSample)
|
||||
updateIdealMatches()
|
||||
updateGraphPlot()
|
||||
updateCodeView()
|
||||
}
|
||||
|
||||
function updateIdealMatches() {
|
||||
|
|
@ -424,6 +481,46 @@ function updateGraphPlot() {
|
|||
graph.plotf(function(x) {
|
||||
return InterUIDynamicTracking(x, weightClass)
|
||||
})
|
||||
if (focusedSample) {
|
||||
var graphedFontSize = Math.min(24, focusedSample.fontSize) // clamp to [-inf,24]
|
||||
graph.plotPoints([
|
||||
[graphedFontSize, focusedSample.tracking]
|
||||
], 'rgb(45, 143, 255)')
|
||||
}
|
||||
}
|
||||
|
||||
var codeOutput = $('#code-output')
|
||||
codeOutput.addEventListener('focus', function(ev) {
|
||||
ev.preventDefault()
|
||||
ev.stopPropagation()
|
||||
codeOutput.select()
|
||||
}, {passive:false,capture:true})
|
||||
|
||||
function updateCodeView() {
|
||||
var s = ''
|
||||
if (focusedSample) {
|
||||
var cssprops = focusedSample.cssProperties()
|
||||
var props = Object.keys(cssprops)
|
||||
props.forEach(function(prop, i) {
|
||||
s += prop + ': ' + cssprops[prop] + ';'
|
||||
if (i != props.length-1) {
|
||||
s += '\n'
|
||||
}
|
||||
})
|
||||
}
|
||||
codeOutput.value = s
|
||||
}
|
||||
|
||||
function updateSelection() {
|
||||
var controlsEl = $('.controls')
|
||||
if (focusedSample) {
|
||||
controlsEl.classList.add('has-selected-sample')
|
||||
} else {
|
||||
controlsEl.classList.remove('has-selected-sample')
|
||||
}
|
||||
|
||||
updateGraphPlot()
|
||||
updateCodeView()
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -456,7 +553,7 @@ bindings.configure('ideal-count', 0, 'int', function (v) {})
|
|||
bindings.configure('ideal-distance', 0, 'float', function (v) {})
|
||||
|
||||
bindings.configure('style', null, null, function (style) {
|
||||
var cl = $('.samples').classList
|
||||
var cl = samplesEl.classList
|
||||
cl.remove('font-style-regular')
|
||||
cl.remove('font-style-italic')
|
||||
cl.remove('font-style-medium')
|
||||
|
|
@ -490,6 +587,17 @@ idealValuesTextArea.addEventListener('input', function(ev) {
|
|||
updateSamples()
|
||||
})
|
||||
|
||||
// listen for clicks onto "background", to deselect any selected sample
|
||||
document.body.addEventListener('pointerdown', function(ev){
|
||||
if (
|
||||
ev.target === document.body ||
|
||||
ev.target === samplesEl ||
|
||||
ev.target.classList && ev.target.classList.contains('row')
|
||||
) {
|
||||
setSelectedSample(null)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// start
|
||||
initSamples()
|
||||
|
|
|
|||
Reference in a new issue