website: major update

This commit is contained in:
Rasmus Andersson 2018-02-20 01:38:51 -08:00
parent 761638ef42
commit 9f367901ef
22 changed files with 2139 additions and 1030 deletions

1
.gitignore vendored
View file

@ -17,6 +17,7 @@ nohup.out
build build
/_* /_*
/docs/lab/fonts /docs/lab/fonts
/docs/_site
src/FontInspector.html src/FontInspector.html
src/svg src/svg

View file

@ -159,13 +159,13 @@ install_otf: all_otf
install: install_otf install: install_otf
geninfo: docs/info.json docs/lab/glyphinfo.json docs/glyphs/metrics.json geninfo: docs/_data/fontinfo.json docs/lab/glyphinfo.json docs/glyphs/metrics.json
src/glyphorder.txt: src/Inter-UI-Regular.ufo/lib.plist src/Inter-UI-Black.ufo/lib.plist src/diacritics.txt misc/gen-glyphorder.py src/glyphorder.txt: src/Inter-UI-Regular.ufo/lib.plist src/Inter-UI-Black.ufo/lib.plist src/diacritics.txt misc/gen-glyphorder.py
misc/gen-glyphorder.py src/Inter-UI-*.ufo > src/glyphorder.txt misc/gen-glyphorder.py src/Inter-UI-*.ufo > src/glyphorder.txt
docs/info.json: misc/fontinfo.py docs/font-files/Inter-UI-*.otf docs/_data/fontinfo.json: misc/fontinfo.py docs/font-files/Inter-UI-*.otf
misc/fontinfo.py -pretty docs/font-files/Inter-UI-Regular.otf > docs/info.json misc/fontinfo.py -pretty docs/font-files/Inter-UI-Regular.otf > docs/_data/fontinfo.json
docs/lab/glyphinfo.json: _local/UnicodeData.txt src/glyphorder.txt src/fontbuild.cfg misc/gen-glyphinfo.py docs/lab/glyphinfo.json: _local/UnicodeData.txt src/glyphorder.txt src/fontbuild.cfg misc/gen-glyphinfo.py
misc/gen-glyphinfo.py -ucd _local/UnicodeData.txt \ misc/gen-glyphinfo.py -ucd _local/UnicodeData.txt \

10
docs/_config.yml Normal file
View file

@ -0,0 +1,10 @@
port: 3000
lsi: false
permalink: /:title
markdown: kramdown
# Since GH pages override this to "true", we test this value to see if we are running locally
safe: false
kramdown:
input: GFM
auto_ids: true
hard_wrap: false

View file

@ -1,5 +1,5 @@
[
{ {
"Inter UI Regular:2018:86daccf": {
"head": { "head": {
"checkSumAdjustment": 3690365233, "checkSumAdjustment": 3690365233,
"created": 3563720514, "created": 3563720514,
@ -117,6 +117,7 @@
"minMemType42": 0, "minMemType42": 0,
"underlinePosition": -422, "underlinePosition": -422,
"underlineThickness": 170 "underlineThickness": 170
},
"version": "2.5"
} }
} ]
}

View file

@ -0,0 +1,46 @@
{% if site.safe == false %}
<!-- During authoring, this automatically reloads the post as its changing -->
<script type="text/javascript">
(function() {
var qs = document.location.search;
var current_etag = qs.match(/etag=("?[a-zA-Z0-9_-]+)/);
if (current_etag !== null) { current_etag = current_etag[1]; }
var scrolly = qs.match(/scrolly=([0-9]+)/);
if (scrolly) {
scrolly = parseInt(scrolly[1]);
if (scrolly > 0) {
window.scrollTo(window.scrollX, scrolly);
setTimeout(function () {
window.scrollTo(window.scrollX, scrolly);
}, 10);
}
}
function check() {
var r = new XMLHttpRequest();
var url = document.location.href + ((qs && qs !== '') ? '&' : '?') + 'r=' + Math.random();
r.open('GET', url, true);
r.onreadystatechange = function() {
if (r.readyState == 4){
var found_etag = r.getResponseHeader('Etag');
if (found_etag) {
found_etag = found_etag.replace(/^"|"$/g);
}
//console.log('current_etag:', current_etag, 'found_etag:', found_etag);
if (current_etag === null) {
current_etag = found_etag;
} else if (found_etag !== current_etag) {
document.location.search =
'?etag=' + encodeURIComponent(found_etag) +
'&scrolly=' + window.scrollY;
return;
}
setTimeout(check, 500);
}
};
r.send(null);
}
check();
})();
</script>
{% endif %}

View file

@ -0,0 +1,88 @@
{%
assign build_version = site.time | date: "%Y%m%d%H%M%S" %}{%
assign description = "Inter UI is a new typeface optimized for computer user interfaces" %}{%
capture url_root
%}{% if site.safe == false %}/{% else %}/inter/{% endif
%}{%
endcapture %}{%
capture release_version
%}{{ site.data.fontinfo[0].version }}{%
endcapture %}{%
capture download_url
%}https://github.com/rsms/inter/releases/download/v{{ release_version }}/Inter-UI-{{ release_version }}.zip{%
endcapture %}{%
for file in site.static_files %}{%
assign _path = file.path | remove_first: "/inter" %}{%
if _path == "/res/base.css" %}{%
assign base_css_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
elsif _path == "/res/base.js" %}{%
assign base_js_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
endif %}{%
endfor
%}<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{% if page.title %}{{ page.title }} — Inter UI{% else %}Inter UI font family{% endif %}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="{{url_root}}inter-ui.css?v={{ release_version }}">
<link rel="stylesheet" href="{{url_root}}res/base.css?v={{ base_css_v }}">
<link rel="icon" type="image/png" href="{{url_root}}res/favicon.png">
<meta name="format-detection" content="telephone=no">
<meta property="twitter:card" content="summary">
<meta property="twitter:site" content="@rsms">
<meta property="twitter:creator" content="@rsms">
<meta property="description" content="{{description}}">
<meta property="og:description" content="{{description}}">
<meta property="twitter:description" content="{{description}}">
{% if page.title %}
<meta property="og:title" content="{{ page.title }}">
<meta property="twitter:title" content="{{ page.title }}">
{% endif %}
{% if page.og_image_url %}
<meta property="og:image" content="{{ page.og_image_url }}">
<meta property="twitter:image" content="{{ page.og_image_url }}">
{% else %}
<meta property="og:image" content="https://rsms.me/inter/res/poster.png">
<meta property="twitter:image" content="https://rsms.me/inter/res/poster.png">
{% endif %}
<meta property="og:url" content="https://rsms.me/inter/{{ page.url | remove_first:'index.html' }}">
<meta property="fb:app_id" content="38027689216">
<meta property="og:site_name" content="rsms.me/inter">
<meta property="og:type" content="product">
<meta property="og:locale" content="en_US" />
</head>
<body>
<script src="{{url_root}}res/base.js?v={{ base_js_v }}"></script>
<div class="row menu">
<ul class="menu">
<li class="home"><a href="{{url_root}}">Inter UI</a></li>
<li><a class="download-link" href="{{ download_url }}"
>Download</a></li>
<li><a href="{{url_root}}samples/"
{% if page.url contains "/samples/" %}class="active"{% endif %}
>Samples</a></li>
<li><a href="{{url_root}}lab/"
{% if page.url contains "/lab/" %}class="active"{% endif %}
>Playground</a></li>
<li><a href="https://github.com/rsms/inter/"
>Source</a></li>
</ul>
</div>
{{ content }}
{% if site.safe == true %}
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-105091131-2"></script>
{% endif %}
{% include autoreload-in-debug.html %}
</body>
</html>

View file

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
set -e set -e
cd "$(dirname "$0")" cd "$(dirname "$0")/.."
pushd res >/dev/null pushd res >/dev/null

9
docs/_scripts/serve.sh Executable file
View file

@ -0,0 +1,9 @@
#!/bin/sh
set -e
cd "$(dirname "$0")/.."
if [ ! -s lab/fonts ]; then
ln -s ../../build/dist lab/fonts
fi
jekyll serve --limit_posts 20 --watch --host 127.0.0.1 --port 3002 --open-url

262
docs/dynmetrics/index.css Normal file
View file

@ -0,0 +1,262 @@
body {
padding-bottom: 0;
}
const {
display: inline;
font-size: 1.2em;
font-style: italic;
font-family: 'Times New Roman', Times, serif;
}
sup {
/*background:lightpink;*/
display: inline-block;
font-size:0.92em;
position: relative;
top:-0.4em;
letter-spacing: 0.001em;
vertical-align: baseline;
}
.row.first {
padding-bottom:1em;
}
formula {
display: inline-flex;
align-items: center;
background: white;
border-radius: 5px;
padding: 0 1em;
line-height: 3em;
height: 3em;
overflow: hidden;
margin-right: 1em;
margin-bottom: 1em;
}
.row.white formula {
background: #f5f5f5;
}
formula:last-child {
margin-right: 0;
}
formula.code {
white-space: pre;
font-family: "SFMono-Regular", Menlo, Consolas, Inconsolata, monospace;
font-size:0.96em;
}
formula > * {
margin: 0 0.2em 0 0.2em;
}
formula > const {
margin-bottom: 0.11em;
}
formula > sup {
margin-left: 0;
}
.samples {
display: flex;
flex-wrap: wrap;
overflow: auto;
overflow-wrap: break-word;
word-break: break-word;
}
.samples .sample {
/*background: lightpink;*/
color: #111;
flex: 0 1 auto;
outline: none;
margin-right: 50px;
margin-bottom: 50px;
}
.samples .sample .di {
display: block;
background-color: #ccc;
height: 1px;
width: 100%;
margin-bottom: 8px;
}
.samples .sample .di > span {
display: block;
background-color: #333;
height: 100%;
}
.samples .sample .di.match > span {
background-color: #0d3;
}
.samples .sample .di.unavailable {
background-color: #eee;
}
.samples .sample .di.unavailable > span {
visibility: hidden;
}
.samples .sample .info {
display: block;
font-size: 11px !important;
line-height: 11px;
margin-bottom: 9px;
color: #bbb;
}
.font-style-regular { font-weight:400 !important; font-style:normal !important; }
.font-style-italic { font-weight:400 !important; font-style:italic !important; }
.font-style-medium { font-weight:500 !important; font-style:normal !important; }
.font-style-medium-italic { font-weight:500 !important; font-style:italic !important; }
.font-style-bold { font-weight:700 !important; font-style:normal !important; }
.font-style-bold-italic { font-weight:700 !important; font-style:italic !important; }
.font-style-black { font-weight:900 !important; font-style:normal !important; }
.font-style-black-italic { font-weight:900 !important; font-style:italic !important; }
.row.with-sidebar {
padding: 0;
}
.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;
}
div.controls {
box-sizing: border-box;
width: 250px;
max-width: 250px;
flex: 0 0 auto;
padding: 10px 0;
border-left: 4px solid #f4f4f4;
display: flex;
flex-direction: column;
overflow: hidden;
}
div.controls hr {
border: none;
height: 2px;
background: #f4f4f4;
margin-top: 10px;
margin-bottom: 10px;
}
div.controls hr.without-bottom-margin { margin-bottom: 0; }
div.controls hr.without-top-margin { margin-top: 0; }
div.controls hr.without-margins { margin: 0; }
div.controls .control {
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
height: 30px;
margin: 0 16px;
}
div.controls > h3 {
margin: 0 16px;
}
div.controls > textarea {
border: none;
padding:16px;
height: 400px;
font-family: "SFMono-Regular", Menlo, Consolas, Inconsolata, monospace;
outline: none;
}
div.controls .control > * {
/*max-width: 50%;*/
flex: 1 1 auto;
margin:0;
margin-right: 16px;
box-sizing: border-box;
}
div.controls .control > :last-child {
margin-right: 0;
}
div.controls .control > select {
min-width: 6em;
align-items: center;
justify-content: center;
}
div.controls .control > input,
div.controls .control > select {
width: 0;
outline: none;
}
div.controls .control > input[type="number"],
div.controls .control > input[type="text"] {
background: none;
border: none;
padding: 4px 0;
font-size: 13px;
}
div.controls .control > input[type="number"] {
max-width: 60px;
-moz-font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
-ms-font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
-o-font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
-webkit-font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
}
div.controls .control > input[type=number]::-webkit-inner-spin-button,
div.controls .control > input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
div.controls .control > input[type="number"][readonly] {
max-width: 40px;
}
div.controls .control > input.wide[type="number"] {
max-width: 100%;
}
div.controls .control > input[type="range"] {
/*max-width: 80%;*/
flex: 1 1 auto;
display: block;
}
div.controls .control > img.icon,
div.controls .control > label {
font-family: georgia, serif;
font-style: italic;
line-height: 16px;
color: black;
width: 16px;
height: 16px;
flex: 0 0 auto;
margin-right: 16px;
opacity: 0.6;
}
div.controls canvas {
height: 200px;
}
.row.small-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) {
.row.with-sidebar {
overflow: auto;
}
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;
}
}

