first commit
This commit is contained in:
commit
51d57259b0
4 changed files with 360 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.make.*
|
62
index.html
Normal file
62
index.html
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<title>Fontsource font preview</title>
|
||||||
|
<script type="module" src="script.js"></script>
|
||||||
|
<style id="font-style"></style>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>Fontsource font preview</h1>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<section id="intro" class="fixed-font">
|
||||||
|
<p>This page shows fonts loaded from <a href="https://fontsource.org">Fontsource</a>.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="controls" class="fixed-font">
|
||||||
|
<label for="font-family">Font:</label>
|
||||||
|
<select id="font-family">
|
||||||
|
</select>
|
||||||
|
<button type="button" id="random-font">I'm feeling lucky</button>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="info">
|
||||||
|
<h2>About this font</h2>
|
||||||
|
|
||||||
|
<p><a id="font-link" target="fontsource"></a></p>
|
||||||
|
|
||||||
|
<section id="variable">
|
||||||
|
<h3>Variable axes</h3>
|
||||||
|
|
||||||
|
<ul id="variable-axes">
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt>Category</dt>
|
||||||
|
<dd id="category"></dd>
|
||||||
|
|
||||||
|
<dt>Styles</dt>
|
||||||
|
<dd><ul id="styles"></ul></dd>
|
||||||
|
|
||||||
|
<dt>Weights</dt>
|
||||||
|
<dd><ul id="weights"></ul></dd>
|
||||||
|
|
||||||
|
<dt>Unicode ranges</dt>
|
||||||
|
<dd><ul id="unicode-ranges"></ul></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="code">
|
||||||
|
<h2>CSS</h2>
|
||||||
|
<pre id="css-display"></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
265
script.js
Normal file
265
script.js
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
const select = document.getElementById('font-family');
|
||||||
|
const link = document.getElementById('font-link');
|
||||||
|
|
||||||
|
const font_info_url = id => `https://api.fontsource.org/v1/fonts/${id}`;
|
||||||
|
|
||||||
|
const variable_info_url = id => `https://api.fontsource.org/v1/variable/${id}`;
|
||||||
|
|
||||||
|
const font_page_url = id => `https://fontsource.org/fonts/${id}`;
|
||||||
|
|
||||||
|
function chars_in_range(v) {
|
||||||
|
if(!v) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const ranges = v.split(',');
|
||||||
|
return ranges.map(r => {
|
||||||
|
let [_,start,end] = r.match(/U\+([A-Z0-9]+)(?:-([A-Z0-9]+))?/i);
|
||||||
|
start = parseInt(start,16);
|
||||||
|
end = parseInt(end,16) || start;
|
||||||
|
let c = '';
|
||||||
|
for(let i = start; i<=end; i++) {
|
||||||
|
c += String.fromCharCode(i);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function element(name, attr, content) {
|
||||||
|
const el = document.createElement(name);
|
||||||
|
if(attr) {
|
||||||
|
Object.entries(attr).forEach(([k,v]) => el.setAttribute(k,v));
|
||||||
|
}
|
||||||
|
if(content !== undefined) {
|
||||||
|
el.innerHTML = content;
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
const css_template = (info,range,weight,style,variable_info) => {
|
||||||
|
|
||||||
|
const axes = Object.keys(variable_info.axes || {}).filter(a=>a!='ital');
|
||||||
|
function get_axis() {
|
||||||
|
if (axes.length === 1 && axes[0]=='wght') {
|
||||||
|
return 'wght';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (axes.length === 2 && axes.includes('wght')) {
|
||||||
|
const selected =
|
||||||
|
axes.find((axis) => axis !== 'wght')?.toLowerCase() ?? 'wght';
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
const isStandard = axes.every((axis) =>
|
||||||
|
['wght', 'wdth', 'slnt', 'opsz'].includes(axis),
|
||||||
|
);
|
||||||
|
|
||||||
|
return isStandard ? 'standard' : 'full';
|
||||||
|
}
|
||||||
|
|
||||||
|
const axis = get_axis();
|
||||||
|
|
||||||
|
let urls;
|
||||||
|
|
||||||
|
if(info.variable) {
|
||||||
|
urls = [`url(https://cdn.jsdelivr.net/fontsource/fonts/${info.id}:vf@latest/${range}-${axis}-${style}.woff2) format('woff2-variations')`];
|
||||||
|
} else {
|
||||||
|
const variant = info.variants[weight][style][range];
|
||||||
|
urls = Object.entries(variant.url).map(([format,url]) => {
|
||||||
|
return `url(${url}) format('${format}')`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const extras = [];
|
||||||
|
if(info.variable) {
|
||||||
|
if(axes.includes('wght')) {
|
||||||
|
extras.push(` font-weight: ${variable_info.axes.wght.min} ${variable_info.axes.wght.max};`);
|
||||||
|
}
|
||||||
|
if(axes.includes('wdth')) {
|
||||||
|
extras.push(` font-stretch: ${variable_info.axes.wdth.min} ${variable_info.axes.wdth.max};`);
|
||||||
|
}
|
||||||
|
if(axes.includes('slnt')) {
|
||||||
|
extras.push(` font-style: oblique ${variable_info.axes.slnt.min}deg ${variable_info.axes.slnt.max}deg;`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(info.variable) {
|
||||||
|
return `
|
||||||
|
/* ${info.family} variable ${style} ${range} */
|
||||||
|
@font-face ${'{'}
|
||||||
|
font-family: '${info.family}';
|
||||||
|
font-style: ${style};
|
||||||
|
font-display: swap;
|
||||||
|
${extras.join('\n')}
|
||||||
|
src: ${urls.join(', ')};
|
||||||
|
unicode-range: ${info.unicodeRange[range]};
|
||||||
|
${'}'}
|
||||||
|
`.trim();
|
||||||
|
} else {
|
||||||
|
return `
|
||||||
|
/* ${info.family} ${weight} ${style} ${range} */
|
||||||
|
@font-face ${'{'}
|
||||||
|
font-family: '${info.family}';
|
||||||
|
font-style: ${style};
|
||||||
|
font-weight: ${weight};
|
||||||
|
font-display: swap;
|
||||||
|
src: ${urls.join(', ')};
|
||||||
|
unicode-range: ${info.unicodeRange[range]};
|
||||||
|
${'}'}
|
||||||
|
`.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetch_json(url) {
|
||||||
|
return await (await fetch(url)).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function go() {
|
||||||
|
let [fonts, axes_info] = await Promise.all([
|
||||||
|
fetch_json('https://api.fontsource.org/v1/fonts'),
|
||||||
|
fetch_json('https://api.fontsource.org/v1/axis-registry')
|
||||||
|
]);
|
||||||
|
|
||||||
|
axes_info = Object.fromEntries(Object.entries(axes_info).map(([k,v]) => [k.toLowerCase(), v]));
|
||||||
|
|
||||||
|
async function use_font() {
|
||||||
|
const name = select.value;
|
||||||
|
console.log(name);
|
||||||
|
const info = await fetch_json(font_info_url(name));
|
||||||
|
const variable_info = info.variable ? await fetch_json(variable_info_url(info.id)) : {};
|
||||||
|
console.log(info);
|
||||||
|
|
||||||
|
document.body.style.setProperty('--family',info.family);
|
||||||
|
|
||||||
|
function show_list(id, list, style_fn, text_fn) {
|
||||||
|
text_fn = text_fn || (v => v);
|
||||||
|
const ul = document.getElementById(id);
|
||||||
|
ul.innerHTML = '';
|
||||||
|
for(let v of list) {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
ul.append(li);
|
||||||
|
li.innerHTML = text_fn(v);
|
||||||
|
style_fn(li.style, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show_list('styles',info.styles, (style,v) => { style['font-style'] = v });
|
||||||
|
show_list('weights',info.weights, (style,v) => { style['font-weight'] = v; style['font-variation-settings'] = `"wght" ${v}`; });
|
||||||
|
show_list('unicode-ranges',info.subsets, () => {}, v => `${v}: <p class="char-range">${chars_in_range(info.unicodeRange[v])}</p>`);
|
||||||
|
|
||||||
|
document.getElementById('category').textContent = info.category;
|
||||||
|
|
||||||
|
document.getElementById('variable').classList.toggle('hidden',!info.variable);
|
||||||
|
if(info.variable) {
|
||||||
|
const variable_axes = document.getElementById('variable-axes');
|
||||||
|
variable_axes.innerHTML = '';
|
||||||
|
console.log(variable_info);
|
||||||
|
|
||||||
|
const ranges = {};
|
||||||
|
|
||||||
|
Object.entries(variable_info.axes).filter(([axis,d]) => !['opsz','ital'].includes(axis.toLowerCase())).forEach(([axis,d]) => {
|
||||||
|
const axis_info = axes_info[axis.toLowerCase()];
|
||||||
|
const li = document.createElement('li');
|
||||||
|
const id = `axis-${axis}`;
|
||||||
|
variable_axes.append(li);
|
||||||
|
const label = element('label',{for: id}, axis_info.name);
|
||||||
|
li.append(label);
|
||||||
|
const range = element(
|
||||||
|
'input',
|
||||||
|
{
|
||||||
|
type: 'range',
|
||||||
|
id: id,
|
||||||
|
min: d.min,
|
||||||
|
max: d.max,
|
||||||
|
step: d.step,
|
||||||
|
value: d.default,
|
||||||
|
list: `axis-list-${axis}`
|
||||||
|
}
|
||||||
|
);
|
||||||
|
li.append(range);
|
||||||
|
ranges[axis] = range;
|
||||||
|
|
||||||
|
const datalist = element('datalist',{id: `axis-list-${axis}`})
|
||||||
|
li.append(datalist);
|
||||||
|
const default_option = element('option',{value:d.default});
|
||||||
|
datalist.append(default_option);
|
||||||
|
|
||||||
|
const output = element(
|
||||||
|
'output',
|
||||||
|
{
|
||||||
|
for: id
|
||||||
|
},
|
||||||
|
d.default
|
||||||
|
);
|
||||||
|
li.append(output);
|
||||||
|
|
||||||
|
const desc = element('p',{class:'help-text'},axis_info.description);
|
||||||
|
li.append(desc);
|
||||||
|
|
||||||
|
range.addEventListener('input', e => {
|
||||||
|
output.textContent = range.value;
|
||||||
|
update_variables();
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
function update_variables() {
|
||||||
|
const settings = Object.entries(ranges).map(([axis,range]) => `"${axis}" ${range.value}`).join(', ');
|
||||||
|
document.body.style['font-variation-settings'] = settings;
|
||||||
|
}
|
||||||
|
update_variables();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const css_declarations = [];
|
||||||
|
if(info.variable) {
|
||||||
|
for(let style of info.styles) {
|
||||||
|
for(let range of info.subsets) {
|
||||||
|
const css = css_template(info, range, '', style, variable_info);
|
||||||
|
css_declarations.push(css);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for(let style of info.styles) {
|
||||||
|
for(let weight of info.weights) {
|
||||||
|
for(let range of info.subsets) {
|
||||||
|
const css = css_template(info, range, weight, style, variable_info);
|
||||||
|
css_declarations.push(css);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const css = css_declarations.join('\n\n');
|
||||||
|
document.getElementById('font-style').textContent = css;
|
||||||
|
document.getElementById('css-display').textContent = css;
|
||||||
|
|
||||||
|
link.href = font_page_url(info.id);
|
||||||
|
link.textContent = info.family;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fonts = fonts.filter(f=>!f.id.includes('noto'));
|
||||||
|
select.innerHTML = '';
|
||||||
|
for(let font of fonts) {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
select.append(option);
|
||||||
|
option.textContent = font.family;
|
||||||
|
option.value = font.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
select.addEventListener('change', use_font);
|
||||||
|
|
||||||
|
function random_font() {
|
||||||
|
const font = fonts[Math.floor(Math.random()*fonts.length)];
|
||||||
|
select.value = font.id;
|
||||||
|
use_font();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('random-font').addEventListener('click', random_font);
|
||||||
|
|
||||||
|
random_font();
|
||||||
|
}
|
||||||
|
|
||||||
|
go();
|
32
style.css
Normal file
32
style.css
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
:root {
|
||||||
|
--spacing: 1em;
|
||||||
|
--family: sans-serif;
|
||||||
|
|
||||||
|
color-scheme: light dark;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: var(--family);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fixed-font {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-text {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.char-range {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 3em;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue