A lightweight base for single-file Preact apps. No install, no build step. Copy this file into a project directory, open it in a browser, start building.

The default view is a live demo — tokens, layout reference, components, and five mode skeletons to copy. Replace App() and everything below it on your first pass.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>mew mew</title>
<style>

/*
 * mew mew design system  karsonkalt.dev
 *
 * A lightweight UI base for single-file Preact apps.
 * No build step. No bundler. Copy this file into a project and start building.
 *
 * STACK
 *   import { html, render, useState, useEffect } from 'https://esm.sh/htm/preact/standalone'
 *   All UI is written as tagged template literals  no JSX, no transpilation.
 *
 * TOKENS
 *   Three surface depths: --bg / --surface / --code-bg
 *   One border: --border
 *   Text: --text / --dim / --blue / --green / --red / --code
 *   Type: --f (13px base) / --ff (system-ui) / --ffm (monospace)
 *   Never hardcode colors or font sizes. Always var(--x).
 *
 * LAYOUT CLASSES
 *   .box .row     flex column / row (min-height:0 on both  safe for nested scroll)
 *   .f1           flex:1 + min-height:0
 *   .sc .cl       overflow auto / hidden
 *   .ac .jb       align-items:center / justify-content:space-between
 *   .bb .br       1px border-bottom / border-right
 *   .wrap         flex-wrap:wrap
 *   .rel          position:relative
 *   .fs0          flex-shrink:0
 *   gap: .g4 .g5 .g6 .g8 .g10 .g12 .g14 .g16 .g20 .g24
 *   pad: .p4 .p6 .p8 .p12 .p14 .p16 .p20 .p24
 *       .ph8 .ph12 .ph16 .ph20  (horizontal only)
 *       .pv8 .pv12              (vertical only)
 *
 * TYPE CLASSES
 *   .lg .sm .xs   15px·600 / 11px / 10px
 *   .bold .med    weight 600 / 500
 *   .dim .lnk     --dim color / --blue color
 *   .mono         monospace
 *   .tr           truncate with ellipsis (display:block)
 *   .caps         uppercase + letter-spacing
 *   .sub          11px dim line (display:block, mt:2px)
 *   .i            italic
 *   .ma           margin:auto
 *
 * BASE COMPONENTS (defined in <script> below)
 *   Box({ row,f1,sc,cl,ac,jb,bb,br,rel,fs0,wrap,gap,pad,flex,class,style,...rest })
 *     The only layout primitive. Never use a raw <div> for structure.
 *   Txt({ dim,lnk,mono,tr,sm,xs,bold,med,i,ma,class,style,...rest })
 *     Inline text with token modifiers.
 *   Btn({ primary,danger,disabled,onClick,style,children })
 *     primary  green fill. danger  red outline. default  surface.
 *   Ghost({ onClick,class,style,children })
 *     Inline text action button. Blue, no border.
 *   Nav({ on,onClick,children })
 *     Sidebar nav item. .on = active state (blue left border).
 *     Wrap children in .lbl-primary and .lbl-secondary spans.
 *   Pre({ code,style,children })
 *     Preformatted block. code=true applies monospace code styling.
 *   Field({ label,children })
 *     Label above, content below, 5px gap.
 *
 * RULES FOR THE NEXT AGENT
 *   This file is the base. Pick a MODE skeleton below, replace App() and all
 *   placeholder components with your implementation, then delete the unused
 *   MODE skeletons and this instruction block.
 *
 *   0. THEME FIRST  before writing any code, ask the user:
 *      "What's the vibe? Dark (default), light, or something specific —
 *       a color, a mood, a brand palette?"
 *      Then update the :root token block to match before anything else.
 *      The defaults are a dark GitHub-style palette  good baseline but totally
 *      replaceable. Common directions: warm dark (amber/sand), light neutral,
 *      high-contrast, a brand color as --blue/--green accent.
 *      Keep the token names the same; only change the values.
 *
 *   1. Use html`...` for all markup. No .jsx files, no createElement calls.
 *   2. Every layout container is <${Box}>. Never a raw <div> for structure.
 *   3. Token classes only. No inline colors, no hardcoded font sizes.
 *   4. Buttons are <${Btn}> or <${Ghost}>. No raw <button> with ad-hoc styles.
 *   5. Controlled inputs: always bind value + onInput.
 *      Every <option> must have an explicit value attribute.
 *   6. Keep state close to where it's used.
 *      Pass the setter down as a prop named "set".
 *   7. Fetch in useEffect. Swallow AbortError. Surface everything else.
 *   8. The .field / .lbl classes exist for form layout — use Field for all labeled inputs.
 *   9. Compose upward from these primitives. Do not redefine what's already here.
 *  10. Everything you add  classes, components, patterns  gets a * suffix in the
 *      doc comment of that file. This marks it as agent/user-authored, not base system.
 *      Keep the * inventory current. A future agent reads it to know what is portable
 *      (no *) vs what belongs to this project (* = do not carry forward blindly).
 *  11. Update the MODE line in the header comment whenever the shape changes.
 *
 * ── MODE (agent-maintained) ───────────────────────────────────────────────────
 *   current: demo  mew mew component showcase
 */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0 }
