# @araharah/theme The **Telemetry Rams** design system as a Tailwind v4 CSS-first token package: every color, spacing, type, and radius value in arah² is defined once in `src/tokens.css`, compiled to `dist/rams.css`, and consumed by `@araharah/web`. The package also hosts the MapLibre basemap style fork, the self-hosted typefaces, and the audit / screenshot evidence tooling. Part of the [arah² monorepo](../README.md) — see the root [README](../README.md) and [`docs/`](../docs/) for bot and Mini App context. --- ## Contents | Path | What it is | | --- | --- | | `src/tokens.css` | Single source of truth for every numeric design value: `@theme static` color / spacing / font / radius tokens plus the `--type-*` typography scale | | `src/theme.css` | Entry stylesheet — imports Tailwind, `tokens.css`, `components.css`; declares the self-hosted `@font-face` rules and the Telegram light/dark shell slots | | `src/components.css` | Component layer (`@layer components`): buttons, inputs, telemetry cards, event chips, maneuver banner, checkbox, toggle, and the `.text-*` typography helpers | | `src/sheet.html` | Component sheet — the rendered proof that every component derives from tokens | | `gallery.html` | DSGN-02 approval gallery: mockup reference render vs. implementation capture, side by side | | `basemap/build.mjs` | Builds `basemap/liberty-rams.json` by forking the OpenFreeMap `liberty` style and deriving every cartographic color from `tokens.css` | | `basemap/liberty-rams.json` | The generated MapLibre style — imported directly by `web/src/map/map.ts` | | `basemap/index.html` | MapLibre GL proof page over KL, with a placeholder route vector | | `scripts/audit.mjs` | Off-token audit: scans rendered CSS and flags any value that does not derive from `tokens.css` | | `scripts/audit.test.mjs` | TDD harness for the audit (12 contract tests, zero dependencies) | | `scripts/screenshot.mjs` | Playwright capture toolchain — writes `screenshots/` | | `fonts/geist/`, `fonts/space-grotesk/` | Self-hosted woff2 subsets (SIL Open Font License 1.1) | ```mermaid graph TD TOK["src/tokens.css — colors, spacing, type, radius"] COMP["src/components.css — component classes"] ENTRY["src/theme.css — entry stylesheet"] CLI["@tailwindcss/cli"] DIST["dist/rams.css — built stylesheet"] BUILD["basemap/build.mjs"] STYLE["basemap/liberty-rams.json"] WEB["@araharah/web — Mini App shell"] AUDIT["scripts/audit.mjs"] SHOTS["scripts/screenshot.mjs"] GAL["gallery.html + src/sheet.html + basemap/index.html"] TOK --> COMP TOK --> ENTRY COMP --> ENTRY ENTRY --> CLI CLI --> DIST TOK --> BUILD BUILD --> STYLE DIST --> WEB STYLE --> WEB TOK --> AUDIT GAL --> AUDIT GAL --> SHOTS SHOTS --> GAL classDef tokens fill:#EB8008,stroke:#1A1A1A,stroke-width:2px,color:#1A1A1A classDef build fill:#D9D2C6,stroke:#1A1A1A,stroke-width:2px,color:#1A1A1A classDef out fill:#F5EDE0,stroke:#736B1E,stroke-width:2px,color:#1E1B14 class TOK,COMP,ENTRY tokens class CLI,BUILD,AUDIT,SHOTS build class DIST,STYLE,WEB,GAL out ``` --- ## Installation `@araharah/theme` is a workspace package — it is never published. Consumers depend on it by name and install from the monorepo root: ```bash git clone http://gitmirah:3000/c-fu/arah2.git cd arah2 npm install ``` Requires Node.js and npm. `basemap/build.mjs` needs Node >= 18 (global `fetch`), and `basemap:preview` shells out to `python3`. --- ## Quick start ```bash npm run build -w theme # src/theme.css -> dist/rams.css (minified) npm run sheet # build, then open theme/src/sheet.html in a browser npm run audit # audit the rendered CSS for off-token values ``` --- ## Scripts | Command | Description | | --- | --- | | `npm run build -w theme` | `tailwindcss -i ./src/theme.css -o ./dist/rams.css --minify` | | `npm run send -w theme` | Copy `fonts/geist` and `fonts/space-grotesk` into `dist/fonts/` so the built stylesheet ships with its typefaces | | `npm run sheet -w theme` | `build`, then print the path to `src/sheet.html` (open it in a browser) | | `npm run gallery -w theme` | `build` + `capture`, then serve the workspace on `:8123` and block on `basemap:preview` | | `npm run basemap -w theme` | `node basemap/build.mjs` — fetch OpenFreeMap `liberty`, derive the Rams cartography, write `basemap/liberty-rams.json` | | `npm run basemap:preview -w theme` | `python3 -m http.server 8123` — serve `theme/` as docroot so `dist/rams.css`, the woff2 fonts, and `basemap/` resolve | | `npm run audit -w theme` | `node ./scripts/audit.mjs ./dist/rams.css` — off-token audit | | `npm run capture -w theme` | `node ./scripts/screenshot.mjs` — Playwright captures into `theme/screenshots/` | | `npm test -w theme` | `node ./scripts/audit.test.mjs` — the audit's contract tests | The root `package.json` delegates `sheet`, `gallery`, `audit`, and `capture` to this workspace, so `npm run audit` from the root is the same command. ### Testing `npm test` runs a plain Node harness — no test framework, no dev-server dependency. `audit.test.mjs` spawns `audit.mjs` against fixtures under `/tmp/opencode/audit-test/` and asserts exit codes: | Contract | Expectation | | --- | --- | | T1 | Stylesheet using only token values → exit `0` | | T2 | Off-token color / non-zero radius / off-token font / shadow / blur / backdrop-filter → exit `1`, each listed as `file:line: declaration` | | T3 | `tokens.css` drifting below 40 colors → exit `2` | | T4 | Any `--radius-*` token other than zero → exit `2` | | T5 | The real `dist/rams.css` build → exit `0` | | T6 | Off-token values inside `@layer components` → flagged | | T7 | `@layer utilities` only audits classes the implementation pages actually wear | | T8 | Inline `style="…"` attributes are audited too | | T9 | The deprecated bare `--radius` key drives bare `rounded` at zero | | T10–T12 | Literal `font:` shorthands, named colors / `oklch()` and friends, and spacing + typography numerics are flagged | `audit.mjs` exit codes: `0` clean, `1` violations (each printed as `file:line: declaration`), `2` token-sheet drift or an error. ### Coverage No coverage threshold configured. The audit itself is the enforcement mechanism: it is a token-conformance gate, not a line-coverage gate. --- ## Usage ### Importing the built stylesheet `@araharah/web` pulls the package in from its shell stylesheet: ```css /* web/src/shell.css */ @import "@araharah/theme/dist/rams.css"; ``` That single import brings the full token layer (`@theme static`, so every token survives compilation as a plain CSS custom property), the `@layer components` class API, the self-hosted `@font-face` rules, and the Telegram light/dark shell slots `--shell-bg` / `--shell-on-surface`. ### Consuming tokens in your own CSS Because `@theme static` forces every token into the output, tokens are addressable as custom properties from anywhere — no Tailwind build step needed: ```css .surface { background: var(--color-surface); border: 1px solid var(--color-matte-black); padding: var(--spacing-space-md); border-radius: var(--radius-md); /* resolves to 0 — Rams forbids roundedness */ } .headline { font: var(--type-headline-md); letter-spacing: var(--type-headline-md-tracking); } ``` Note the convention: `font:` always takes the compound `--type-*` token and letter-spacing takes the `-tracking` companion, because the CSS `font` shorthand cannot carry tracking. ### Component classes `src/components.css` is the public component API. Every declaration in it is a token reference; the only literals are the machined border geometry (1px / 2px rules, 3px focus brackets) and the exact 18×18 checkbox square. ```html
TrafficKL-01
2.4KM AHEAD
Congestion Crash Clear
→ STEP 320 m
``` ### Importing the basemap style ```ts // web/src/map/map.ts import ramsStyle from "@araharah/theme/basemap/liberty-rams.json"; ``` The style object is never re-tinted at runtime — MapLibre receives it as-is. --- ## How the tokens are defined `src/tokens.css` holds every value and nothing else does. Its header records the vocabulary as copied verbatim from the frontmatter of `stitch_minimalist_route_navigator/telemetry_rams/DESIGN.md`, so mockup markup maps 1:1 onto utilities (`bg-signal-orange`, `px-margin`, `gap-space-md`). - **`@theme static`** — Tailwind v4 tree-shakes unused theme variables by default; `static` forces every token into the compiled output so the package stays consumable as plain CSS custom properties, including by the basemap derivation script. - **Radius at absolute zero** — every `--radius*` key is `0px`. The deprecated bare `--radius` key is set too, because that is the key v4 drives bare `rounded` from. - **Rams-forbidden primitives disabled at the key** — `--shadow`, `--shadow-inner`, `--drop-shadow`, and `--blur` are all set to `initial`, so `shadow-*` / `blur-*` utilities cannot compile from any source. - **Typography** — `--type-*` compound tokens follow the grammar `weight size/line-height family…`, each paired with a `-tracking` companion. - **`--shell-bg` / `--shell-on-surface`** — declared in `src/theme.css`, mapping the OS color scheme onto shell-level surfaces only; components and cartography stay Rams-constant under both Telegram themes. The four mockup screens under `stitch_minimalist_route_navigator/` are the read-only class vocabulary reference. `src/theme.css` lists them as explicit `@source` paths and excludes `scripts/` and `basemap/` from the auto-scan, so prose can never mint a utility class. The mockup files themselves are never edited. --- ## Audit `scripts/audit.mjs` treats "zero off-token values" as evidence rather than an eyeball claim. It parses `src/tokens.css` into an allowlist, then scans **rendered** CSS — `dist/rams.css` plus the inline `