522
docs/dynmetrics/index.html Normal file
View file

@ -0,0 +1,522 @@
---
layout: default
title: Dynamic Metrics
---
{%
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 == "/dynmetrics/index.css" %}{%
assign index_css_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
elsif _path == "/res/bindings.js" %}{%
assign bindings_js_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
elsif _path == "/res/graphplot.js" %}{%
assign graphplot_js_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
endif %}{%
endfor
%}
<link rel="stylesheet" href="index.css?v={{ index_css_v }}">
<script src="{{url_root}}res/bindings.js?v={{ bindings_js_v }}"></script>
<script src="{{url_root}}res/graphplot.js?v={{ graphplot_js_v }}"></script>
<div class="row first"><div>
<h1>Dynamic Metrics</h1>
<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
formula:
</p>
<p>
<formula>
tracking =
<const>a</const> + <const>b</const> ×
<const>e</const><sup>(<const>c</const> × fontSize)</sup>
</formula>
<formula>
leading = <const>l</const> × fontSize
</formula>
<formula>
<const>e</const><num>2.718</num>
</formula>
</p>
<p class="small-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">
<span class="di"><span></span></span>
<span class="info"
title="size, tracking, (ideal tracking) — progress bar shows distance from ideal"
>15 &nbsp; 0.0 &nbsp; ( 0.0 )</span>
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 weeks work in a single day by what he has found while walking along the Beach when the surf was down.
</p>
</div>
<div class="sidebar controls">
<div class="control">
<label title="Number of ideal matches">ni</label>
<input title="Number of ideal matches" type="number" readonly data-binding="ideal-count">
<label title="Distance from ideal">di</label>
<input title="Distance from ideal" type="number" class="wide" readonly data-binding="ideal-distance">
</div>
<div class="control">
<img title="Style" class="icon" src="../samples/icons/style.svg">
<select data-binding="style">
<option value="regular" default>Regular</option>
<option value="italic">Italic</option>
<option value="medium" default>Medium</option>
<option value="medium-italic">Medium Italic</option>
<option value="bold" default>Bold</option>
<option value="bold-italic">Bold Italic</option>
<option value="black" default>Black</option>
<option value="black-italic">Black Italic</option>
</select>
</div>
<div class="control">
<img title="Base tracking" class="icon" src="../samples/icons/letter-spacing.svg">
<input type="range" min="-0.05" max="0.06" step="0.001" data-binding="base-tracking">
<input type="number" min="-0.15" max="1" step="0.001" data-binding="base-tracking">
</div>
<hr>
<div class="control">
<label title="Constant a">a</label>
<input type="range" min="-0.05" max="0" step="0.001" data-binding="var-a">
<input type="number" min="-0.05" max="0" step="0.0001" data-binding="var-a">
</div>
<div class="control">
<label title="Constant b">b</label>
<input type="range" min="0" max="1" step="0.01" data-binding="var-b">
<input type="number" min="0" max="1" step="0.001" data-binding="var-b">
</div>
<div class="control">
<label title="Constant c">c</label>
<input type="range" min="-1" max="0" step="0.01" data-binding="var-c">
<input type="number" min="-1" max="0" step="0.001" data-binding="var-c">
</div>
<hr>
<div class="control">
<label title="Constant l controls line height">l</label>
<input type="range" min="1" max="2" step="0.1" data-binding="var-l">
<input type="number" min="1" max="2" step="0.1" data-binding="var-l">
</div>
<hr class="without-bottom-margin">
<canvas class="graphplot">Canvas not Supported</canvas>
<hr class="without-top-margin">
<h3>Ideal values</h3>
<textarea id="ideal-values"></textarea>
<hr class="without-top-margin">
</div>
</div>
<script type="text/javascript">(function(){
// internal variables that are user-controllable, but are not part of
// Dynamic Metrics
var baseTracking = 0
var weightClass = 400
// Ideal values designed one by one, by hand.
// font size => tracking
var idealValues = {}
var idealValuesList = []
var idealValuesTextArea = $('textarea#ideal-values')
function setIdealValues(valueMap) {
idealValues = valueMap
idealValuesList = []
Object.keys(idealValues).forEach(function(fontSize) {
idealValuesList.push([fontSize, idealValues[fontSize]])
})
if (idealValuesTextArea.value == '') {
idealValuesTextArea.value = idealValuesList.map(function(t){
return t[0] + '\t' + t[1]
}).join('\n')
}
}
function parseValues(s) {
var values = {}
s = s.replace(/^[\s\r\t\n]+|[\s\r\t\n]+$/g, '') // trim whitespace
s.split(/\s*\r?\n\s*/).map(function(line) {
var t = line.split(/\s+/)
if (t.length == 2) {
t[0] = parseInt(t[0])
t[1] = parseFloat(t[1])
values[t[0]] = t[1]
}
})
return values
}
setIdealValues({
// set-2018-02-20
6: 0.055,
7: 0.044,
8: 0.034,
9: 0.024,
10: 0.018,
11: 0.012,
12: 0.007,
13: 0.003,
14: 0.001,
15: -0.002,
16: -0.004,
17: -0.006,
18: -0.008,
20: -0.01,
24: -0.013,
})
// setIdealValues({
// // set-2018-02-19
// 6: 0.059,
// 7: 0.043,
// 8: 0.032,
// 9: 0.022,
// 10: 0.014,
// 11: 0.009,
// 12: 0.004,
// 13: 0,
// 14: -0.003,
// 15: -0.005,
// 16: -0.0075,
// 17: -0.0084,
// 18: -0.0095,
// 20: -0.012,
// 24: -0.014,
// })
// setIdealValues({
// // set-2018-02-18
// 6: 0.06,
// 7: 0.04,
// 8: 0.03,
// 9: 0.02,
// 10: 0.01,
// 11: 0.005,
// 12: 0.002,
// 13: 0.001,
// 14: 0,
// 15: -0.002,
// 16: -0.005,
// 17: -0.007,
// 18: -0.009,
// 20: -0.011,
// 24: -0.011,
// })
// Variables for constants involved in Dynamic Metrics
var a = -0.013
// -0.0119
// -0.0101
// -0.0092
// -0.012
// -0.013
var b = 0.23
// 0.455
// 0.421
// 0.26
// 0.23
// 0.251
var c = -0.21
// -0.29
// -0.23
// -0.21
// -0.222
// a = -0.012; b = 0.23; c = -0.21; // di=0.002514 on set-2018-02-18
// a = -0.013; b = 0.251; c = -0.222 // di=0.001742 on set-2018-02-18
// a = -0.015; b = 0.283; c = -0.23; // di=0.00221 on set-2018-02-18
// a = -0.0149; b = 0.298; c = -0.23; // di=0.000484 on set-2018-02-19
a = -0.016; b = 0.21; c = -0.18; // di=0.000532 on set-2018-02-20
// these have a short di, but stray far away from larger font sizes
// a = -0.0077
// b = 0.377
var l = 1.4
// InterUIDynamicTracking takes the font size in points or pixels and returns
// the compensating tracking in EM.
//
function InterUIDynamicTracking(fontSize, weightClass) {
// if (!weightClass) { -- currently unused
// weightClass = 400
// }
//
// y = -0.01021241 + 0.3720623 * e ^ (-0.2808687 * x)
// [6-35] 0.05877 .. -0.0101309 (13==0; stops growing around 30-35)
// See https://gist.github.com/rsms/8efdbca5f8145a584ed08a7c3d6e5788
//
return a + b * Math.pow(Math.E, c * fontSize)
// [6 - 38] 0.05798 .. -0.01099 (midpoint = 12.533)
// y = 0.025 - (ln(x) * 0.01)
// return 0.025 - Math.log(fontSize) * 0.01
}
function InterUIDynamicLineHeight(fontSize, weightClass) {
return Math.round(fontSize * l)
}
var NPOS = Number.MAX_SAFE_INTEGER
function Sample(fontSize) {
this.rootEl = sampleTemplate.cloneNode(true)
this.infoEl = $('.info', this.rootEl)
this.diEl = $('.di', this.rootEl)
this.diEl.classList.add('unavailable')
this.style = this.rootEl.style
this.maxBoxWidth = 0
this.fontSize = 0
this.tracking = 0
this.lineHeight = 0
this.idealTracking = NPOS
this.matchesIdeal = false
this.setFontSize(fontSize)
this.render()
}
Sample.prototype.idealDistance = function(fontSize) {
// returns the distance from the ideal (or NPOS if no "ideal" data available)
if (this.idealTracking == NPOS) {
return NPOS
}
return (
Math.max(this.tracking, this.idealTracking) -
Math.min(this.tracking, this.idealTracking)
)
}
Sample.prototype.setFontSize = function(fontSize) {
this.fontSize = fontSize
this.tracking = baseTracking + InterUIDynamicTracking(fontSize, weightClass)
this.lineHeight = InterUIDynamicLineHeight(fontSize, weightClass)
this.maxBoxWidth = Math.round(fontSize * (this.tracking + 1) * 25)
var idealTracking = idealValues[this.fontSize]
if (idealTracking === undefined) {
if (this.idealTracking != NPOS) {
this.diEl.classList.add('unavailable')
}
this.idealTracking = NPOS
this.matchesIdeal = false
} else {
if (this.idealTracking == NPOS) {
this.diEl.classList.remove('unavailable')
}
this.idealTracking = idealTracking
// match to a precision of 3
this.matchesIdeal = this.tracking.toFixed(3) == idealTracking.toFixed(3)
var di = 1
if (this.matchesIdeal) {
this.diEl.classList.add('match')
} else {
this.diEl.classList.remove('match')
di = 1 - Math.min(1, this.idealDistance() * 50)
}
this.diEl.firstChild.style.width = (di * this.maxBoxWidth) + 'px'
}
}
Sample.prototype.render = function() {
this.style.fontSize = this.fontSize + 'px'
this.style.letterSpacing = this.tracking + 'em'
this.style.lineHeight = this.lineHeight + 'px'
var SP = '\u00A0\u00A0\u00A0'
this.infoEl.innerText = (
this.fontSize +
SP + this.tracking.toFixed(3) +
// SP + this.lineHeight.toFixed(3) +
(
this.idealTracking != NPOS ? (
SP + '( ' + this.idealTracking +
(this.matchesIdeal ? ' \u2713' : '') +
' )'
) : ''
)
)
this.style.maxWidth = this.maxBoxWidth + 'px'
}
var bindings = new Bindings()
var sampleTemplate
var samples = [] // Sample[]
var graph = new GraphPlot($('canvas.graphplot'))
graph.setOrigin(-0.2, 0.8)
graph.setScale(0.049, 5)
function addChildren(targetNode, children) {
targetNode.style.visibility = 'hidden'
var i = 0;
for (; i < children.length; i++) {
targetNode.appendChild(children[i])
}
targetNode.style.visibility = null
}
function initSamples() {
var samplesEl = $('.samples')
sampleTemplate = $('.sample', samplesEl)
samplesEl.removeChild(sampleTemplate)
// small and medium sizes
var i, fontSize = 6, endFontSize = 16
for (; fontSize <= endFontSize; fontSize++) {
samples.push(new Sample(fontSize))
}
// a few select large samples
samples.push(new Sample(17))
samples.push(new Sample(18))
samples.push(new Sample(20))
samples.push(new Sample(24))
samples.push(new Sample(30))
samples.push(new Sample(40))
// add to dom in one go
addChildren(samplesEl, samples.map(function(s) { return s.rootEl }))
}
function updateSample(sample) {
sample.setFontSize(sample.fontSize) // updates derived values
sample.render()
}
function updateSamples() {
samples.forEach(updateSample)
updateIdealMatches()
updateGraphPlot()
}
function updateIdealMatches() {
// ideal-distance
var idealCount = 0
var distance = 0
var ndistances = 0
samples.forEach(function(sample) {
if (sample.matchesIdeal) {
idealCount++
}
var di = sample.idealDistance()
if (di != NPOS) {
distance += di
ndistances++
}
})
distance = distance / ndistances
bindings.setValue('ideal-distance', distance.toFixed(6))
bindings.setValue('ideal-count', idealCount)
}
function updateGraphPlot() {
graph.clear()
graph.plotLine(idealValuesList, '#0d3')
graph.plotf(function(x) {
return InterUIDynamicTracking(x, weightClass)
})
}
bindings.configure('base-tracking', 0, 'float', function (tracking) {
baseTracking = tracking
updateSamples()
})
bindings.configure('var-a', a, 'float', function (v) {
a = v
updateSamples()
})
bindings.configure('var-b', b, 'float', function (v) {
b = v
updateSamples()
})
bindings.configure('var-c', c, 'float', function (v) {
c = v
updateSamples()
})
bindings.configure('var-l', l, 'float', function (v) {
l = v
updateSamples()
})
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
cl.remove('font-style-regular')
cl.remove('font-style-italic')
cl.remove('font-style-medium')
cl.remove('font-style-medium-italic')
cl.remove('font-style-bold')
cl.remove('font-style-bold-italic')
cl.remove('font-style-black')
cl.remove('font-style-black-italic')
cl.add('font-style-' + style)
weightClass = (
style.indexOf('black') != -1 ? 900 :
style.indexOf('bold') != -1 ? 700 :
style.indexOf('medium') != -1 ? 500 :
400
)
updateSamples()
})
bindings.bindAllInputs('.control input')
bindings.bindAllInputs('.control select')
// double-click base tracking to reset
$('input[type="range"][data-binding="base-tracking"]').addEventListener(
'dblclick',
function(ev) { bindings.setValue('base-tracking', 0) }
)
// allow editing of ideal values
idealValuesTextArea.addEventListener('input', function(ev) {
setIdealValues(parseValues(idealValuesTextArea.value))
updateSamples()
})
// start
initSamples()
updateSamples()
})();</script>

