Skip to content

Commit c66ffc3

Browse files
committed
feat(cli): add typography playground to panda studio
1 parent 6fbd19b commit c66ffc3

2 files changed

Lines changed: 74 additions & 1 deletion

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@pandacss/cli': patch
3+
---
4+
5+
Add a typography playground to `panda studio`. Combine font-size, weight, family, line-height, and letter-spacing tokens on your own sample text to preview a text style before you build it.

packages/cli/src/studio-codegen.ts

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

198205
const 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+
552619
function 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

Comments
 (0)