@@ -99,7 +99,10 @@ const VIEWER_HTML = `<!doctype html>
9999 <div class="nav-label">Tokens</div>
100100 <ul id="nav"></ul>
101101 <div class="nav-label nav-label-spaced">Playground</div>
102- <ul><li><a href="#tool-contrast" data-cat="tool-contrast">Contrast</a></li></ul>
102+ <ul>
103+ <li><a href="#tool-contrast" data-cat="tool-contrast">Contrast</a></li>
104+ <li><a href="#tool-typography" data-cat="tool-typography">Typography</a></li>
105+ </ul>
103106 </nav>
104107 </aside>
105108 <main class="content">
@@ -193,6 +196,10 @@ section h2 { font-size: 12px; font-weight: 600; text-transform: uppercase; lette
193196.badge { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; padding: 5px 10px; border-radius: 999px; border: 1px solid var(--border); }
194197.badge.pass { background: color-mix(in srgb, #16a34a 20%, transparent); border-color: #16a34a; }
195198.badge.fail { background: color-mix(in srgb, #dc2626 15%, transparent); border-color: #dc2626; opacity: 0.7; }
199+ .type-play { display: flex; flex-direction: column; gap: 12px; }
200+ .type-play-preview { display: flex; align-items: center; border: 1px solid var(--border); border-radius: 12px; background: var(--card); padding: 32px; min-height: 200px; overflow-wrap: anywhere; }
201+ .type-play-css { border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; font-family: ui-monospace, monospace; font-size: 12px; color: var(--muted); line-height: 1.7; white-space: pre; overflow-x: auto; }
202+ .tool-controls textarea { resize: vertical; min-height: 64px; }
196203`
197204
198205const VIEWER_JS = `const root = document.documentElement
@@ -547,9 +554,70 @@ function renderContrast(container, colors) {
547554 update()
548555}
549556
557+ function optionsFor(tokens, category) {
558+ return tokens
559+ .filter((token) => token.category === category)
560+ .map((token) => '<option value="' + token.value + '">' + token.name + ' (' + token.value + ')</option>')
561+ .join('')
562+ }
563+
564+ function renderTypographyPlayground(container, tokens) {
565+ const fields = [
566+ { prop: 'font-size', category: 'fontSizes', label: 'Font size' },
567+ { prop: 'font-weight', category: 'fontWeights', label: 'Font weight' },
568+ { prop: 'font-family', category: 'fonts', label: 'Font family' },
569+ { prop: 'line-height', category: 'lineHeights', label: 'Line height' },
570+ { prop: 'letter-spacing', category: 'letterSpacings', label: 'Letter spacing' },
571+ ].filter((field) => tokens.some((token) => token.category === field.category))
572+ if (fields.length === 0) return
573+
574+ const section = document.createElement('section')
575+ section.id = 'tool-typography'
576+ section.dataset.cat = 'tool-typography'
577+ const heading = document.createElement('h2')
578+ heading.textContent = 'typography'
579+
580+ const tool = document.createElement('div')
581+ tool.className = 'tool'
582+
583+ const controls = document.createElement('div')
584+ controls.className = 'tool-controls'
585+ controls.innerHTML =
586+ fields
587+ .map(
588+ (field) =>
589+ '<label>' + field.label + '<select data-prop="' + field.prop + '">' + optionsFor(tokens, field.category) + '</select></label>',
590+ )
591+ .join('') + '<label>Sample text<textarea id="type-play-text">The quick brown fox jumps over the lazy dog</textarea></label>'
592+
593+ const output = document.createElement('div')
594+ output.className = 'type-play'
595+ const preview = document.createElement('div')
596+ preview.className = 'type-play-preview'
597+ const cssOut = document.createElement('div')
598+ cssOut.className = 'type-play-css'
599+ output.append(preview, cssOut)
600+
601+ tool.append(controls, output)
602+ section.append(heading, tool)
603+ container.appendChild(section)
604+
605+ const selects = [...controls.querySelectorAll('select')]
606+ const text = controls.querySelector('#type-play-text')
607+ function update() {
608+ for (const select of selects) preview.style.setProperty(select.dataset.prop, select.value)
609+ preview.textContent = text.value
610+ cssOut.textContent = selects.map((select) => select.dataset.prop + ': ' + select.value + ';').join('\\n')
611+ }
612+ for (const select of selects) select.addEventListener('change', update)
613+ text.addEventListener('input', update)
614+ update()
615+ }
616+
550617function renderTools(tokens) {
551618 const tools = document.getElementById('tools')
552619 renderContrast(tools, tokens.filter((token) => token.category === 'colors'))
620+ renderTypographyPlayground(tools, tokens)
553621 updateActiveNav()
554622}
555623
0 commit comments