Files
zeekay 81dc6df0ac Make the base layer apply, bind Zen by name, and let appearance reach the page
The design system's base layer had never applied. `@import … layer(base)`
compiles to `@media layer(base){ @layer base{ … } }`, and `layer(base)` is not a
media query, so it matched nothing: body computed Times New Roman and the rule
that takes the underline off a link never ran — ninety-five of them on the
research index. It is imported bare now; the file opens its own layer.

Nothing was asking for Zen by name either. The tokens that declare --font-sans
were the one @hanzo/design file this stylesheet did not import, so the variable
was empty and text got the face only where gui's own fallback happened to carry
it. One :root block binds the family from @hanzo/font, and no component spells a
typeface.

Type is rungs rather than numbers, on the two knobs zoo.ngo already derives its
register from, so the pages share a scale without restating a ramp. Display type
is stated on the element, because gui injects its letter-spacing class at render
and a site class ties on specificity and loses on order.

Appearance can now reach the whole page: the palette moved out of a TypeScript
object a stylesheet cannot see and into tokens, so the accent axis lands on the
send key instead of on nothing. The boot script runs in the head, a rung earlier
than the body, so the choice is in effect before the first paint. The light class
goes on the document because the light half of every colour token is scoped to
it, and with the base layer alive every heading would otherwise paint near-white
on paper.
2026-09-05 13:56:36 -07:00

48 lines
2.0 KiB
TypeScript

import { useState } from 'react'
import { YStack } from '@hanzo/ui'
import { Appearance, useAppearance } from '@hanzo/appearance'
import { Label, Panel, Press } from './kit'
/**
* How the page reads to you: type size, how far apart the sizes sit, density,
* face, measure.
*
* Nothing here decides any of that. @hanzo/appearance writes the knobs and
* @hanzo/design multiplies them into every ramp, so a change reaches the whole
* page rather than the handful of places that happen to read a token directly —
* which is only true while this site keeps asking for rungs instead of numbers.
*
* Bottom LEFT, because bottom right is Ask Blue. A site with a settings mark and
* a chat mark in one corner has asked the reader to guess which is which, and an
* overlap that only resolves by stacking order is still two controls in one
* place.
*
* `useAppearance` runs whether the panel is open or not. The head script paints
* type, density and measure before anything else, but it deliberately does not
* validate a colour, so the mount is where an accent lands. The panel itself is
* not built until it is asked for.
*/
export default function Look() {
const [open, setOpen] = useState(false)
useAppearance()
return (
<YStack position="fixed" b={12} l={12} z={40} items="flex-start" gap="$2">
{/* Opaque, unlike the page's panels: this one floats over running text and
the site's translucent surface leaves both unreadable. */}
{open && (
<Panel bg="white" width={320} maxW="calc(100vw - 24px)" maxH="70vh" overflow="scroll" gap="$2">
<Appearance />
</Panel>
)}
{/* A mark, not a button: two letters and the site's edge. Measured, a
full-padded Press lands on the door's composer at 390 — the composer is
the width of the phone there, so the only clear ground is the strip
under it. */}
<Press px="$2" py="$1" onPress={() => setOpen(!open)} label="How this page reads">
<Label>Aa</Label>
</Press>
</YStack>
)
}