View file

@ -1,38 +1,26 @@
<!DOCTYPE HTML> ---
<html lang="en" prefix="og: http://ogp.me/ns#"> layout: default
<head> title: Glyphs
<meta charset="utf-8"> ---
<title>Repertoire Inter UI font family</title> {%
<meta name="viewport" content="width=device-width, initial-scale=1"> capture url_root
<meta http-equiv="X-UA-Compatible" content="IE=edge"> %}{% if site.safe == false %}/{% else %}/inter/{% endif
%}{%
endcapture %}{%
<meta property="og:title" content="Inter UI font family"> for file in site.static_files %}{%
<meta property="twitter:title" content="Inter UI font family"> assign _path = file.path | remove_first: "/inter" %}{%
<meta property="description" content="Inter UI is a new typeface optimized for computer user interfaces"> if _path == "/glyphs/glyphs.css" %}{%
<meta property="og:description" content="Inter UI is a new typeface optimized for computer user interfaces"> assign glyphs_css_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
<meta property="twitter:description" content="Inter UI is a new typeface optimized for computer user interfaces"> elsif _path == "/glyphs/glyphs.js" %}{%
<meta property="twitter:card" content="summary"> assign glyphs_js_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
<meta property="twitter:site" content="@rsms"> endif %}{%
<meta property="twitter:creator" content="@rsms"> endfor
<meta property="og:image" content="https://rsms.me/inter/res/poster.png">
<meta property="twitter:image" content="https://rsms.me/inter/res/poster.png">
<meta property="fb:app_id" content="38027689216"> %}
<meta property="fb:admins" content="728642302">
<meta property="og:url" content="https://rsms.me/inter/">
<meta property="og:site_name" content="rsms.me">
<meta property="og:type" content="product">
<meta property="og:locale" content="en_US" />
<link rel="icon" type="image/png" href="../favicon.png" /> <link rel="stylesheet" href="glyphs.css?v={{ glyphs_css_v }}">
<link href="../inter-ui.css" rel="stylesheet">
<link href="../index.css" rel="stylesheet">
<link href="glyphs.css" rel="stylesheet">
</head>
<body>
<script src="../index.js"></script>
<div id="svgs"> <div id="svgs">
<svg id="svg-A" xmlns="http://www.w3.org/2000/svg" width="176" height="348"><path d="M146 -76.8 L44.8 -76.8 L44.8 -54.8 L146 -54.8z M33.2 0 L94 -172.4 L96.4 -172.4 L157.2 0 L183.2 0 L108 -204.8 L82.4 -204.8 L7.2 0z" transform="translate(-7.2 281.6)"/></svg> <svg id="svg-A" xmlns="http://www.w3.org/2000/svg" width="176" height="348"><path d="M146 -76.8 L44.8 -76.8 L44.8 -54.8 L146 -54.8z M33.2 0 L94 -172.4 L96.4 -172.4 L157.2 0 L183.2 0 L108 -204.8 L82.4 -204.8 L7.2 0z" transform="translate(-7.2 281.6)"/></svg>
<svg id="svg-AE" xmlns="http://www.w3.org/2000/svg" width="264" height="348"><path d="M142.4 -186 L144.8 -204.8 L127.2 -204.8 L6 0 L35.6 0z M164 -76.8 L60.4 -76.8 L60.4 -55.2 L164 -55.2z M269.2 -22 L168.4 -22 L168.4 0 L269.2 0z M164.8 -204.8 L140.4 -204.8 L148.8 0 L173.2 0z M249.6 -115.2 L162.8 -115.2 L162.8 -93.2 L249.6 -93.2z M260.8 -204.8 L160 -204.8 L160 -182.8 L260.8 -182.8z" transform="translate(-6 281.6)"/></svg> <svg id="svg-AE" xmlns="http://www.w3.org/2000/svg" width="264" height="348"><path d="M142.4 -186 L144.8 -204.8 L127.2 -204.8 L6 0 L35.6 0z M164 -76.8 L60.4 -76.8 L60.4 -55.2 L164 -55.2z M269.2 -22 L168.4 -22 L168.4 0 L269.2 0z M164.8 -204.8 L140.4 -204.8 L148.8 0 L173.2 0z M249.6 -115.2 L162.8 -115.2 L162.8 -93.2 L249.6 -93.2z M260.8 -204.8 L160 -204.8 L160 -182.8 L260.8 -182.8z" transform="translate(-6 281.6)"/></svg>
@ -2223,8 +2211,7 @@
</div><!--END-SVGS don't remove this comment--> </div><!--END-SVGS don't remove this comment-->
<div class="row intro"><div> <div class="row intro"><div>
<h2 class="back"><a href="../">The Inter UI font family</a></h2> <h1><a href="./">Glyphs</a></h1>
<h1><a href="./">Inter UI glyphs</a></h1>
<p> <p>
This shows the complete set of glyphs in Inter UI Regular. This shows the complete set of glyphs in Inter UI Regular.
You can zoom in and out by pressing You can zoom in and out by pressing
@ -2253,6 +2240,4 @@
<div id="kerning-list"></div> <div id="kerning-list"></div>
</div></div> </div></div>
<script src="glyphs.js"></script> <script src="glyphs.js?v={{ glyphs_js_v }}"></script>
</body>
</html>

