Files
zeekayandHanzo Dev f5531da6e4 Borders you can see are not the same as borders that shout
The dark theme drew every emphasised edge at --neutral-500 (#737373) — a 4.43:1
mid-grey. One of those around a card on near-black is a wireframe, and that is
what "garish white on dark for outlines" was: hanzoai/id put it on .hanzo-id-input,
base/ui-react put it on .input, .btn--outline, a dashed .btn--add and an iframe
frame. They were following the file's own instruction to reach for --border-strong
whenever an edge had to be visible, because the only alternative was --border at
#1f1f1f — 1.27:1, invisible the moment it lands on anything lifted. Two rungs, an
octave apart, and nothing in between.

So there is a middle now, and the tiers split by DUTY rather than by degree.
--border (.10) and --border-strong (.16) are DECORATIVE: alpha, so they composite
correctly on the page, on a card and inside a popover, and they owe no ratio.
--border-control (--neutral-500) is PERCEIVABLE: it is the edge that IS the
affordance, it owes 3:1, and it is named for the duty so it cannot be spent on a
card by someone who just wanted "stronger". The gate follows the duty — it pins
--border-control and --ring, and lets the hairlines be as quiet as they look.
--input, which every shadcn `border-input` resolves through, was #1f1f1f at
1.27:1 and is now the control rung: that was a live WCAG 1.4.11 failure on every
field in the fleet, and nothing measured it because the gate watched a token the
inputs were not using.

The rest is the reference surface (hanzoai/extension sidebar.css) carried over:
#0a0a0a rather than #000000, a 0a/0f/17/26 lift ladder that actually steps,
#fafafa ink instead of #ffffff (21:1 on pure black is the maximum-contrast pair
that exists, and it halates), a graded text ramp at .78/.55/#a3a3a3, and
--glass/--glass-strong so a surface can rise by 5% of white instead of a block
fill. --border-hairline and --border-card were two more names for the one
hairline and are gone; the 46 components that used them now say --border.

MOBILE-FIRST, and free. Spacing is authored at phone values and scales up at md.
--gutter is max(1rem, safe-area-inset), so a notch is cleared with no per-app
rule. Under pointer:coarse every control clears a 44px --tap-target, checkbox and
radio hit areas expand to 44px via a pseudo-element while the 16px box is left
alone (WCAG 2.5.8 puts the floor at 24px; every one of them shipped at 16), and
fields render at --text-control = 16px so iOS stops zooming the viewport on
focus. --text-control has to be a token rather than a media query because every
component sets its size inline and an inline style outranks any stylesheet.

Light was quietly broken and is not a theme you can leave for later: --card,
--muted, --secondary and --accent were four names for #f5f5f5, so the surface
ladder was flat; ::selection had no light value; and the components painted from
the --white-* ladder, which does not invert — a tier2 badge was white-on-white
and the table header was a black bar with grey text on it. Components now paint
from --glass/--surface-*, and check 4 fails the build if any semantic token built
from a white rung has no .light counterpart.

Verified in Chromium at 390 / 768 / 1280 on the real components, reading computed
styles: no horizontal overflow at any width, fields 13px -> 16px on touch,
targets under 44px 16/19 -> 1/19 (the last is the 16px checkbox box, whose hit
area measures 44x44 and takes a tap 18px off-centre), --ring and --border-control
at 3.19:1 dark / 4.05:1 light worst case.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-04 09:47:04 -07:00

32 lines
2.8 KiB
HTML

<!-- @dsCard group="Components" viewport="700x300" name="Navigation" subtitle="Tabs and Accordion" -->
<!doctype html><html class="dark"><head><meta charset="utf-8"><title>Navigation components</title>
<link rel="stylesheet" href="../../styles.css">
<script src="https://unpkg.com/[email protected]/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/[email protected]/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
<script src="../../_ds_bundle.js"></script>
<style>body{margin:0;padding:20px;background:#000;font-family:var(--font-sans)}pre{margin:0;padding:16px;background:var(--card);border:1px solid var(--border);border-radius:var(--radius-lg);font-size:12px;line-height:1.7;color:var(--neutral-300);overflow:auto}</style></head>
<body><div id="root"></div>
<script type="text/babel">
const { Tabs, TabsList, TabsTrigger, TabsContent, Accordion, AccordionItem } = window.HanzoDesignSystem_0a9445;
const TS = "import Hanzo from '@hanzo/cloud'\n\nconst hanzo = new Hanzo()\nawait hanzo.chat.completions.create({\n model: 'enso-pro',\n messages: [{ role: 'user', content: 'Hi' }],\n})";
const PY = "from hanzo import Hanzo\n\nhanzo = Hanzo()\nhanzo.chat.completions.create(\n model=\"enso-pro\",\n messages=[{\"role\": \"user\", \"content\": \"Hi\"}],\n)";
const SH = "curl https://api.hanzo.ai/v1/chat/completions \\\n -H \"Authorization: Bearer $HANZO_API_KEY\" \\\n -d '{\"model\":\"enso-pro\"}'";
function Demo(){
return (<div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:24}}>
<Tabs defaultValue="ts">
<TabsList><TabsTrigger value="ts">TypeScript</TabsTrigger><TabsTrigger value="py">Python</TabsTrigger><TabsTrigger value="sh">cURL</TabsTrigger></TabsList>
<TabsContent value="ts"><pre>{TS}</pre></TabsContent>
<TabsContent value="py"><pre>{PY}</pre></TabsContent>
<TabsContent value="sh"><pre>{SH}</pre></TabsContent>
</Tabs>
<Accordion defaultValue="q1">
<AccordionItem value="q1" title="How is usage billed?">Per token, metered hourly. You pay only for what you use.</AccordionItem>
<AccordionItem value="q2" title="Can I self-host?">Yes. Every service in the Hanzo cloud is open source.</AccordionItem>
<AccordionItem value="q3" title="Which models are available?">Zen open weights, plus Enso Flash, Pro, and Ultra.</AccordionItem>
</Accordion>
</div>)
}
ReactDOM.createRoot(document.getElementById('root')).render(<Demo />);
</script></body></html>