button, input, select, textarea { font: inherit }
button { appearance: none; -webkit-appearance: none; cursor: pointer }

:root {
  --bg:      #0d1117;
  --surface: #161b22;
  --code-bg: #0a0d12;
  --border:  #30363d;
  --text:    #e6edf3;
  --dim:     #8b949e;
  --blue:    #58a6ff;
  --green:   #3fb950;
  --red:     #f85149;
  --code:    #79c0ff;
  --f:       13px;
  --ff:      system-ui, sans-serif;
  --ffm:     ui-monospace, monospace;
}

html, body { height: 100%; overflow: hidden }
body { background: var(--bg); color: var(--text); font: var(--f)/1.5 var(--ff); display: flex; flex-direction: column }

/* layout */
.box { display: flex; flex-direction: column; min-height: 0; min-width: 0 }
.row { flex-direction: row }
.f1  { flex: 1; min-height: 0 }
.sc  { overflow: auto }
.cl  { overflow: hidden }
.ac  { align-items: center }
.jb  { justify-content: space-between }
.bb  { border-bottom: 1px solid var(--border) }
.br  { border-right:  1px solid var(--border) }
.rel { position: relative }
.fs0 { flex-shrink: 0 }
.wrap{ flex-wrap: wrap }

/* gap */
.g2{gap:2px}.g3{gap:3px}.g4{gap:4px}.g5{gap:5px}.g6{gap:6px}.g8{gap:8px}
.g10{gap:10px}.g12{gap:12px}.g14{gap:14px}.g16{gap:16px}.g20{gap:20px}.g24{gap:24px}

/* padding */
.p4{padding:4px}.p6{padding:6px}.p8{padding:8px}.p12{padding:12px}
.p14{padding:14px}.p16{padding:16px}.p20{padding:20px}.p24{padding:24px}
.ph8{padding-left:8px;padding-right:8px}
.ph12{padding-left:12px;padding-right:12px}
.ph16{padding-left:16px;padding-right:16px}
.ph20{padding-left:20px;padding-right:20px}
.pv8{padding-top:8px;padding-bottom:8px}
.pv12{padding-top:12px;padding-bottom:12px}

/* text */
.dim  { color: var(--dim) }
.lnk  { color: var(--blue) }
.mono { font-family: var(--ffm) }
.tr   { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block }
.lg   { font-size: 15px; font-weight: 600; line-height: 1.2 }
.sm   { font-size: 11px }
.xs   { font-size: 10px }
.bold { font-weight: 600 }
.med  { font-weight: 500 }
.i    { font-style: italic }
.ma   { margin: auto }
.caps { text-transform: uppercase; letter-spacing: .04em }
.sub  { display: block; font-size: 11px; margin-top: 2px; line-height: 1.3; color: var(--dim) }

