129 lines
4.8 KiB
JavaScript
129 lines
4.8 KiB
JavaScript
// Demo page behaviour. Not a build input — see src/demo/demo.scss.
|
|||
|
|
import '../sass/main.scss'
|
||
|
|
import './demo.scss'
|
||
|
|
|
||
|
|
// -- Colour swatches ---------------------------------------------------------
|
||
|
|
//
|
||
|
|
// Read from the emitted custom properties rather than from a hand-kept list, so
|
||
|
|
// the demo cannot drift from the stylesheet: delete a token and its swatch
|
||
|
|
// disappears on reload.
|
||
|
|
|
||
|
|
function emittedTokens (group) {
|
||
|
|
const names = new Set()
|
||
|
|
for (const sheet of document.styleSheets) {
|
||
|
|
let rules
|
||
|
|
try { rules = sheet.cssRules } catch { continue } // cross-origin
|
||
|
|
for (const rule of rules) {
|
||
|
|
if (rule.selectorText !== ':root') continue
|
||
|
|
for (const prop of rule.style) {
|
||
|
|
if (prop.startsWith(`--tlig-${group}-`)) names.add(prop)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return [...names]
|
||
|
|
}
|
||
|
|
|
||
|
|
function renderSwatches () {
|
||
|
|
const root = getComputedStyle(document.documentElement)
|
||
|
|
const html = emittedTokens('color').map((name) => {
|
||
|
|
const value = root.getPropertyValue(name).trim()
|
||
|
|
const short = name.replace('--tlig-color-', '')
|
||
|
|
return `<figure class="swatch">
|
||
|
|
<div class="swatch__chip"><div class="swatch__fill" style="background:${value}"></div></div>
|
||
|
|
<figcaption class="swatch__meta">
|
||
|
|
<div class="swatch__name">${short}</div>
|
||
|
|
<div class="swatch__value">${value}</div>
|
||
|
|
</figcaption>
|
||
|
|
</figure>`
|
||
|
|
}).join('')
|
||
|
|
document.querySelector('#swatches').innerHTML = html
|
||
|
|
document.querySelector('#swatch-count').textContent = emittedTokens('color').length
|
||
|
|
}
|
||
|
|
|
||
|
|
// -- Script switcher ---------------------------------------------------------
|
||
|
|
//
|
||
|
|
// The point of this control: all four papyrus files share one family name and
|
||
|
|
// are separated by unicode-range, so switching scripts changes no CSS at all.
|
||
|
|
// The browser picks the font file by codepoint.
|
||
|
|
|
||
|
|
const SAMPLES = {
|
||
|
|
en: {
|
||
|
|
label: 'English',
|
||
|
|
heading: 'True Life in God',
|
||
|
|
line: 'Peace be with you — I am the Alpha and the Omega.',
|
||
|
|
note: 'Latin — PapyrusLETBold, the original file.',
|
||
|
|
},
|
||
|
|
hu: {
|
||
|
|
// The pangram, not the site's own title: "Igaz Élet Istenben" contains no
|
||
|
|
// codepoint above U+00FF, so it would be served by the Latin file and
|
||
|
|
// would demonstrate nothing. ő (U+0151) and ű (U+0171) are the test.
|
||
|
|
label: 'Magyar',
|
||
|
|
heading: 'Árvíztűrő tükörfúrógép',
|
||
|
|
line: 'Igaz Élet Istenben — ő and ű are what the Latin-Extended cut is for.',
|
||
|
|
note: 'Latin Extended-A — the hu file. The original PapyrusLETBold has no ő or ű.',
|
||
|
|
},
|
||
|
|
bg: {
|
||
|
|
label: 'Български',
|
||
|
|
heading: 'Истински живот в Бога',
|
||
|
|
line: 'Мир на вас — Аз съм Алфа и Омега.',
|
||
|
|
note: 'Cyrillic — the cyrillic and bulgarian cuts.',
|
||
|
|
},
|
||
|
|
ru: {
|
||
|
|
label: 'Русский',
|
||
|
|
heading: 'Истинная жизнь в Боге',
|
||
|
|
line: 'Мир вам — Я есмь Альфа и Омега.',
|
||
|
|
note: 'Cyrillic — same file as Bulgarian, no extra rule.',
|
||
|
|
},
|
||
|
|
el: {
|
||
|
|
label: 'Ελληνικά',
|
||
|
|
heading: 'Αληθινή εν Θεώ Ζωή',
|
||
|
|
line: 'Ειρήνη υμίν — Εγώ ειμι το Άλφα και το Ωμέγα.',
|
||
|
|
note: 'No papyrus cut exists for Greek — this is the fallback face, shown honestly.',
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
function setScript (code) {
|
||
|
|
const sample = SAMPLES[code]
|
||
|
|
if (!sample) return
|
||
|
|
document.querySelector('#script-heading').textContent = sample.heading
|
||
|
|
document.querySelector('#script-line').textContent = sample.line
|
||
|
|
document.querySelector('#script-note').textContent = sample.note
|
||
|
|
document.querySelector('#script-heading').lang = code
|
||
|
|
for (const button of document.querySelectorAll('[data-script]')) {
|
||
|
|
button.setAttribute('aria-pressed', String(button.dataset.script === code))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// -- Breakpoint readout ------------------------------------------------------
|
||
|
|
|
||
|
|
function trackViewport () {
|
||
|
|
const output = document.querySelector('#viewport')
|
||
|
|
const update = () => {
|
||
|
|
const width = window.innerWidth
|
||
|
|
const name = width >= 1200 ? 'wide' : width >= 800 ? 'tablet' : 'phone'
|
||
|
|
output.textContent = `${width}px · ${name}`
|
||
|
|
}
|
||
|
|
window.addEventListener('resize', update, { passive: true })
|
||
|
|
update()
|
||
|
|
}
|
||
|
|
|
||
|
|
// -- Wire up -----------------------------------------------------------------
|
||
|
|
|
||
|
|
renderSwatches()
|
||
|
|
trackViewport()
|
||
|
|
|
||
|
|
// `?script=bg` opens straight onto a given script, so a particular cut can be
|
||
|
|
// linked to or screenshotted without clicking.
|
||
|
|
const requested = new URLSearchParams(location.search).get('script')
|
||
|
|
setScript(requested in SAMPLES ? requested : 'en')
|
||
|
|
|
||
|
|
for (const button of document.querySelectorAll('[data-script]')) {
|
||
|
|
button.addEventListener('click', () => setScript(button.dataset.script))
|
||
|
|
}
|
||
|
|
|
||
|
|
const boxes = document.querySelector('#toggle-boxes')
|
||
|
|
boxes.addEventListener('click', () => {
|
||
|
|
const on = document.body.classList.toggle('show-boxes')
|
||
|
|
boxes.setAttribute('aria-pressed', String(on))
|
||
|
|
})
|