View file

@ -1,43 +1,6 @@
<!DOCTYPE HTML> ---
<html lang="en" prefix="og: http://ogp.me/ns#"> layout: default
<head> ---
<meta charset="utf-8">
<title>Inter UI font family</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta property="og:title" content="Inter UI font family">
<meta property="twitter:title" content="Inter UI font family">
<meta property="description" content="Inter UI is a new typeface optimized for computer user interfaces">
<meta property="og:description" content="Inter UI is a new typeface optimized for computer user interfaces">
<meta property="twitter:description" content="Inter UI is a new typeface optimized for computer user interfaces">
<meta property="twitter:card" content="summary">
<meta property="twitter:site" content="@rsms">
<meta property="twitter:creator" content="@rsms">
<meta property="og:image" content="https://rsms.me/inter/res/poster.png">
<meta property="twitter:image" content="https://rsms.me/inter/res/poster.png">
<meta property="fb:app_id" content="38027689216">
<meta property="og:url" content="https://rsms.me/inter/">
<meta property="og:site_name" content="rsms.me">
<meta property="og:type" content="product">
<meta property="og:locale" content="en_US" />
<meta name="format-detection" content="telephone=no">
<link rel="icon" type="image/png" href="favicon.png" />
<link href="inter-ui.css?v=2.5" rel="stylesheet">
<link href="index.css?v=5" rel="stylesheet">
</head>
<body>
<div class="row menu">
<ul class="menu">
<li class="home"><a href="../">Inter UI</a></li>
<li><a class="download-link"
href="https://github.com/rsms/inter/releases/latest/"
>Download</a></li>
<li><a href="samples/">Samples</a></li>
<li><a href="lab/">Playground</a></li>
<li><a href="https://github.com/rsms/inter/">Source</a></li>
</ul>
</div>
<div class="row"><div> <div class="row"><div>
<h1>The Inter UI font family</h1> <h1>The Inter UI font family</h1>
@ -55,7 +18,7 @@
</div></div> </div></div>
<div class="row white" style="padding-bottom:0"><div> <div class="row white" style="padding-bottom:0"><div>
<p id="samples" class="samples items"> <p id="samples" class="samples items sample-images">
<a href="samples/" class="plain"><img src="samples/img/01.png" srcset="samples/img/01@2x.png 2x" width="888"></a> <a href="samples/" class="plain"><img src="samples/img/01.png" srcset="samples/img/01@2x.png 2x" width="888"></a>
</p> </p>
<p style="text-align:center"> <p style="text-align:center">
@ -82,6 +45,14 @@
<p>&nbsp;</p> <p>&nbsp;</p>
<h2><a href="{{url_root}}dynmetrics/">Dynamic Metrics</a></h2>
<p>
There's of course no absolute right or wrong when it comes to expressing yourself with typography, but Inter UI <em>Dynamic Metrics</em> provides guidelines for good typography. You simply provide the optical font size, and the tracking and leading is calculated for you to produce the best results.
<a href="{{url_root}}dynmetrics/">Learn about Dynamic Metrics —></a>
</p>
<p>&nbsp;</p>
<h2><a id="free" href="#free">How much does it cost?</a></h2> <h2><a id="free" href="#free">How much does it cost?</a></h2>
<p> <p>
Inter UI is a <a href="https://github.com/rsms/inter">free and open source</a> font family. You are free to use this font in almost any way imaginable. Inter UI is a <a href="https://github.com/rsms/inter">free and open source</a> font family. You are free to use this font in almost any way imaginable.
@ -326,9 +297,7 @@
<a href="https://twitter.com/rsms" class="plain">@rsms</a> <a href="https://twitter.com/rsms" class="plain">@rsms</a>
</div></div> </div></div>
<script src="index.js?v=2"></script> <script>(function(){
<script>
(function(){
// FAQ anchors // FAQ anchors
var av = document.querySelectorAll('ul.faq > li.q'), a, i, e, id, tn var av = document.querySelectorAll('ul.faq > li.q'), a, i, e, id, tn
@ -354,7 +323,4 @@
e.insertBefore(tn, e.firstChild) e.insertBefore(tn, e.firstChild)
} }
})(); })();</script>
</script>
</body>
</html>

View file