/* buttons */
.btn       { background: #21262d; border: 1px solid var(--border); border-radius: 6px; padding: 5px 12px; cursor: pointer; color: var(--text); font-weight: 500 }
.btn:hover { background: #30363d }
.btn:disabled { opacity: .5; cursor: default }
.btn-p       { background: #238636; border-color: #2ea043; color: #fff }
.btn-p:hover { background: #2ea043 }
.btn-d       { background: transparent; border-color: rgba(248,81,73,.4); color: var(--red) }
.ghost       { background: none; border: none; color: var(--blue); cursor: pointer; padding: 2px 0; font-size: 12px }
.ghost:hover { opacity: .8 }

/* nav item */
.nav        { display: flex; align-items: flex-start; width: 100%; text-align: left; background: none; border: none; border-left: 2px solid transparent; padding: 9px 10px 9px 14px; border-radius: 0 6px 6px 0; cursor: pointer; color: var(--dim) }
.nav:hover  { background: var(--surface) }
.nav.on     { background: var(--surface); border-left-color: var(--blue); color: var(--text) }
.nav.on .lbl-primary { color: var(--text); font-weight: 500 }
.nav > div  { flex: 1; min-width: 0; overflow: hidden }
.lbl-primary   { color: var(--text); font-size: 13px; line-height: 1.4; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.lbl-secondary { color: var(--dim); font-size: 11px; font-family: var(--ffm); display: block; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }

/* field / form */
.field { display: flex; flex-direction: column; gap: 5px }
.lbl   { font-size: 11px; font-weight: 600; color: var(--dim) }
input[type=text], input[type=date], input[type=number], select, textarea {
  background: var(--surface); border: 1px solid var(--border); border-radius: 6px;
  color: var(--text); padding: 4px 8px
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--blue); outline-offset: -1px }

/* pre */
.pre  { background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; font-family: var(--ffm); font-size: 12px; color: var(--dim); white-space: pre-wrap; overflow-x: auto }
.pre.code { color: var(--code); background: var(--code-bg) }

/* table */
table { border-collapse: collapse; width: 100%; font-size: 12px }
th { text-align: left; padding: 5px 10px; color: var(--dim); font-weight: 500; border-bottom: 1px solid var(--border); white-space: nowrap }
td { padding: 4px 10px; border-bottom: 1px solid var(--border); font-family: var(--ffm); white-space: nowrap }
tr:last-child td { border-bottom: none }
</style>
</head>
<body>
<div id="app"></div>
<script type="module">
import { html, render, useState, useEffect } from 'https://esm.sh/htm/preact/standalone'

// ── layout lookup tables ───────────────────────────────────────────────────────
const G = {2:'g2',3:'g3',4:'g4',5:'g5',6:'g6',8:'g8',10:'g10',12:'g12',14:'g14',16:'g16',20:'g20',24:'g24'}
const P = {'4px':'p4','6px':'p6','8px':'p8','12px':'p12','14px':'p14','16px':'p16','20px':'p20','24px':'p24'}

// ── base components ───────────────────────────────────────────────────────────
function Box({ row,f1,sc,cl,ac,jb,bb,br,rel,fs0,wrap, gap,pad,flex, class:cx='', style:s={}, children, ...rest }) {
  const gc = G[gap], pc = P[pad]
  const style = { ...(gc?{}:{gap}), ...(pc?{}:{padding:pad}), flex, ...s }
  const c = ['box',row&&'row',f1&&'f1',sc&&'sc',cl&&'cl',ac&&'ac',jb&&'jb',bb&&'bb',br&&'br',rel&&'rel',fs0&&'fs0',wrap&&'wrap',gc,pc,cx].filter(Boolean).join(' ')
  return html`<div class=${c} style=${Object.keys(style).some(k=>style[k]!=null)?style:undefined} ...${rest}>${children}</div>`
}

function Txt({ dim,lnk,mono,tr,sm,xs,bold,med,i,ma, class:cx='', style:s={}, children, ...rest }) {
  const c = [dim&&'dim',lnk&&'lnk',mono&&'mono',tr&&'tr',sm&&'sm',xs&&'xs',bold&&'bold',med&&'med',i&&'i',ma&&'ma',cx].filter(Boolean).join(' ')
  return html`<span class=${c||undefined} style=${Object.keys(s).length?s:undefined} ...${rest}>${children}</span>`
}

const Btn = ({ primary,danger,disabled,onClick,style:s={},children }) =>
  html`<button class=${['btn',primary&&'btn-p',danger&&'btn-d'].filter(Boolean).join(' ')} disabled=${disabled} onClick=${onClick} style=${Object.keys(s).length?s:undefined}>${children}</button>`

const Ghost = ({ onClick,children,class:cx='',style:s={} }) =>
  html`<button class=${['ghost',cx].filter(Boolean).join(' ')} onClick=${onClick} style=${Object.keys(s).length?s:undefined}>${children}</button>`

const Nav = ({ on,onClick,children }) =>
  html`<button class=${on?'nav on':'nav'} onClick=${onClick}><div>${children}</div></button>`

const Pre = ({ code,style:s={},children }) =>
  html`<pre class=${code?'pre code':'pre'} style=${Object.keys(s).length?s:undefined}>${children}</pre>`

const Field = ({ label,children }) =>
  html`<div class="field"><span class="lbl">${label}</span>${children}</div>`

// ── demo app ──────────────────────────────────────────────────────────────────
// This is the default template. It demonstrates mew mew's tokens, layout
// system, and components. Replace everything below App() on your first pass.

const SECTIONS = [
  { id: 'overview',    name: 'Overview',    meta: 'what & why' },
  { id: 'tokens',      name: 'Tokens',      meta: 'colors · type · surfaces' },
  { id: 'layout',      name: 'Layout',      meta: 'Box · gap · pad · flex' },
  { id: 'components',  name: 'Components',  meta: 'Btn · Ghost · Nav · Field · Pre' },
  { id: 'modes',       name: 'Modes',       meta: 'skeleton templates' },
]

function Topbar({ section, onSection }) {
  return html`
    <${Box} row ac jb bb style=${{flexShrink:0,height:'38px',padding:'0 16px'}}>
      <${Box} row ac gap=${10}>
        <${Txt} bold>mew mew</${Txt}>
        <${Txt} dim sm>design system</${Txt}>
      </${Box}>
      <${Box} row gap=${8}>
        ${SECTIONS.map(s => html`
          <${Ghost} key=${s.id} onClick=${()=>onSection(s.id)}
            style=${{color: section===s.id ? 'var(--text)' : 'var(--dim)', fontWeight: section===s.id?600:400}}>
            ${s.name}
          </${Ghost}>
        `)}
      </${Box}>
    </${Box}>
  `
}

// ── section: overview ────────────────────────────────────────────────────────
function SectionOverview() {
  return html`
    <${Box} sc f1 pad="32px" gap=${24} style=${{maxWidth:'680px',margin:'0 auto',width:'100%'}}>
      <${Box} gap=${8}>
        <${Txt} class="lg">mew mew</${Txt}>
        <${Txt} dim>A lightweight UI base for single-file Preact apps. No build step. No bundler.</${Txt}>
      </${Box}>

      <${Box} gap=${12}>
        <${Txt} bold>Stack</${Txt}>
        <${Pre} code>${`import { html, render, useState, useEffect }
  from 'https://esm.sh/htm/preact/standalone'`}</${Pre}>
        <${Txt} dim sm>All UI is tagged template literals — no JSX, no transpilation. Copy the file, open in a browser, start building.</${Txt}>
      </${Box}>

      <${Box} gap=${12}>
        <${Txt} bold>How to use</${Txt}>
        <${Box} gap=${8}>
          ${[
            ['1', 'Copy mewmew.html into your project.'],
            ['2', 'Pick a MODE skeleton from the Modes section.'],
            ['3', 'Replace App() and the placeholder components with your implementation.'],
            ['4', 'Delete unused MODE skeletons and this demo section.'],
            ['5', 'Update the MODE line in the header comment.'],
          ].map(([n,t]) => html`
            <${Box} row gap=${10} ac key=${n}>
              <span style=${{width:'18px',height:'18px',background:'var(--surface)',border:'1px solid var(--border)',borderRadius:'4px',display:'flex',alignItems:'center',justifyContent:'center',fontSize:'11px',color:'var(--dim)',flexShrink:0}}>${n}</span>
              <${Txt} sm>${t}</${Txt}>
            </${Box}>
          `)}
        </${Box}>
      </${Box}>

      <${Box} gap=${8}>
        <${Txt} bold>Conventions</${Txt}>
        <table>
          <tbody>
            ${[
              ['Layout',    'Always Box. Never a raw div for structure.'],
              ['Text',      'Always Txt. Token modifiers (dim, sm, bold…) as boolean props.'],
              ['Buttons',   'Btn or Ghost. No raw button with ad-hoc styles.'],
              ['Colors',    'var(--x) only. Never hardcode hex.'],
              ['State',     'Keep close to where it\'s used. Pass setter as prop named "set".'],
              ['Fetch',     'useEffect. Swallow AbortError. Surface everything else.'],
              ['New code',  'Add * suffix to doc entries. Marks agent-authored vs portable base.'],
            ].map(([k,v]) => html`
              <tr key=${k}>
                <td style=${{width:'100px',color:'var(--dim)'}}>${k}</td>
                <td>${v}</td>
              </tr>
            `)}
          </tbody>
        </table>
      </${Box}>
    </${Box}>
  `
}

// ── section: tokens ──────────────────────────────────────────────────────────
function SectionTokens() {
  const colors = [
    ['--bg',      '#0d1117', 'Page background'],
    ['--surface', '#161b22', 'Cards, panels, inputs'],
    ['--code-bg', '#0a0d12', 'Code blocks'],
    ['--border',  '#30363d', 'All borders'],
    ['--text',    '#e6edf3', 'Primary text'],
    ['--dim',     '#8b949e', 'Secondary / muted text'],
    ['--blue',    '#58a6ff', 'Links, active states, focus'],
    ['--green',   '#3fb950', 'Primary actions, success'],
    ['--red',     '#f85149', 'Destructive actions, errors'],
    ['--code',    '#79c0ff', 'Code text'],
  ]
  const typeTokens = [
    ['--f',   '13px',           'Base body size'],
    ['--ff',  'system-ui',      'Body typeface'],
    ['--ffm', 'ui-monospace',   'Monospace typeface'],
  ]
  const typeClasses = [
    ['.lg',    '15px / 600', 'Headings, labels'],
    ['.sm',    '11px',       'Secondary text, meta'],
    ['.xs',    '10px',       'Timestamps, codes'],
    ['.bold',  '600',        'Weight emphasis'],
    ['.med',   '500',        'Soft emphasis'],
    ['.dim',   'var(--dim)', 'Muted text'],
    ['.lnk',   'var(--blue)','Link / action text'],
    ['.mono',  '--ffm',      'Inline code'],
    ['.caps',  'uppercase',  'Label caps'],
    ['.tr',    'truncate',   'Ellipsis overflow'],
    ['.sub',   '11px dim',   'Subscript line'],
  ]

  return html`
    <${Box} sc f1 pad="32px" gap=${24} style=${{maxWidth:'760px',margin:'0 auto',width:'100%'}}>

      <${Box} gap=${12}>
        <${Txt} bold>Color tokens</${Txt}>
        <${Box} gap=${6}>
          ${colors.map(([name, val, desc]) => html`
            <${Box} row ac gap=${12} key=${name}>
              <div style=${{width:'32px',height:'32px',background:`var(${name})`,border:'1px solid var(--border)',borderRadius:'6px',flexShrink:0}}></div>
              <${Txt} mono sm style=${{width:'90px',flexShrink:0,color:'var(--code)'}}>${name}</${Txt}>
              <${Txt} mono sm dim style=${{width:'72px',flexShrink:0}}>${val}</${Txt}>
              <${Txt} sm dim>${desc}</${Txt}>
            </${Box}>
          `)}
        </${Box}>
      </${Box}>

      <${Box} gap=${12}>
        <${Txt} bold>Type tokens</${Txt}>
        <table>
          <thead><tr><th>Token</th><th>Value</th><th>Use</th></tr></thead>
          <tbody>
            ${typeTokens.map(([k,v,d]) => html`<tr key=${k}><td class="mono" style=${{color:'var(--code)'}}>${k}</td><td class="mono">${v}</td><td class="dim">${d}</td></tr>`)}
          </tbody>
        </table>
      </${Box}>

      <${Box} gap=${12}>
        <${Txt} bold>Type classes</${Txt}>
        <table>
          <thead><tr><th>Class</th><th>Value</th><th>Use</th></tr></thead>
          <tbody>
            ${typeClasses.map(([k,v,d]) => html`<tr key=${k}><td class="mono" style=${{color:'var(--code)'}}>${k}</td><td class="mono dim">${v}</td><td class="dim">${d}</td></tr>`)}
          </tbody>
        </table>
      </${Box}>

    </${Box}>
  `
}

// ── section: layout ──────────────────────────────────────────────────────────
function SectionLayout() {
  return html`
    <${Box} sc f1 pad="32px" gap=${24} style=${{maxWidth:'760px',margin:'0 auto',width:'100%'}}>

      <${Box} gap=${12}>
        <${Txt} bold>Box — the only layout primitive</${Txt}>
        <${Pre} code>${`<\${Box} row ac jb bb gap=\${12} pad="16px">
  <\${Txt} bold>title<\/\${Txt}>
  <\${Btn} primary>action<\/\${Btn}>
<\/\${Box}>`}</${Pre}>
        <${Txt} dim sm>Every prop maps to a CSS class or inline style. Never nest raw divs for structure.</${Txt}>
      </${Box}>

      <${Box} gap=${12}>
        <${Txt} bold>Box props</${Txt}>
        <table>
          <thead><tr><th>Prop</th><th>Class / Effect</th></tr></thead>
          <tbody>
            ${[
              ['row',    '.row — flex-direction: row'],
              ['f1',     '.f1 — flex:1 + min-height:0 (fills available space)'],
              ['sc',     '.sc — overflow: auto (scrollable)'],
              ['cl',     '.cl — overflow: hidden (clip)'],
              ['ac',     '.ac — align-items: center'],
              ['jb',     '.jb — justify-content: space-between'],
              ['bb',     '.bb — border-bottom'],
              ['br',     '.br — border-right'],
              ['rel',    '.rel — position: relative'],
              ['fs0',    '.fs0 — flex-shrink: 0'],
              ['wrap',   '.wrap — flex-wrap: wrap'],
              ['gap={n}','.g{n} — gap (2/3/4/5/6/8/10/12/14/16/20/24)'],
              ['pad="xpx"','.p{n} or inline — padding (4/6/8/12/14/16/20/24px)'],
              ['flex',   'inline flex: value'],
            ].map(([k,v]) => html`<tr key=${k}><td class="mono" style=${{color:'var(--code)'}}>${k}</td><td class="dim sm">${v}</td></tr>`)}
          </tbody>
        </table>
      </${Box}>

      <${Box} gap=${12}>
        <${Txt} bold>Common patterns</${Txt}>
        <${Box} gap=${16}>
          <${Box} gap=${6}>
            <${Txt} sm dim>Topbar (fixed height, never grows)</${Txt}>
            <${Pre} code>${`<\${Box} row ac jb bb style={{flexShrink:0,height:'38px',padding:'0 16px'}}>
  ...
<\/\${Box}>`}</${Pre}>
          </${Box}>
          <${Box} gap=${6}>
            <${Txt} sm dim>Two-pane shell (sidebar + main, both scroll independently)</${Txt}>
            <${Pre} code>${`<\${Box} f1>          // full height
  <\${Box} row f1 cl> // clip outer, row layout
    <\${Box} br sc style={{width:'220px',flexShrink:0}}>  // sidebar scrolls
    <\${Box} f1 sc>  // main scrolls
  <\/\${Box}>
<\/\${Box}>`}</${Pre}>
          </${Box}>
          <${Box} gap=${6}>
            <${Txt} sm dim>Card / panel</${Txt}>
            <${Pre} code>${`<\${Box} style={{background:'var(--surface)',border:'1px solid var(--border)',borderRadius:'8px'}}>
  <\${Box} row ac jb bb pad="12px"><\/\${Box}>  // header
  <\${Box} pad="16px" gap=\${12}><\/\${Box}>    // body
<\/\${Box}>`}</${Pre}>
          </${Box}>
        </${Box}>
      </${Box}>

    </${Box}>
  `
}

// ── section: components ───────────────────────────────────────────────────────
function SectionComponents() {
  const [nav, setNav] = useState('alpha')
  const [inp, setInp] = useState('')
  const [sel, setSel] = useState('b')

  return html`
    <${Box} sc f1 pad="32px" gap=${28} style=${{maxWidth:'760px',margin:'0 auto',width:'100%'}}>

      <${Box} gap=${12}>
        <${Txt} bold>Btn</${Txt}>
        <${Box} row gap=${8} ac wrap>
          <${Btn}>default</${Btn}>
          <${Btn} primary>primary</${Btn}>
          <${Btn} danger>danger</${Btn}>
          <${Btn} disabled>disabled</${Btn}>
        </${Box}>
        <${Pre} code>${`<\${Btn}>default<\/\${Btn}>
<\${Btn} primary>primary<\/\${Btn}>
<\${Btn} danger>danger<\/\${Btn}>
<\${Btn} disabled>disabled<\/\${Btn}>`}</${Pre}>
      </${Box}>

      <${Box} gap=${12}>
        <${Txt} bold>Ghost</${Txt}>
        <${Box} row gap=${16}>
          <${Ghost}>action</${Ghost}>
          <${Ghost}>+ new item</${Ghost}>
          <${Ghost}>view all →</${Ghost}>
        </${Box}>
        <${Pre} code>${`<\${Ghost} onClick=\${handler}>action<\/\${Ghost}>`}</${Pre}>
      </${Box}>

      <${Box} gap=${12}>
        <${Txt} bold>Nav</${Txt}>
        <${Box} style=${{width:'200px',border:'1px solid var(--border)',borderRadius:'8px',overflow:'hidden'}}>
          ${['alpha','beta','gamma'].map(id => html`
            <${Nav} key=${id} on=${nav===id} onClick=${()=>setNav(id)}>
              <span class="lbl-primary">${id}</span>
              <span class="lbl-secondary">meta · ${id}</span>
            </${Nav}>
          `)}
        </${Box}>
        <${Pre} code>${`<\${Nav} on=\${selected===id} onClick=\${()=>setSelected(id)}>
  <span class="lbl-primary">label<\/span>
  <span class="lbl-secondary">meta<\/span>
<\/\${Nav}>`}</${Pre}>
      </${Box}>

      <${Box} gap=${12}>
        <${Txt} bold>Field + inputs</${Txt}>
        <${Box} gap=${12} style=${{maxWidth:'300px'}}>
          <${Field} label="TEXT INPUT">
            <input type="text" value=${inp} onInput=${e=>setInp(e.target.value)} placeholder="type something"/>
          </${Field}>
          <${Field} label="SELECT">
            <select value=${sel} onChange=${e=>setSel(e.target.value)}>
              <option value="a">Option A</option>
              <option value="b">Option B</option>
              <option value="c">Option C</option>
            </select>
          </${Field}>
          <${Field} label="TEXTAREA">
            <textarea rows=${3} placeholder="multiline…"></textarea>
          </${Field}>
        </${Box}>
        <${Pre} code>${`<\${Field} label="LABEL">
  <input type="text" value=\${val} onInput=\${e=>setVal(e.target.value)}/>
<\/\${Field}>`}</${Pre}>
      </${Box}>

      <${Box} gap=${12}>
        <${Txt} bold>Pre</${Txt}>
        <${Pre}>plain preformatted block with .pre styling</pre>
        <${Pre} code>code block — monospace, code color, dark bg</pre>
      </${Box}>

      <${Box} gap=${12}>
        <${Txt} bold>Txt</${Txt}>
        <${Box} gap=${6}>
          <${Txt}>default text</${Txt}>
          <${Txt} dim>dim secondary text</${Txt}>
          <${Txt} lnk>link / action text</${Txt}>
          <${Txt} sm>small (11px)</${Txt}>
          <${Txt} xs>extra small (10px)</${Txt}>
          <${Txt} bold>bold 600</${Txt}>
          <${Txt} mono>monospace inline</${Txt}>
          <${Txt} class="caps">caps label</${Txt}>
          <${Txt} i dim>italic muted</${Txt}>
        </${Box}>
      </${Box}>

      <${Box} gap=${12}>
        <${Txt} bold>Table</${Txt}>
        <table>
          <thead><tr><th>Name</th><th>Type</th><th>Value</th></tr></thead>
          <tbody>
            <tr><td>alpha</td><td class="dim">string</td><td class="mono">foo</td></tr>
            <tr><td>beta</td><td class="dim">number</td><td class="mono">42</td></tr>
            <tr><td>gamma</td><td class="dim">boolean</td><td class="mono">true</td></tr>
          </tbody>
        </table>
        <${Txt} dim sm>Table styles are global. No component needed.</${Txt}>
      </${Box}>

    </${Box}>
  `
}

// ── section: modes ────────────────────────────────────────────────────────────
// Each mode is a full skeleton. Pick one, activate it as App(), delete the rest.
// ─────────────────────────────────────────────────────────────────────────────
function SectionModes() {
  const [active, setActive] = useState('list-inspector')
  const modes = [
    { id: 'list-inspector', name: 'List → Inspector',   desc: 'Sidebar nav list, detail pane on selection. Data-entry, settings, entity viewers.' },
    { id: 'query-viewer',   name: 'Query Viewer',       desc: 'Input at top, result table below. SQL/J1QL explorers, filters, search.' },
    { id: 'dashboard',      name: 'Dashboard',          desc: 'Metric cards + stat rows, read-only overview. No sidebar.' },
    { id: 'data-dense',     name: 'Data Dense',         desc: 'Full-width table, filter bar at top, minimal chrome. Bulk operations.' },
    { id: 'form-wizard',    name: 'Form / Wizard',      desc: 'Sequential steps, validation, submit. Onboarding, config flows.' },
  ]

  const skeletons = {
    'list-inspector': `// MODE: list → inspector
// Topbar (38px) + sidebar (220px) + detail pane
// Sidebar scrolls independently; detail scrolls independently.

function App() {
  const [items] = useState([])          // fetch your data here
  const [sel, setSel] = useState(null)
  const item = items.find(i => i.id === sel) ?? null

  return html\`
    <\${Box} f1>
      <\${Box} row ac jb bb style={{flexShrink:0,height:'38px',padding:'0 16px'}}>
        <\${Txt} bold>app name</\${Txt}>
      </\${Box}>
      <\${Box} row f1 cl>

        <\${Box} br sc style={{width:'220px',flexShrink:0}}>
          <\${Box} row ac jb class="ph16 pv8 bb">
            <\${Txt} sm bold dim>items</\${Txt}>
            <\${Ghost} onClick={() => {}}>+ new</\${Ghost}>
          </\${Box}>
          \${items.map(it => html\`
            <\${Nav} key=\${it.id} on=\${sel===it.id} onClick={() => setSel(it.id)}>
              <span class="lbl-primary">\${it.name}</span>
              <span class="lbl-secondary">\${it.meta}</span>
            </\${Nav}>
          \`)}
        </\${Box}>

        <\${Box} f1 sc>
          \${!item
            ? html\`<\${Box} f1 ac style={{justifyContent:'center'}}><\${Txt} dim i>select an item</\${Txt}></\${Box}>\`
            : html\`
              <\${Box} row ac jb bb class="ph20 pv12" style={{flexShrink:0}}>
                <\${Txt} bold>\${item.name}</\${Txt}>
                <\${Btn} primary onClick={() => {}}>save</\${Btn}>
              </\${Box}>
              <\${Box} pad="20px" gap=\${16}>
                <!-- fields / content here -->
              </\${Box}>
            \`
          }
        </\${Box}>

      </\${Box}>
    </\${Box}>
  \`
}`,

    'query-viewer': `// MODE: query viewer
// Topbar + query input bar (fixed) + result table (scrollable).
// Input is always visible; table fills remaining height.

function App() {
  const [query, setQuery]   = useState('')
  const [rows, setRows]     = useState([])
  const [loading, setLoad]  = useState(false)

  async function run() {
    setLoad(true)
    // fetch / query logic here
    setLoad(false)
  }

  return html\`
    <\${Box} f1>
      <\${Box} row ac jb bb style={{flexShrink:0,height:'38px',padding:'0 16px'}}>
        <\${Txt} bold>app name</\${Txt}>
      </\${Box}>

      <\${Box} row ac bb gap=\${8} class="p8" style={{flexShrink:0}}>
        <input type="text" value=\${query} onInput=\${e=>setQuery(e.target.value)}
          placeholder="enter query…" style={{flex:1}}/>
        <\${Btn} primary disabled=\${loading} onClick=\${run}>
          \${loading ? 'running…' : 'run'}
        </\${Btn}>
      </\${Box}>

      <\${Box} f1 sc>
        \${rows.length === 0
          ? html\`<\${Box} f1 ac style={{justifyContent:'center'}}><\${Txt} dim i>no results</\${Txt}></\${Box}>\`
          : html\`
            <table>
              <thead><tr>
                \${Object.keys(rows[0]).map(k => html\`<th key=\${k}>\${k}</th>\`)}
              </tr></thead>
              <tbody>
                \${rows.map((r,i) => html\`
                  <tr key=\${i}>
                    \${Object.values(r).map((v,j) => html\`<td key=\${j}>\${v}</td>\`)}
                  </tr>
                \`)}
              </tbody>
            </table>
          \`
        }
      </\${Box}>
    </\${Box}>
  \`
}`,

    'dashboard': `// MODE: dashboard
// No sidebar. Topbar + metric cards row + content area.
// Cards are fixed-height; content scrolls.

function App() {
  const metrics = [
    { label: 'Total',   value: '—' },
    { label: 'Active',  value: '—' },
    { label: 'Errors',  value: '—' },
  ]

  return html\`
    <\${Box} f1>
      <\${Box} row ac jb bb style={{flexShrink:0,height:'38px',padding:'0 16px'}}>
        <\${Txt} bold>app name</\${Txt}>
      </\${Box}>

      <\${Box} row gap=\${12} bb class="p16" style={{flexShrink:0}}>
        \${metrics.map(m => html\`
          <\${Box} key=\${m.label} gap=\${4}
            style={{background:'var(--surface)',border:'1px solid var(--border)',borderRadius:'8px',padding:'12px 16px',minWidth:'120px'}}>
            <\${Txt} xs dim caps>\${m.label}</\${Txt}>
            <\${Txt} class="lg">\${m.value}</\${Txt}>
          </\${Box}>
        \`)}
      </\${Box}>

      <\${Box} f1 sc pad="16px" gap=\${16}>
        <!-- charts / tables / content here -->
      </\${Box}>
    </\${Box}>
  \`
}`,

    'data-dense': `// MODE: data dense
// Full-width table. Filter bar at top (fixed). Table scrolls.
// Minimal chrome — no sidebar, no detail pane.

function App() {
  const [filter, setFilter] = useState('')
  const [rows, setRows]     = useState([])
  const visible = rows.filter(r => !filter || JSON.stringify(r).includes(filter))

  return html\`
    <\${Box} f1>
      <\${Box} row ac jb bb style={{flexShrink:0,height:'38px',padding:'0 16px'}}>
        <\${Txt} bold>app name</\${Txt}>
        <\${Txt} dim sm>\${visible.length} rows</\${Txt}>
      </\${Box}>

      <\${Box} row ac bb gap=\${8} class="ph16 pv8" style={{flexShrink:0}}>
        <input type="text" value=\${filter} onInput=\${e=>setFilter(e.target.value)}
          placeholder="filter…" style={{width:'240px'}}/>
        <\${Btn} primary onClick={() => {}}>+ new</\${Btn}>
      </\${Box}>

      <\${Box} f1 sc>
        <table>
          <thead><tr>
            <!-- column headers here -->
          </tr></thead>
          <tbody>
            \${visible.map((r,i) => html\`
              <tr key=\${i}>
                <!-- cells here -->
              </tr>
            \`)}
          </tbody>
        </table>
      </\${Box}>
    </\${Box}>
  \`
}`,

    'form-wizard': `// MODE: form / wizard
// Sequential steps. Step indicator (fixed) + form body (scrollable) + footer nav.
// No sidebar.

const STEPS = ['Details', 'Config', 'Review']

function App() {
  const [step, setStep] = useState(0)
  const [data, setData] = useState({})
  const set = k => v => setData(d => ({...d, [k]: v}))

  return html\`
    <\${Box} f1>
      <\${Box} row ac jb bb style={{flexShrink:0,height:'38px',padding:'0 16px'}}>
        <\${Txt} bold>app name</\${Txt}>
        <\${Txt} dim sm>step \${step+1} of \${STEPS.length}</\${Txt}>
      </\${Box}>

      <\${Box} row ac bb gap=\${0} style={{flexShrink:0}}>
        \${STEPS.map((s,i) => html\`
          <\${Box} key=\${s} pad="12px" class="ph20"
            style={{borderBottom: i===step ? '2px solid var(--blue)' : '2px solid transparent',
                    color: i===step ? 'var(--text)' : 'var(--dim)',
                    fontWeight: i===step ? 600 : 400}}>
            \${s}
          </\${Box}>
        \`)}
      </\${Box}>

      <\${Box} f1 sc pad="32px" gap=\${16} style={{maxWidth:'520px',margin:'0 auto',width:'100%'}}>
        <!-- step content here, gate on step index -->
      </\${Box}>

      <\${Box} row ac jb bb class="ph20 pv12" style={{flexShrink:0,borderTop:'1px solid var(--border)'}}>
        <\${Btn} disabled=\${step===0} onClick=\${()=>setStep(s=>s-1)}>← back</\${Btn}>
        \${step < STEPS.length-1
          ? html\`<\${Btn} primary onClick=\${()=>setStep(s=>s+1)}>next →</\${Btn}>\`
          : html\`<\${Btn} primary onClick=\${()=>{}}>submit</\${Btn}>\`
        }
      </\${Box}>
    </\${Box}>
  \`
}`,
  }

  return html`
    <${Box} row f1 cl>
      <${Box} br sc style=${{width:'200px',flexShrink:0}}>
        <${Box} row ac class="ph16 pv8 bb">
          <${Txt} sm bold dim>templates</${Txt}>
        </${Box}>
        ${modes.map(m => html`
          <${Nav} key=${m.id} on=${active===m.id} onClick=${()=>setActive(m.id)}>
            <span class="lbl-primary">${m.name}</span>
          </${Nav}>
        `)}
      </${Box}>

      <${Box} f1 sc pad="24px" gap=${16}>
        ${modes.filter(m=>m.id===active).map(m => html`
          <${Box} gap=${8} key=${m.id}>
            <${Txt} bold>${m.name}</${Txt}>
            <${Txt} dim sm>${m.desc}</${Txt}>
          </${Box}>
          <${Pre} code style=${{fontSize:'11px',lineHeight:'1.6'}}>${skeletons[m.id]}</${Pre}>
        `)}
      </${Box}>
    </${Box}>
  `
}

// ── root ──────────────────────────────────────────────────────────────────────
function App() {
  const [section, setSection] = useState('overview')
  const views = { overview: SectionOverview, tokens: SectionTokens, layout: SectionLayout, components: SectionComponents, modes: SectionModes }
  const View = views[section] ?? SectionOverview

  return html`
    <${Box} f1>
      <${Topbar} section=${section} onSection=${setSection}/>
      <${View}/>
    </${Box}>
  `
}

render(html`<${App}/>`, document.getElementById('app'))
</script>
</body>
</html>