HTML & CSS
The zero-build lane — NAMCHE for server-rendered HTML, no React required.
@namche/css is the design
system for apps that write plain HTML: Hono services, Astro content pages,
internal tools, anything server-rendered. One stylesheet, no build step, no
framework, no JavaScript — the components on this site restated as CSS
classes.
Both lanes read the same semantic variables from
@namche/design-tokens,
so they cannot drift apart: a button is an Erebos pill with an uppercase mono
label whether it came from the registry or from a class attribute.
Which lane?
- React registry (the rest of these docs) — products and tools that need interactive components: dialogs, selects, command menus, data tables.
@namche/css— server-rendered pages where interactivity is forms and links. When a page outgrows that, reach for platform elements (<dialog>,popover,<details>) styled by the same tokens before reaching for React.
The React components are canonical. If the CSS lane renders something
differently, that is a bug in @namche/css.
Install
npm install @namche/cssWith a bundler (Vite, Astro, …) one import brings tokens, theme and components:
@import "@namche/css";Without a build step, inline the files into your page <style> (or serve
them statically) in the order styles.css declares: the tokens, the semantic
bridge, then the components. Apps that already load the tokens import
@namche/css/components.css alone. In a Hono app, resolve the files from
node_modules at startup and cache the concatenated result.
Use
<button class="btn">Save</button>
<a class="btn btn-outline btn-sm" href="/edit">Edit</a>
<div class="card">
<div class="card-header">
<div class="card-title">Expedition</div>
<div class="card-description">Eight members, two routes.</div>
</div>
<div class="card-content prose"><p>…</p></div>
<div class="card-footer"><button class="btn btn-sm">Review</button></div>
</div>
<div class="field">
<label class="label" for="name">Name</label>
<input class="input" id="name" placeholder="Ang Tharkay" />
<span class="field-description">As recorded in the permit.</span>
</div>The vocabulary:
| Classes | Mirrors |
|---|---|
.btn + -outline -secondary -ghost -destructive -link, sizes -xs -sm -lg -icon | Button |
.badge + -secondary -outline -destructive | Badge |
.card, .card-header -title -description -action -content -footer, .card-sm | Card |
.input .textarea .select .label .field .field-description | Input, Textarea, Label |
.table (wrap in .table-container for overflow) | Table |
.alert, .alert-title -description, .alert-destructive | Alert |
.separator, .separator-vertical | Separator |
.kbd | Kbd |
.prose | running text with the editorial link voice |
States follow the platform: disabled, :focus-visible and
aria-invalid="true" render the same treatments as the React lane.
Dark mode
Same contract as the registry: add the dark class to <html>. Every colour
is a variable that flips with the class — no second stylesheet.
Fonts
Nothing to do: every face loads from cdn.namche.ai via the tokens'
@font-face rules — Namche Shadow always, and Geist since
@namche/design-tokens 1.2.0. On older token versions Geist is the
consumer's job (self-host it, for example via @fontsource-variable/geist);
without it the token stacks fall back to system fonts.