@ -1,138 +0,0 @@
var isMac = false
function $$(query, el) {
return [].slice.call((el || document).querySelectorAll(query))
}
function $(query, el) {
return (el || document).querySelector(query)
}
// fetchjson(url string, cb (err Error, d Object)->nil)
//
var fetchjson = (
typeof window.fetch == 'function' ? (
function _fetchjson(url, cb) {
return window.fetch(url)
.then(function(r) { return r.json() })
.then(function(data) { cb(null, data) })
.catch(cb)
}
) :
function _fetchjson(url, cb) {
var r = new XMLHttpRequest()
r.addEventListener("load", function(){
try {
cb(null, JSON.parse(r.responseText))
} catch (err) {
cb(err)
}
})
r.open("GET", url)
r.send()
}
)
;(function(){
"use strict";
// anim.min.js
var anim=function(h){h=function(a,e,f,b){var g,d,c=[],j=function(a){
if(a=c.shift())a[1]?h.apply(this,a).anim(j):0<a[0]?setTimeout(j,1E3*a[0]):(a[0](),j())};
a.charAt&&(a=document.getElementById(a));if(0<a||!a)e={},f=0,j(c=[[a||0]]);
q(e,{padding:0,margin:0,border:"Width"},[l,m,n,p]);q(e,{borderRadius:"Radius"},[l+p,l+m,n+m,n+p]);++r;
for(g in e)d=e[g],!d.to&&0!==d.to&&(d=e[g]={to:d}),h.defs(d,a,g,b);h.iter(e,1E3*f,j);
return{anim:function(){c.push([].slice.call(arguments));return this}}};
var l="Top", m="Right",n="Bottom",p="Left",r=1,q=function(a,e,f,b,g,d,c){for(b in a)if(b in e){c=a[b];
for(g=0;d=f[g];g++)a[b.replace(e[b],"")+d+(e[b]||"")]={to:0===c.to?c.to:c.to||c,fr:c.fr,e:c.e};
delete a[b]}},s=function(w,a){return w["r"+a]||w["webkitR"+a]||w["mozR"+a]||w["msR"+a]||w["oR"+a]}(
window,"equestAnimationFrame");h.defs=function(a,e,f,b,g){g=e.style;a.a=f;a.n=e;a.s=f in g?g:e;
a.e=a.e||b;a.fr=a.fr||(0===a.fr?0:a.s==e?e[f]:(window.getComputedStyle?getComputedStyle(e, null)
:e.currentStyle)[f]);a.u=(/\d(\D+)$/.exec(a.to)||/\d(\D+)$/.exec(a.fr)||[0,0])[1];a.fn=/color/i.test(f)?
h.fx.color:h.fx[f]||h.fx._;a.mx="anim_"+f;e[a.mx]=a.mxv=r;e[a.mx]!=a.mxv&&(a.mxv=null)};h.iter=function(a,e,f){
var b,g,d,c,h,k=+new Date+e;b=function(){g=k-(new Date).getTime();if(50>g){
for(d in a)d=a[d],d.p=1,d.fn(d,d.n,d.to,d.fr,d.a,d.e);f&&f()}else{g/=e;for(d in a){d=a[d];
if(d.n[d.mx]!=d.mxv)return;h=d.e;c=g;"lin"==h?c=1-c:"ease"==h?(c=2*(0.5-c),c=1-(c*c*c-3*c+2)/4):
"ease-in"==h?(c= 1-c,c*=c*c):c=1-c*c*c;d.p=c;d.fn(d,d.n,d.to,d.fr,d.a,d.e)}s?s(b):setTimeout(b,20)}};
b()};h.fx={_:function(a,e,f,b,g){b=parseFloat(b)||0;f=parseFloat(f)||0;a.s[g]=(1<=a.p?f:a.p*(f-b)+b)+a.u},
width:function(a,e,f,b,g,d){0<=a._fr||(a._fr=!isNaN(b=parseFloat(b))?b:"width"==g?e.clientWidth:e.clientHeight);
h.fx._(a,e,f,a._fr,g,d)},opacity:function(a,e,f,b,g){if(isNaN(b=b||a._fr))b=e.style,b.zoom=1,
b=a._fr=(/alpha\(opacity=(\d+)\b/i.exec(b.filter)||{})[1]/100||1;b*=1;f=a.p*(f-b)+b;e=e.style;g in e?e[g]= f:
e.filter=1<=f?"":"alpha("+g+"="+Math.round(100*f)+")"},color:function(a,e,f,b,g,d,c,j){
a.ok||(f=a.to=h.toRGBA(f),b=a.fr=h.toRGBA(b),0==f[3]&&(f=[].concat(b),f[3]=0),0==b[3]&&
(b=[].concat(f),b[3]=0),a.ok=1);j=[0,0,0,a.p*(f[3]-b[3])+1*b[3]];for(c=2;0<=c;c--)j[c]=Math.round(
a.p*(f[c]-b[c])+1*b[c]);(1<=j[3]||h.rgbaIE)&&j.pop();try{a.s[g]=(3<j.length?"rgba(":"rgb(")+j.join(",")+")"
}catch(k){h.rgbaIE=1}}};h.fx.height=h.fx.width;
h.RGBA=/#(.)(.)(.)\b|#(..)(..)(..)\b|(\d+)%,(\d+)%,(\d+)%(?:,([\d\.]+))?|(\d+),(\d+),(\d+)(?:,([\d\.]+))?\b/;
h.toRGBA=function(a,e){e=[0,0,0,0];a.replace(/\s/g,"").replace(h.RGBA,function(a,b,g,d,c,h,k,l,m,n,p,q,r,s,t){
k=[b+b||c,g+g||h,d+d||k];b=[l,m,n];for(a=0;3>a;a++)k[a]=parseInt(k[a],16),b[a]=Math.round(2.55*b[a]);
e=[k[0]||b[0]||q||0,k[1]||b[1]||r||0,k[2]||b[2]||s||0,p||t||1]});return e};return h}();
if (!window.MSStream &&
/mac|ipad|iphone|ipod/i.test(navigator.userAgent))
{
isMac = true
document.body.classList.add('mac_or_ios')
if (navigator.userAgent.indexOf('Safari') != -1) {
document.body.classList.add('safari')
}
}
// timeNow() :float
var timeNow = (
window.performance !== undefined && window.performance.now ? function() {
return window.performance.now()
} : Date.now ? function() {
return Date.now()
} : function() {
return (new Date()).getTime()
}
)
// download-link
fetchjson('/inter/info.json', function(err, data) {
if (err) { throw err }
var ids = Object.keys(data)
var regularId = ids[0]
ids.forEach(function(id){
if (id.indexOf('Inter UI Regular:') == 0) {
regularId = id
}
})
if (ids.length == 0) {
console.error('failed to find Inter UI Regular in info.json', data)
return
}
var regular = data[regularId]
// console.log('info.json:', regular)
if (regular.names && regular.names.version) {
var v = regular.names.version
var p = v.indexOf(';')
if (p != -1) {
v = v.substr(0, p)
}
var directDownloadURL =
'https://github.com/rsms/inter/releases/download/v' + v +
'/Inter-UI-' + v + '.zip'
var av = document.querySelectorAll('a.download-link'), i, e
for (i = 0; i < av.length; ++i) {
e = av[i]
e.href = directDownloadURL
}
}
})
// Google Analytics
;(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-105091131-2', 'auto');
ga('send', 'pageview');
})();

View file

@ -6,8 +6,8 @@ body {
font: 15px/22px 'Inter UI', system-ui, sans-serif; font: 15px/22px 'Inter UI', system-ui, sans-serif;
font-size: 15px; font-size: 15px;
line-height:1.4; line-height: 1.5;
/*letter-spacing: 0.009em;*/ letter-spacing: -0.002em;
font-weight: 400; font-weight: 400;
padding-bottom: 30px; padding-bottom: 30px;
@ -98,14 +98,20 @@ dem { /* de-emphasize */
opacity: 0.7; opacity: 0.7;
} }
num { /* number */ num { /* number */
-moz-font-feature-settings: 'calt' 1, 'ss01' 1; /*-moz-font-feature-settings: 'calt' 1, 'ss01' 1;
-ms-font-feature-settings: 'calt' 1, 'ss01' 1; -ms-font-feature-settings: 'calt' 1, 'ss01' 1;
-o-font-feature-settings: 'calt' 1, 'ss01' 1; -o-font-feature-settings: 'calt' 1, 'ss01' 1;
-webkit-font-feature-settings: 'calt' 1, 'ss01' 1; -webkit-font-feature-settings: 'calt' 1, 'ss01' 1;
font-feature-settings: 'calt' 1, 'ss01' 1; font-feature-settings: 'calt' 1, 'ss01' 1;*/
letter-spacing:0.02em;
white-space: pre; white-space: pre;
} }
small {
font-size: 11px;
letter-spacing: 0.013em;
}
h1, h2, h3 { h1, h2, h3 {
font-weight: 500; font-weight: 500;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
@ -181,6 +187,27 @@ h1 > a, h2 > a, h3 > a {
max-width: 888px; max-width: 888px;
flex: 1 0 100%; flex: 1 0 100%;
} }
.row .learn-more {
margin-top: 2em;
text-align: center;
font-size: 11px;
letter-spacing: 0.007em;
}
.row .learn-more a {
color: rgba(0,0,0,0.4);
text-decoration-color: rgba(0, 0, 0, 0);
}
.row .learn-more a:hover {
color: inherit;
}
.row.full-width {
padding: 50px 0;
justify-content: flex-start;
}
.row.full-width > * {
max-width: initial;
}
.row-divider { .row-divider {
margin:0 auto; margin:0 auto;
@ -228,77 +255,6 @@ h1 > a, h2 > a, h3 > a {
opacity: 1; opacity: 1;
} }
/* narrow windows */
@media only screen and (max-width: 565px) {
.row.menu ul {
justify-content: space-between;
}
.row.menu ul li {
margin-right: 15px;
}
.row.menu ul li:last-child {
margin-right: 0;
}
.row.menu ul li.home {
/*color: red;
clear: both;*/
/*display: block;*/
text-align:center;
margin:0 0 -12px 0;
width: 100%;
}
.row.menu ul li.home > a {
border-bottom: none;
padding: 0 1em;
margin: 0.5em 0;
line-height:34px;
border-radius: 90px;
/*color: white;
background-color: rgba(3, 102, 214, 1);*/
}
.row.menu ul li.home > a:hover {
color: white;
background-color: #222;
}
}
/* small devices (<= iPhone 6+) */
@media only screen and (max-device-width: 414px) {
body {
font-size: 14px;
line-height: 20px;
}
.row {
padding-left: 20px;
padding-right: 20px;
}
.row.menu ul {
margin-left: 20px;
margin-right: 20px;
}
}
/* small devices (<= iPhone 5) */
@media only screen and (max-device-width: 320px) {
.row.menu {
font-size:13px;
}
.row.menu ul {
margin-left: 0;
margin-right: 0;
}
.row.menu ul li {
flex: 1 0 auto;
text-align: center;
border-right: 1px solid rgba(0,0,0,0.1);
margin-left:0;
margin-right:0;
}
.row.menu ul li:last-child {
border-right: none;
}
}
.row.white { .row.white {
background: white; background: white;
@ -346,14 +302,10 @@ a > img {
} }
.sample-images {} .sample-images {}
.sample-images > img, .sample-images > svg { .sample-images img, .sample-images svg {
display: block; display: block;
margin:100px 0;
width: 100%; width: 100%;
} }
.sample-images > img:first-child, .sample-images > svg:first-child {
margin-top:50px;
}
/* FAQ */ /* FAQ */
@ -493,6 +445,8 @@ boxes {
} }
box { box {
overflow: auto; overflow: auto;
flex: 1 1 0;
min-width: 280px;
max-width: 100%; max-width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -501,8 +455,6 @@ box {
border-radius: 3px; border-radius: 3px;
margin-right:1em; margin-right:1em;
margin-bottom:1em; margin-bottom:1em;
flex: 1 1 10%;
/*width:220px;*/
} }
body.safari box { body.safari box {
/* Fix for broken flex wrap in safari */ /* Fix for broken flex wrap in safari */
@ -514,3 +466,85 @@ box:first-child {
box h3 { box h3 {
margin-bottom:0.8em; margin-bottom:0.8em;
} }
/* ------------------------------------------------------ */
/* narrow windows */
@media only screen and (max-width: 565px) {
.row.menu ul {
justify-content: space-between;
}
.row.menu ul li {
margin-right: 15px;
}
.row.menu ul li:last-child {
margin-right: 0;
}
.row.menu ul li.home {
/*color: red;
clear: both;*/
/*display: block;*/
text-align:center;
margin:0 0 -12px 0;
width: 100%;
}
.row.menu ul li.home > a {
border-bottom: none;
padding: 0 1em;
margin: 0.5em 0;
line-height:34px;
border-radius: 90px;
/*color: white;
background-color: rgba(3, 102, 214, 1);*/
}
.row.menu ul li.home > a:hover {
color: white;
background-color: #222;
}
}
/* small devices (<= iPhone 6+) */
@media only screen and (max-device-width: 414px) {
box { padding: 1em; }
box tablex r { font-size: 0.9em; }
body {
font-size: 14px;
line-height: 20px;
}
.row {
padding-left: 20px;
padding-right: 20px;
}
.row.menu ul {
margin-left: 20px;
margin-right: 20px;
}
}
/* small devices (<= iPhone 5) */
@media only screen and (max-device-width: 320px) {
box {
font-size: 0.8em;
min-width: 240px;
}
/*.row.menu {
font-size:13px;
}
.row.menu ul {
margin-left: 0;
margin-right: 0;
}
.row.menu ul li {
flex: 1 0 auto;
text-align: center;
border-right: 1px solid rgba(0,0,0,0.1);
margin-left:0;
margin-right:0;
}
.row.menu ul li:last-child {
border-right: none;
}*/
}

74
docs/res/base.js Normal file
View file

@ -0,0 +1,74 @@
function $$(query, el) {
return [].slice.call((el || document).querySelectorAll(query))
}
function $(query, el) {
return (el || document).querySelector(query)
}
// fetchjson(url string) :Promise<Object>
//
var fetchjson = (
typeof window.fetch == 'function' ? (
function _fetchjson(url, cb) {
return window.fetch(url).then(function(r) { return r.json() })
}
) :
function _fetchjson(url, cb) {
return new Promise(function(resolve, reject) {
var r = new XMLHttpRequest()
r.addEventListener("load", function(){
try {
resolve(JSON.parse(r.responseText))
} catch (err) {
reject(err)
}
})
r.addEventListener("error", function(ev) {
reject(ev.error || ev || new Error('network error'))
})
r.open("GET", url)
r.send()
})
}
)
// timeNow() :float
//
var timeNow = (
window.performance !== undefined && window.performance.now ? function() {
return window.performance.now()
} : Date.now ? function() {
return Date.now()
} : function() {
return (new Date()).getTime()
}
)
// Mac or not? Maybe even a buggy Safari?
var isMac = false
if (!window.MSStream &&
/mac|ipad|iphone|ipod/i.test(navigator.userAgent))
{
isMac = true
if (navigator.userAgent.indexOf('Safari') != -1 &&
navigator.userAgent.indexOf('Chrome') == -1)
{
document.body.classList.add('safari')
}
}
// Google Analytics
// ;(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
// (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
// m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
// })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
// ga('create', 'UA-105091131-2', 'auto');
// ga('send', 'pageview');
window.dataLayer = window.dataLayer || [];
window.dataLayer.push(['js', new Date()])
window.dataLayer.push(['config', 'UA-105091131-2'])

View file

@ -1,11 +1,14 @@
// requires index.js // requires index.js
function passThrough(v) { return v }
function Binding(name){ function Binding(name){
this.name = name this.name = name
this.value = undefined this.value = undefined
this.inputs = [] this.inputs = []
this.listeners = [] this.listeners = []
this.formatter = undefined this.parser = undefined
this.formatter = passThrough
} }
@ -22,7 +25,7 @@ Binding.prototype.addInput = function(el) {
if (this.value === undefined) { if (this.value === undefined) {
this.value = el.value this.value = el.value
} else { } else {
input.el.value = this.value input.el.value = this.formatter(this.value)
} }
el.addEventListener('input', _onInput, {passive:true}) el.addEventListener('input', _onInput, {passive:true})
} }
@ -39,8 +42,8 @@ Binding.prototype.addListener = function(listener) {
Binding.prototype.setValue = function(nextval, origin) { Binding.prototype.setValue = function(nextval, origin) {
// console.log('Binding.setValue nextval:', nextval, {origin}) // console.log('Binding.setValue nextval:', nextval, {origin})
var prevval = this.value var prevval = this.value
if (this.formatter) { if (this.parser) {
nextval = this.formatter(nextval, prevval) nextval = this.parser(nextval, prevval)
} }
if (this.value === nextval) { if (this.value === nextval) {
return return
@ -49,7 +52,7 @@ Binding.prototype.setValue = function(nextval, origin) {
this.value = nextval this.value = nextval
this.inputs.forEach(function(input) { this.inputs.forEach(function(input) {
if (input.el !== origin) { if (input.el !== origin) {
input.el.value = nextval input.el.value = binding.formatter(nextval)
} }
}) })
this.listeners.forEach(function(listener) { this.listeners.forEach(function(listener) {
@ -106,6 +109,11 @@ Bindings.prototype.setValue = function(name, value) {
binding.setValue(value) binding.setValue(value)
} }
Bindings.prototype.setFormatter = function(name, formatter) {
var binding = this.getBinding(name)
binding.formatter = formatter || passThrough
}
Bindings.prototype.value = function(name, defaultValue) { Bindings.prototype.value = function(name, defaultValue) {
var binding = this.bindings[name] var binding = this.bindings[name]
@ -125,11 +133,11 @@ function fmt_int(nextval, prevval) {
// configure is convenience function for setting value, adding a // configure is convenience function for setting value, adding a
// listener and associating a formatter with a binding. // listener and associating a parser with a binding.
// If a listener and a value is provided, the value is set and the listener // If a listener and a value is provided, the value is set and the listener
// is immediately invoked. // is immediately invoked.
// //
Bindings.prototype.configure = function(name, value, formatter, listener) { Bindings.prototype.configure = function(name, value, parser, listener) {
var binding = this.getBinding(name) var binding = this.getBinding(name)
if (listener) { if (listener) {
binding.addListener(listener) binding.addListener(listener)
@ -137,23 +145,23 @@ Bindings.prototype.configure = function(name, value, formatter, listener) {
if (value !== undefined && value !== null) { if (value !== undefined && value !== null) {
binding.setValue(value) binding.setValue(value)
} }
if (formatter) { if (parser) {
if (typeof formatter == 'string') { if (typeof parser == 'string') {
switch (formatter) { switch (parser) {
case 'number': case 'number':
case 'float': case 'float':
formatter = fmt_float; break; parser = fmt_float; break;
case 'int': case 'int':
case 'integer': case 'integer':
formatter = fmt_int; break; parser = fmt_int; break;
default: default:
throw new Error('unknown formatter "' + formatter + '"') throw new Error('unknown parser "' + parser + '"')
} }
} else if (typeof formatter != 'function') { } else if (typeof parser != 'function') {
throw new Error('formatter should be a string or function') throw new Error('parser should be a string or function')
} }
binding.formatter = formatter binding.parser = parser
} }
} }

View file

Before

Width:  |  Height:  |  Size: 700 B

After

Width:  |  Height:  |  Size: 700 B

Before After
Before After

239
docs/res/graphplot.js Normal file
View file

@ -0,0 +1,239 @@
function GraphPlot(canvas) {
this.canvas = canvas
const g = canvas.getContext('2d')
if (g == null) {
throw new Error('failed to acquire 2d context')
}
this.width = 0 // dp
this.height = 0 // dp
this.widthPx = 0 // px
this.heightPx = 0 // px
this.pixelRatio = 1
this.g = g
this.dataSegments = []
this.axes = {
x0: .5, // % from left to x=0
y0: .5, // % from top to y=0
scalex: 40, // pixels from x=0 to x=1
scaley: 40, // pixels from y=0 to y=1
negativeX: true,
}
if (!this.autosize()) {
this.setSize(256, 256)
}
}
GraphPlot.prototype.autosize = function() {
try {
this.canvas.width = null
this.canvas.height = null
this.canvas.style.width = null
this.canvas.style.height = null
var cs = window.getComputedStyle(this.canvas)
var width = parseFloat(cs.width)
var height = parseFloat(cs.height)
this.setSize(width, height)
return true
} catch (err) {
if (typeof console != 'undefined' && console.warn) {
console.warn('GraphPlot.autosize failed: ' + err)
}
}
return false
}
// setOrigin sets the origin of axis x and y
// The values should be in the range [0-1] and maps to the extremes
// of the canvas.
//
GraphPlot.prototype.setOrigin = function(x, y) {
var p = this
p.axes.x0 = x
p.axes.y0 = y
}
// setScale sets the value scale for x and y axis.
// The values should be provided as display points.
//
GraphPlot.prototype.setScale = function(x, y) {
var p = this
if (y === undefined) {
y = x
}
p.axes.scalex = x
p.axes.scaley = y
}
// setSize sets the size of canvas in display points
//
GraphPlot.prototype.setSize = function(width, height) {
var p = this
p.width = width
p.height = height
const el = p.canvas, g = p.g
p.pixelRatio = window.devicePixelRatio || 1
if (p.pixelRatio != 1) {
el.width = p.widthPx = width * p.pixelRatio
el.height = p.heightPx = height * p.pixelRatio
g.scale(p.pixelRatio, p.pixelRatio)
} else {
el.width = p.widthPx = width
el.height = p.heightPx = height
g.scale(1, 1)
}
el.style.width = `${width}px`
el.style.height = `${height}px`
}
GraphPlot.prototype.renderAxes = function() {
var p = this
, g = p.g
, x0 = Math.round(p.axes.x0 * p.widthPx) / p.pixelRatio
, y0 = Math.round(p.axes.y0 * p.heightPx) / p.pixelRatio
g.beginPath()
g.strokeStyle = "rgb(0, 0, 0, 0.2)"
if (y0 > 0 && y0 < p.width) {
g.moveTo(0, y0); g.lineTo(p.width, y0) // X axis
}
if (x0 > 0 && x0 < p.height) {
g.moveTo(x0, 0); g.lineTo(x0, p.height) // Y axis
}
g.stroke()
}
// plotf plots an arbitrary function on the graph
//
GraphPlot.prototype.plotf = function(f, color) {
var p = this
, g = p.g
, w = p.width
, h = p.height
, x0 = p.axes.x0 * p.width
, y0 = p.axes.y0 * p.height
, x = 0
, y = 0
, dx = 4 / p.pixelRatio // smaller means finer curves and more CPU
, scalex = p.axes.scalex * w
, scaley = p.axes.scaley * h
, iMax = Math.round((w - x0) / dx)
, iMin = p.axes.negativeX ? Math.round(-x0 / dx) : 0
g.beginPath()
g.lineWidth = 1
g.strokeStyle = color || "rgb(0, 0, 0, 0.8)"
for (var i = iMin; i <= iMax; i++) {
x = dx * i
y = f(x / scalex) * scaley
if (i == iMin) {
g.moveTo(x0 + x, y0 - y)
} else {
g.lineTo(x0 + x, y0 - y)
}
}
g.stroke()
}
// plotLines draws straight lines between a collection of points
//
GraphPlot.prototype.plotLine = function(points, color) {
var p = this
, g = p.g
, x0 = p.axes.x0 * p.width
, y0 = p.axes.y0 * p.height
, x = 0
, y = 0
, scalex = p.axes.scalex * p.width
, scaley = p.axes.scaley * p.height
, pt
g.beginPath()
g.lineWidth = 1
g.strokeStyle = color || "rgb(0, 0, 0, 0.8)"
var i = 0
for (; i < points.length; i++) {
pt = points[i]
x = pt[0] * scalex
y = pt[1] * scaley
if (i == 0) {
g.moveTo(x0 + x, y0 - y)
} else {
g.lineTo(x0 + x, y0 - y)
}
}
g.stroke()
}
// plotPoints draws points
//
GraphPlot.prototype.plotPoints = function(points, color) {
var p = this
, g = p.g
, x0 = p.axes.x0 * p.width
, y0 = p.axes.y0 * p.height
, x = 0
, y = 0
, scalex = p.axes.scalex * p.width
, scaley = p.axes.scaley * p.height
, pt
, i = 0
g.fillStyle = color || "rgb(0, 0, 0, 0.8)"
for (; i < points.length; i++) {
pt = points[i]
x = x0 + pt[0] * scalex
y = y0 - pt[1] * scaley
g.beginPath()
g.arc(x, y, 3, 0, Math.PI + (Math.PI * 2) / 2, false)
g.fill()
}
}
GraphPlot.prototype.clear = function() {
var p = this
p.g.clearRect(0, 0, p.width, p.height)
p.renderAxes()
}
GraphPlot.prototype.renderDemo = function() {
var p = this
, g = p.g
, dpscale = p.pixelRatio
, w = p.widthPx
, h = p.heightPx
p.clear()
p.plotf(
function(x) { return Math.sin(x) },
'blue'
)
p.plotf(
function(x) { return Math.cos(3*x) },
'hotpink'
)
// var scale = p.height / 4
// g.moveTo(0, scale)
// var i, sine, lines = 200, frag = p.width / lines
// for (i = 0; i < lines; i++) {
// sine = Math.sin(i / scale * 2) * scale
// g.lineTo(i * frag, -sine + scale)
// }
// g.stroke()
}

243
docs/samples/index.css Normal file
View file

@ -0,0 +1,243 @@
body {
padding-bottom: 0;
background: white;
}
.row.footer h2 {
margin:0;
text-align:center;
}
livesample {
display: block;
color: #111;
outline: none;
padding-left: 20px;
border-left: 2px solid transparent;
margin-left:-22px;
margin-top: 1em;
margin-bottom: 1.6em;
}
livesample:hover {
/*color: rgb(3, 102, 214);*/
border-left-color: rgb(3, 102, 214);
}
livesample:focus {
border-left-color: #eee;
}
livesample > p {
margin-top: 0;
}
livesample.s1 {
padding-left: 16px;
letter-spacing: -0.01em;
font-size: 5em;
font-weight: 600;
line-height: 1.1;
margin-top: 0;
margin-bottom: 0.3em;
}
livesample.s2 {
max-width: 400px;
font-size: 1em;
}
livesample.s3 {
font-size: 13px;
line-height: 18px;
}
livesample.s3 b, livesample.s3 strong {
font-weight:500;
color: black;
}
livesample.col3 {
-moz-column-width: 20em;
-moz-columns: 20em;
-webkit-columns: 20em;
columns: 20em;
-moz-column-gap: 2em;
-webkit-column-gap: 2em;
column-gap: 2em;
}
livesample.col2 {
-moz-column-count: 2;
-webkit-column-count: 2;
column-count: 2;
}
.font-style-regular { font-weight:400 !important; font-style:normal !important; }
.font-style-italic { font-weight:400 !important; font-style:italic !important; }
.font-style-medium { font-weight:500 !important; font-style:normal !important; }
.font-style-medium-italic { font-weight:500 !important; font-style:italic !important; }
.font-style-bold { font-weight:700 !important; font-style:normal !important; }
.font-style-bold-italic { font-weight:700 !important; font-style:italic !important; }
.font-style-black { font-weight:900 !important; font-style:normal !important; }
.font-style-black-italic { font-weight:900 !important; font-style:italic !important; }
div.live {
margin-top:1em;
margin-bottom:100px;
padding-bottom:20px;
border-bottom: 1px solid #ddd;
}
div.live .learn-more {
margin-top:40px;
user-select: none;
}
div.controls {
position: absolute;
right: 0;
top: 150px;
width: 250px;
padding: 10px 0;
/*background:#eee;*/
opacity:0.3;
transition: 90ms opacity cubic-bezier(0.25, 0.47, 0.44, 0.93);
/*border:1px solid #bbb;*/
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
border-right:none;
display: flex;
flex-direction: column;
overflow: hidden;
font-size: 13px;
}
div.controls:hover {
opacity:1;
background:#f5f5f5;
border-color: transparent;
}
div.controls .control {
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
min-height: 30px;
margin: 0 16px;
}
div.controls .control > * {
flex: 1 1 auto;
margin:0;
margin-right: 16px;
box-sizing: border-box;
}
div.controls .control > :last-child {
margin-right: 0;
}
div.controls .control > select {
min-width: 6em;
align-items: center;
justify-content: center;
}
div.controls .control > input,
div.controls .control > select {
width: 0;
outline: none;
}
div.controls .control > input[type="number"],
div.controls .control > input[type="text"] {
background: none;
border: none;
padding: 4px 0;
font-size: inherit;
/*border-radius: 2px;*/
}
div.controls .control > input[type="number"] {
max-width: 48px;
text-align: right;
-moz-font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
-ms-font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
-o-font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
-webkit-font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
}
div.controls .control > input[type=number]::-webkit-inner-spin-button,
div.controls .control > input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
div.controls .control > input[type="range"] {
/*max-width: 80%;*/
flex: 1 1 auto;
display: block;
}
div.controls .control > img.icon,
div.controls .control > label {
font-family: georgia, serif;
font-style: italic;
color: black;
width: 16px;
height: 16px;
flex: 0 0 auto;
margin-right: 16px;
opacity: 0.6;
}
div.controls canvas {
height: 200px;
}
div.controls .control.info,
div.controls canvas {
transition: 390ms opacity cubic-bezier(0.25, 0.47, 0.44, 0.93);
opacity: 0;
}
div.controls:hover .control.info,
div.controls:hover canvas {
opacity: 1;
}
/* narrow windows */
@media only screen and (max-width: 1200px) {
div.controls {
width: 210px;
font-size: 12px;
}
}
@media only screen and (max-width: 1024px) {
div.controls {
width: 140px;
font-size: 11px;
}
div.controls canvas {
display: none;
}
div.controls .control.info {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
div.controls .control > input[type="range"] {
width: 0;
flex: 0 1 0%;
display: none;
}
div.controls .control > input[type="number"] {
max-width: 100%;
flex: 1 1 auto;
}
}
@media only screen and (max-width: 740px) {
livesample.s1 {
font-size:4.5em;
}
div.controls {
display: none;
}
}
@media only screen and (max-width: 565px) {
livesample.s1 {
font-size:4em;
}
}
@media only screen and (max-width: 414px) {
livesample.s1 {
font-size:3.4em;
}
}
.sample-images img, .sample-images svg {
margin: 100px 0;
}
.sample-images > img:first-child, .sample-images > svg:first-child {
margin-top:50px;
}

View file

@ -1,257 +1,31 @@
<!DOCTYPE HTML> ---
<html lang="en" prefix="og: http://ogp.me/ns#"> layout: default
<head> title: Samples
<meta charset="utf-8"> ---
<title>Inter UI font family</title> {%
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta property="og:title" content="Inter UI font family">
<meta property="twitter:title" content="Inter UI font family">
<meta property="description" content="Inter UI is a new typeface optimized for computer user interfaces">
<meta property="og:description" content="Inter UI is a new typeface optimized for computer user interfaces">
<meta property="twitter:description" content="Inter UI is a new typeface optimized for computer user interfaces">
<meta property="twitter:card" content="summary">
<meta property="twitter:site" content="@rsms">
<meta property="twitter:creator" content="@rsms">
<meta property="og:image" content="https://rsms.me/inter/res/poster.png">
<meta property="twitter:image" content="https://rsms.me/inter/res/poster.png">
<meta property="fb:app_id" content="38027689216">
<meta property="og:url" content="https://rsms.me/inter/">
<meta property="og:site_name" content="rsms.me">
<meta property="og:type" content="product">
<meta property="og:locale" content="en_US" />
<meta name="format-detection" content="telephone=no">
<link rel="icon" type="image/png" href="../favicon.png" />
<link href="../inter-ui.css?v=2.5" rel="stylesheet">
<link href="../index.css?v=5" rel="stylesheet">
<style type="text/css">
body {
padding-bottom: 0;
background: white;
}
.row.footer h2 { capture url_root
margin:0; %}{% if site.safe == false %}/{% else %}/inter/{% endif
text-align:center; %}{%
} endcapture %}{%
/*.row.menu { for file in site.static_files %}{%
background: white; assign _path = file.path | remove_first: "/inter" %}{%
}*/ if _path == "/samples/index.css" %}{%
assign index_css_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
elsif _path == "/res/bindings.js" %}{%
assign bindings_js_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
elsif _path == "/res/graphplot.js" %}{%
assign graphplot_js_v = file.modified_time | date: "%Y%m%d%H%M%S" %}{%
endif %}{%
endfor
livesample { %}
display: block; <link rel="stylesheet" href="index.css?v={{ index_css_v }}">
color: #111; <script src="{{url_root}}res/bindings.js?v={{ bindings_js_v }}"></script>
outline: none; <script src="{{url_root}}res/graphplot.js?v={{ graphplot_js_v }}"></script>
padding-left: 20px;
border-left: 2px solid transparent;
margin-left:-22px;
margin-top: 1em;
margin-bottom: 1.6em;
}
livesample:hover {
/*color: rgb(3, 102, 214);*/
border-left-color: rgb(3, 102, 214);
}
livesample:focus {
border-left-color: #eee;
}
livesample > p {
margin-top: 0;
}
livesample.s1 {
padding-left: 16px;
letter-spacing: -0.005em;
font-size: 5em;
font-weight: 600;
line-height: 1.1;
margin-top: 0;
margin-bottom: 0.3em;
}
livesample.s2 {
max-width: 400px;
font-size: 1em;
}
livesample.s3 {
font-size: 13px;
line-height: 18px;
}
livesample.s3 b, livesample.s3 strong {
font-weight:500;
color: black;
}
livesample.col3 { <div class="row"><div>
-moz-column-width: 20em;
-moz-columns: 20em;
-webkit-columns: 20em;
columns: 20em;
-moz-column-gap: 2em;
-webkit-column-gap: 2em;
column-gap: 2em;
}
livesample.col2 {
-moz-column-count: 2;
-webkit-column-count: 2;
column-count: 2;
}
.font-style-regular { font-weight:400 !important; font-style:normal !important; }
.font-style-italic { font-weight:400 !important; font-style:italic !important; }
.font-style-medium { font-weight:500 !important; font-style:normal !important; }
.font-style-medium-italic { font-weight:500 !important; font-style:italic !important; }
.font-style-bold { font-weight:700 !important; font-style:normal !important; }
.font-style-bold-italic { font-weight:700 !important; font-style:italic !important; }
.font-style-black { font-weight:900 !important; font-style:normal !important; }
.font-style-black-italic { font-weight:900 !important; font-style:italic !important; }
div.live {
margin-top:1em;
margin-bottom:100px;
padding-bottom:100px;
border-bottom: 1px solid #ddd;
}
div.controls {
position: absolute;
right: 0;
top: 150px;
width: 220px;
padding: 10px 16px;
/*background:#eee;*/
opacity:0.3;
border:1px solid #bbb;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
border-right:none;
display: flex;
flex-direction: column;
overflow: hidden;
}
div.controls:hover {
opacity:1;
background:#f5f5f5;
border-color: transparent;
}
div.controls .control {
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
height:30px;
}
div.controls .control > * {
/*max-width: 50%;*/
flex: 1 1 auto;
margin:0;
margin-right: 16px;
box-sizing: border-box;
}
div.controls .control > :last-child {
margin-right: 0;
}
div.controls .control > select {
min-width: 6em;
align-items: center;
justify-content: center;
}
div.controls .control > input,
div.controls .control > select {
width: 0;
outline: none;
}
div.controls .control > input[type="number"],
div.controls .control > input[type="text"] {
background: none;
border: none;
padding: 4px 0;
font-size: 13px;
/*border-radius: 2px;*/
}
div.controls .control > input[type="number"] {
max-width: 60px;
text-align: right;
-moz-font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
-ms-font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
-o-font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
-webkit-font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
font-feature-settings: 'calt' 1, 'zero' 1, 'tnum' 1;
}
div.controls .control > input[type="range"] {
/*max-width: 80%;*/
flex: 1 1 auto;
display: block;
}
div.controls .control > img.icon {
width: 16px;
height: 16px;
flex: 0 0 auto;
margin-right: 16px;
opacity: 0.8;
}
/*div.controls .control > input.foo {
background-color: hotpink;
border:none;
border-radius: 90px;
}*/
/* narrow windows */
@media only screen and (max-width: 1200px) {
div.live div.controls {
width: 200px;
}
}
@media only screen and (max-width: 1024px) {
div.live div.controls {
width: 100px;
}
div.controls .control > input[type="range"] {
width: 0;
flex: 0 1 0%;
display: none;
}
div.controls .control > input[type="number"] {
max-width: 100%;
flex: 1 1 auto;
}
}
@media only screen and (max-width: 740px) {
livesample.s1 {
font-size:4.5em;
}
div.live div.controls {
display: none;
}
}
@media only screen and (max-width: 565px) {
livesample.s1 {
font-size:4em;
}
}
@media only screen and (max-width: 414px) {
livesample.s1 {
font-size:3.4em;
}
}
</style>
</head>
<body>
<div class="row menu">
<ul class="menu">
<li class="home"><a href="../">Inter UI</a></li>
<li><a class="download-link"
href="https://github.com/rsms/inter/releases/latest/"
>Download</a></li>
<li><a href="/inter/samples/" class="active">Samples</a></li>
<li><a href="../lab/">Playground</a></li>
<li><a href="https://github.com/rsms/inter/">Source</a></li>
</ul>
</div>
<div class="row white"><div>
<div class="live"> <div class="live">
<div class="controls"> <div class="controls">
@ -278,11 +52,10 @@ div.live {
<input type="range" min="-0.05" max="0.06" step="0.001" data-binding="letter-spacing"> <input type="range" min="-0.05" max="0.06" step="0.001" data-binding="letter-spacing">
<input type="number" min="-0.15" max="1" step="0.001" data-binding="letter-spacing"> <input type="number" min="-0.15" max="1" step="0.001" data-binding="letter-spacing">
</div> </div>
<!-- <div class="control"> <canvas class="graphplot">Canvas not Supported</canvas>
<img class="icon" src="icons/font-size.svg"> <div class="control info">
<input type="text" class="foo" data-binding="foo"> <a href="{{url_root}}dynmetrics/">Learn about Dynamic Metrics</a>
<input type="text" class="foo" data-binding="foo"> </div>
</div> -->
</div> </div>
<livesample contenteditable class="s1"> <livesample contenteditable class="s1">
@ -350,23 +123,12 @@ When the tide is on the right moon and the wind has blown a gale from
the southeast, the strand is entirely submerged, and people upon the the southeast, the strand is entirely submerged, and people upon the
main shore three miles away can see the surf breaking over the Beach main shore three miles away can see the surf breaking over the Beach
hills. hills.
</p><p>
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 weeks work in a single day by what he has
found while walking along the Beach when the surf was down.
</p><p>
“The Captain” knew all this and had patrolled that Beach scores of
times.
</p><p>
Ten years had passed since the first time which laid the habit of
wandering along the surf-shore apparently in search of whatever the sea
had cast up. Sometimes a spar, sometimes sheets of copper torn from a
wreck and carried by a high surf far along the strand, sometimes a
vessels gilded name, at other times only scattered drift-wood were the
rewards of these lonely walks.
</p> </p>
</livesample> </livesample>
<p class="learn-more">
&nbsp;
<!-- <a href="{{url_root}}dynmetrics/">Learn about Dynamic Metrics</a> -->
</p>
</div> </div>
<p class="sample-images"> <p class="sample-images">
@ -389,50 +151,39 @@ rewards of these lonely walks.
<img src="img/15.png" srcset="img/15@2x.png 2x" width="888"> <img src="img/15.png" srcset="img/15@2x.png 2x" width="888">
</p> </p>
<p style="text-align:center"> <p style="text-align:center">
<a href="https://www.figma.com/file/WmU5NWr52bnUcqv5os0V4sWi/" class="plain">Open these samples in Figma</a> <a
href="https://www.figma.com/file/WmU5NWr52bnUcqv5os0V4sWi/"
class="plain">Open these samples in Figma</a>
</p> </p>
</div></div> </div></div>
<script src="../index.js?v=2"></script>
<script src="bindings.js"></script>
<script type="text/javascript">(function(){ <script type="text/javascript">(function(){
// InterUIDynamicTracking takes the font size in points or pixels and returns // InterUIDynamicTracking produces tracking in EM for the given font size
// the compensating tracking in EM.
// //
function InterUIDynamicTracking(fontSize, weightClass) { function InterUIDynamicTracking(fontSize, weightClass) {
// if (!weightClass) { -- currently unused // if (!weightClass) { -- currently unused
// weightClass = 400 // weightClass = 400
// } // }
// //
// y = -0.01021241 + 0.3720623 * e ^ (-0.2808687 * x) var a = -0.0149, b = 0.298, c = -0.23;
// [6-35] 0.05877 .. -0.0101309 (13==0; stops growing around 30-35) return a + b * Math.pow(Math.E, (c * fontSize))
// See https://gist.github.com/rsms/8efdbca5f8145a584ed08a7c3d6e5788
//
return -0.01021241 + 0.3720623 * Math.pow(Math.E, (-0.2808687 * fontSize))
// y = 0.025 - (ln(x) * 0.01)
// return 0.025 - Math.log(fontSize) * 0.01
} }
// InterUIDynamicLineHeight produces the line height for the given font size
function InterUIDynamicLeading(fontSize, weightClass) {
var lineHeight = Math.round(fontSize * 1.4)
// //
// TODO function InterUIDynamicLineHeight(fontSize, weightClass) {
// var l = 1.4
// console.log( return Math.round(fontSize * l)
// 'lineHeight:', lineHeight,
// `(${Math.round(fontSize * 1.45)})`,
// )
return lineHeight
} }
var bindings = new Bindings() var bindings = new Bindings()
var graph = new GraphPlot($('canvas.graphplot'))
graph.setOrigin(-0.2, 0.8)
graph.setScale(0.049, 5)
var s2 = $('livesample.s2') var s2 = $('livesample.s2')
@ -443,22 +194,39 @@ function updateWidth() {
s2.style.maxWidth = Math.round(w) + 'px' s2.style.maxWidth = Math.round(w) + 'px'
} }
function updateTracking() {
var fontSize = bindings.value('font-size', 0)
var letterSpacing = InterUIDynamicTracking(fontSize, 400)
bindings.setValue('letter-spacing', letterSpacing)
}
function updateGraph() {
graph.clear()
var fontSize = bindings.value('font-size', 0)
var letterSpacing = bindings.value('letter-spacing')
graph.plotf(function(x) {
return InterUIDynamicTracking(x, 400)
})
var graphedFontSize = Math.min(24, fontSize) // clamp to [-inf,24]
graph.plotPoints([[graphedFontSize, letterSpacing]], '#000')
}
bindings.configure('letter-spacing', 0, 'float', function (letterSpacing) { bindings.configure('letter-spacing', 0, 'float', function (letterSpacing) {
s2.style.letterSpacing = letterSpacing + 'em' s2.style.letterSpacing = letterSpacing + 'em'
updateWidth() updateWidth()
updateGraph()
})
bindings.setFormatter('letter-spacing', function(v) {
return v.toFixed(3)
}) })
bindings.configure('font-size', 18, 'int', function(fontSize, prevval) { bindings.configure('font-size', 16, 'float', function(fontSize, prevval) {
s2.style.fontSize = fontSize + 'px' s2.style.fontSize = fontSize + 'px'
updateWidth() updateWidth()
updateTracking()
var letterSpacing = InterUIDynamicTracking(fontSize, 400) var lineHeight = InterUIDynamicLineHeight(fontSize, 400)
letterSpacing = parseFloat(letterSpacing.toFixed(3)) // lower precision
var lineHeight = InterUIDynamicLeading(fontSize, 400)
s2.style.lineHeight = lineHeight + 'px' s2.style.lineHeight = lineHeight + 'px'
bindings.setValue('letter-spacing', letterSpacing)
}) })
bindings.configure('style', null, null, function (style) { bindings.configure('style', null, null, function (style) {
@ -478,8 +246,14 @@ bindings.configure('style', null, null, function (style) {
bindings.bindAllInputs('div.live .control input') bindings.bindAllInputs('div.live .control input')
bindings.bindAllInputs('div.live .control select') bindings.bindAllInputs('div.live .control select')
// resize canvas when window resizes
var resizeDebounceTimer = null
window.addEventListener('resize', function(){
clearTimeout(resizeDebounceTimer)
resizeDebounceTimer = setTimeout(function(){
graph.autosize()
updateGraph()
}, 500)
}, {passive:true, capture:false})
})();</script> })();</script>
</body>
</html>

View file

@ -1,24 +0,0 @@
#!/bin/sh
set -e
cd "$(dirname "$0")"
if [ ! -s lab/fonts ]; then
ln -s ../../build/dist lab/fonts
fi
if (which caddy >/dev/null); then
caddy_args=(\
-host localhost \
"bind localhost" \
"mime .woff2 font/woff2" \
"mime .woff application/font-woff" \
)
caddy "${caddy_args[@]}"
elif (which servedir >/dev/null); then
servedir
else
echo "Can not find 'caddy' nor 'servedir' in PATH." >&2
echo "Install caddy from brew, apt or https://caddyserver.com/download"
echo "or install servedir with 'npm install -g secure-servedir'"
exit 1
fi

View file

@ -7,6 +7,7 @@ import os
import sys import sys
import argparse import argparse
import json import json
import re
from base64 import b64encode from base64 import b64encode
from fontTools import ttLib from fontTools import ttLib
@ -288,6 +289,13 @@ def genFontInfo(fontpath, outputType, withGlyphs=True):
if 'subfamilyName' in nameDict: if 'subfamilyName' in nameDict:
info['name'] += '-' + nameDict['subfamilyName'].replace(' ', '') info['name'] += '-' + nameDict['subfamilyName'].replace(' ', '')
if 'version' in nameDict:
version = nameDict['version']
v = re.split(r'[\s;]+', version)
if v and len(v) > 0:
version = v[0]
info['version'] = version
if outputType is not OUTPUT_TYPE_GLYPHLIST: if outputType is not OUTPUT_TYPE_GLYPHLIST:
if len(nameDict): if len(nameDict):
info['names'] = nameDict info['names'] = nameDict
@ -425,7 +433,7 @@ def main():
args = argparser.parse_args() args = argparser.parse_args()
fonts = {} fonts = []
outputType = OUTPUT_TYPE_COMPLETE outputType = OUTPUT_TYPE_COMPLETE
if args.asGlyphList: if args.asGlyphList:
outputType = OUTPUT_TYPE_GLYPHLIST outputType = OUTPUT_TYPE_GLYPHLIST
@ -437,7 +445,7 @@ def main():
# internal cache that mixes up values for different fonts. # internal cache that mixes up values for different fonts.
reload(sstruct) reload(sstruct)
font = genFontInfo(fontpath, outputType=outputType, withGlyphs=args.withGlyphs) font = genFontInfo(fontpath, outputType=outputType, withGlyphs=args.withGlyphs)
fonts[font['id']] = font fonts.append(font)
n += 1 n += 1
ostream = sys.stdout ostream = sys.stdout
@ -447,6 +455,7 @@ def main():
if args.prettyJson: if args.prettyJson:
json.dump(fonts, ostream, sort_keys=True, indent=2, separators=(',', ': ')) json.dump(fonts, ostream, sort_keys=True, indent=2, separators=(',', ': '))
sys.stdout.write('\n')
else: else:
json.dump(fonts, ostream, separators=(',', ':')) json.dump(fonts, ostream, separators=(',', ':'))