@@ -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
@@ -549,9 +556,70 @@ function renderContrast(container, colors) {
549556 update()
550557}
551558
559+ function optionsFor(tokens, category) {
560+ return tokens
561+ .filter((token) => token.category === category)
562+ .map((token) => '<option value="' + token.value + '">' + token.name + ' (' + token.value + ')</option>')
563+ .join('')
564+ }
565+
566+ function renderTypographyPlayground(container, tokens) {
567+ const fields = [
568+ { prop: 'font-size', category: 'fontSizes', label: 'Font size' },
569+ { prop: 'font-weight', category: 'fontWeights', label: 'Font weight' },
570+ { prop: 'font-family', category: 'fonts', label: 'Font family' },
571+ { prop: 'line-height', category: 'lineHeights', label: 'Line height' },
572+ { prop: 'letter-spacing', category: 'letterSpacings', label: 'Letter spacing' },
573+ ].filter((field) => tokens.some((token) => token.category === field.category))
574+ if (fields.length === 0) return
575+
576+ const section = document.createElement('section')
577+ section.id = 'tool-typography'
578+ section.dataset.cat = 'tool-typography'
579+ const heading = document.createElement('h2')
580+ heading.textContent = 'typography'
581+
582+ const tool = document.createElement('div')
583+ tool.className = 'tool'
584+
585+ const controls = document.createElement('div')
586+ controls.className = 'tool-controls'
587+ controls.innerHTML =
588+ fields
589+ .map(
590+ (field) =>
591+ '<label>' + field.label + '<select data-prop="' + field.prop + '">' + optionsFor(tokens, field.category) + '</select></label>',
592+ )
593+ .join('') + '<label>Sample text<textarea id="type-play-text">The quick brown fox jumps over the lazy dog</textarea></label>'
594+
595+ const output = document.createElement('div')
596+ output.className = 'type-play'
597+ const preview = document.createElement('div')
598+ preview.className = 'type-play-preview'
599+ const cssOut = document.createElement('div')
600+ cssOut.className = 'type-play-css'
601+ output.append(preview, cssOut)
602+
603+ tool.append(controls, output)
604+ section.append(heading, tool)
605+ container.appendChild(section)
606+
607+ const selects = [...controls.querySelectorAll('select')]
608+ const text = controls.querySelector('#type-play-text')
609+ function update() {
610+ for (const select of selects) preview.style.setProperty(select.dataset.prop, select.value)
611+ preview.textContent = text.value
612+ cssOut.textContent = selects.map((select) => select.dataset.prop + ': ' + select.value + ';').join('\\n')
613+ }
614+ for (const select of selects) select.addEventListener('change', update)
615+ text.addEventListener('input', update)
616+ update()
617+ }
618+
552619function renderTools(tokens) {
553620 const tools = document.getElementById('tools')
554621 renderContrast(tools, tokens.filter((token) => token.category === 'colors'))
622+ renderTypographyPlayground(tools, tokens)
555623 updateActiveNav()
556624}
557625
0 commit comments