A CSS-first identity layer for services, tools, and reports that need provenance. It works in plain HTML, modern frameworks, and print/PDF, with no component runtime to adopt and zero runtime dependencies.
The sharpest lane is report and tooling UI: static/PDF reports, evidence, provenance, system state, dense workbenches, and explanatory figures. The default CSS bundle stays the shared service identity, not the whole package: app shells, navigation, forms, tables, feedback, overlays, prose, motion, and the token system that lets apps feel related without sharing a component runtime.
The look is deliberately restrained: a mostly neutral canvas, one core accent for emphasis, dot-matrix display type, and hairline borders. Status colors, display-expression tokens, and data-viz colors are separate governed tiers, not extra brand accents. Opt-in skins re-point the root accent; Mermaid, D2, and Vega bridges use resolved renderer theme data instead of live CSS variables.
- Building an app or service shell? Use the quick start below, then the framework guide for your stack.
- Building a report, audit, or provenance view? Start with docs/reporting.md and docs/frontier-primitives.md.
- Maintaining or integrating the package? Read
docs/architecture.md,
docs/stability.md, and
llms.txt.
The demo is the kitchen sink — every component, light/dark, RTL, live theming. The service shell shows the shared app identity; the static report shows the same system under a no-build, no-JS, Chromium-PDF-ready constraint.
@ponchia/ui ships its design as CSS, not components. You drop in one stylesheet
and style with semantic ui-* classes; an optional thin layer of typed
class-name recipes and SSR-safe vanilla behaviors sits on top for the few things
that genuinely need JS (theme persistence, dialogs, toasts, disclosure).
The guiding principle is that colour is rationed and structure carries
meaning — layout, type weight, and the hairline do the work before a hue does,
and the accent is a spotlight, not a paint bucket. Because framework rules live
in @layer bronto, ordinary un-layered consumer CSS overrides them without a
specificity fight. Print and reduced-motion rules still use !important
deliberately where the framework must neutralize animation or layout at a media
boundary.
It ships a complete, accessible standard component set because that is the identity layer every service consumes. Its sharper edge is the opt-in tooling, analytical, and communication layer sketched above. The line that holds it together: each primitive owns only its visual grammar and pure geometry — no chart engine, no domain state, no hit-testing. See docs/frontier-primitives.md for the thesis.
npm i @ponchia/uiOr drop it in with no build step, straight from a CDN:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.11/dist/bronto.css">1. Load the CSS. One flattened, minified default CSS bundle — the standard component set, one request (~89 kB raw / ~15 kB gzip) — that is dist/bronto.css, not the whole package tarball:
@import '@ponchia/ui'; /* via a bundler */<!-- or a plain <link>, no bundler -->
<link rel="stylesheet" href="/node_modules/@ponchia/ui/dist/bronto.css">Prefer source leaves through a bundler? Use
@import '@ponchia/ui/css'(a thin@importfan-out) instead. Both resolve the Doto@font-facewith relative URLs, so there's no/fontspath assumption.
The size above is only
dist/bronto.css. The opt-in analytical and report layers —report.css,dataviz.css,figure.css,annotations.css,legend.css,interval.css,clamp.css,highlights.css, and the rest — are not in the default CSS bundle; link each one you need fromdist/css/. For LLM-authored static reports see docs/reporting.md.
2. Write markup with ui-* classes (primary is the default button; modifiers are opt-in):
<button class="ui-button">Save</button>
<button class="ui-button ui-button--ghost">Cancel</button>
<div class="ui-card">
<span class="ui-eyebrow">Status</span>
<span class="ui-badge ui-badge--success">Online</span>
</div>3. (Optional) typed recipes — build class strings in JS/TS:
import { ui, cx } from '@ponchia/ui/classes';
ui.button({ variant: 'ghost' }); // → "ui-button ui-button--ghost"
ui.meter({ tone: 'warning' }); // → "ui-meter ui-meter--warning"4. (Optional) behaviors — SSR-safe vanilla JS, each returns a cleanup function:
import { initThemeToggle, initDialog, toast } from '@ponchia/ui/behaviors';
initThemeToggle(); // wires [data-bronto-theme-toggle] + localStorage
initDialog(); // native <dialog> glue: [data-bronto-open] / [data-bronto-close]
toast('Saved', { tone: 'success' }); // body-anchored stack, no markup neededBehaviors cover theme persistence, disclosure, dropdown menus, native-<dialog> modals/drawers, tabs, combobox, form validation, table sort, carousel, source backrefs and toasts — wired by data-bronto-* attributes.
5. (Optional) display glyphs — a 71-glyph dot-matrix icon set:
import { renderGlyph } from '@ponchia/ui/glyphs';
el.innerHTML = renderGlyph('search', { label: 'Search' }); // role="img"
// or drop a placeholder and expand it: <span data-bronto-glyph="check"></span>
// import { initDotGlyph } from '@ponchia/ui/behaviors'; initDotGlyph();Arrows, chevrons, check/close/plus/minus, search/menu/gear, info/warning/bell/lock, home/user/heart/star/spark and circle-family marks are rendered on the same .ui-dotmatrix primitive and re-skinned by the --field-dot* tokens. Default dot look is for display sizes; pass { solid: true } to fuse the cells into a crisp pixel glyph that works as an inline icon down to ~16px. For dense lists/tables, renderGlyph(name, { render: 'mask' }) emits one .ui-icon element backed by an internal CSS mask.
- Primitives — buttons, cards, chips, badges, links, key/value,
ui-num, avatars. - Forms — inputs, select, textarea, search, switch, checkbox,
ui-input-icon,ui-input-group. - Feedback — alert/callout, toast, tooltip,
ui-progress(task) andui-meter(measured value). - Overlay — modal + drawer on native
<dialog>, dropdown menu,ui-carousel+ui-lightbox(gallery, user-driven — not an auto-slider). - Disclosure & nav — tabs, accordion, segmented, breadcrumb, pagination,
ui-steps,ui-timeline,ui-pagehead,ui-kbd. - Shells — a service/app shell (
ui-app-*) and a content/marketing site shell (ui-site*,ui-container). - Prose —
.ui-prosestyles raw, unclassed semantic HTML (Markdown / CMS / LLM output) with zero classes. - Analytical & communication primitives (opt-in) —
@ponchia/ui/css/analytical.css: figure stages, SVG annotations (subject/connector/note), standalone legends/data-keys, text/evidence marks, leader-line connectors (+ a pure@ponchia/ui/connectorsgeometry kernel), a guided-focus spotlight, a crosshair/readout, a cross-cutting selection vocabulary, and CSS Custom Highlight API highlights. Each owns its visual grammar + pure geometry and refuses scales/state/hit-testing — figures that explain themselves, not a chart engine. The richer annotation engine lives in@ponchia/annotations; this package keeps only the dependency-free Bronto helper surface. Plus standalonesource/provenance (trust), interval, clamp, and lifecyclestateleaves. - Reports (opt-in) —
@ponchia/ui/css/report-kit.cssfor a complete static/PDF report vocabulary, or@ponchia/ui/css/report.cssplus only the leaves a narrow report uses. Covers, decisions, claims, sections, severity-labelled findings, evidence packets, evidence ledgers, action registers, source-card bindings,ui-figurecomposition, intervals, bounded excerpts, chart wrappers and print utilities. - Motion & dots — the dot-matrix motif kit: dot grid, status dots, dot loaders, the orbital spinner, matrix reveal — all reduced-motion aware.
- Glyphs —
@ponchia/ui/glyphs, a 71-glyph dot-matrix icon set on the.ui-dotmatrixprimitive (display marks + crispsolidinline icons + one-node.ui-iconmask rendering). - Colorways (opt-in) —
data-bronto-skin="amber-crt | phosphor-green | e-ink": a root-level recolour of the one accent (OKLCH, per-theme, contrast-gated), never in the default bundle. - Data-viz (opt-in) — a colourblind-safe chart palette (
--chart-*+ dot-matrix pattern fills, resolved hex incharts.json), gated under simulated protan/deutan/tritan vision. Charts only, never UI chrome.
Full generated catalog of every class: docs/reference.md. The decision guide (which primitive when): docs/usage.md.
Core DOM accent surfaces derive from a single --accent variable via color-mix(). Re-brand the entire app — both light and dark — with root-level, per-theme overrides:
:root { --accent: #2f6df6; } /* whole app blue */Scoped --accent overrides recolor direct accent uses in that subtree, but the
derived family (--accent-text, --accent-soft, focus/dot/ramp tokens) is a
root-level skin contract. Use the full per-theme recipe in
docs/theming.md for production rebrands.
Buttons, focus rings, dot motifs, accent borders and soft fills follow the root accent automatically. Light/dark is data-theme="light" / "dark" on <html> (defaults to prefers-color-scheme); data-density and data-contrast give density and contrast presets. A full re-skin (radius, display face, dot density, surfaces) is a handful more token overrides, while status colours and data-viz colours remain their own governed tiers — the default monochrome look is one skin, not the architecture.
One system, many skins. That the knob is real isn't a claim — it ships: drop in @ponchia/ui/css/skins.css and set data-bronto-skin="amber-crt | phosphor-green | e-ink" on <html> for a complete, contrast-gated recolour (the derived accent family, focus ring, dot-matrix and glyphs all follow). Colour is governed in tiers — neutral canvas · one accent · locked status · display expression · opt-in data-viz — so it always earns its place; the full constitution is ADR-0001.
When you change
--accent, contrast becomes yours: verify your hue in the theme playground (it shows the derived family and computed WCAG ratios). Mermaid, D2, and Vega helpers emit resolved theme data for those renderers; they do not live-reskin from a later CSS--accentoverride. Full contract: docs/theming.md.
Not an afterthought — a gate. Every contractual token pairing has a declared WCAG 2.1 conformance level the build fails below (docs/contrast.md, regenerated + CI-checked), and the shipped colorways are held to the same floors. The data-viz palette is gated for distinctness under simulated protanopia/deuteranopia/tritanopia, and never relies on colour alone (dot-matrix pattern fills). APCA Lc is published advisory alongside WCAG. Components ship ARIA-correct markup and SSR-safe keyboard behaviors; prefers-reduced-motion, prefers-contrast and forced-colors are all honored.
The CSS is the framework, so it works with React, Svelte/SvelteKit, Astro, Vue, Solid, Qwik or plain HTML — there's no component runtime to adopt. The optional classes and behaviors entrypoints pull in no UI framework and are SSR-safe. For React, Solid and Qwik there are also optional thin bindings — @ponchia/ui/react, @ponchia/ui/solid and @ponchia/ui/qwik wrap the behaviors as hooks (useDialog, useToast, …); react/solid-js/@builder.io/qwik are optional peer deps. Svelte and Vue get dependency-free lifecycle adapters too: @ponchia/ui/svelte exports actions, and @ponchia/ui/vue exports directives/plugin helpers over the same behavior layer.
Per-framework getting-started guides + runnable example apps live in the repo:
| Stack | Guide | Example |
|---|---|---|
| Vanilla / Vite / plain HTML | vanilla.md | examples/vanilla-vite |
| Astro | astro.md | examples/astro |
| SvelteKit | sveltekit.md | examples/sveltekit |
| Vue | vue.md | examples/vue-vite |
| React | react-solid.md | examples/react-vite |
| Solid | react-solid.md | examples/solid-vite |
| Qwik | react-solid.md | examples/qwik-vite |
| Tailwind v4 bridge / cascade-layer interop | tailwind.md | examples/tailwind-vite |
- Tokens as data —
import tokens, { themeColor, cssVars } from '@ponchia/ui/tokens'(plustokens.json, W3C DTCGtokens.dtcg.json,tokens/resolved.jsonfor concrete values in canvas/SVG/MapLibre, andtokens/figma.variables.jsonfor local Figma Variables import/sync scripts). - Chart colours for dashboards —
import charts from '@ponchia/ui/charts.json' with { type: 'json' }in Node ESM, or the same path through a bundler JSON import (resolved hex per theme; series 1 = your accent) plus the opt-in@ponchia/ui/css/dataviz.css. - Static reports for LLMs — add
@ponchia/ui/css/report-kit.cssfor the complete report vocabulary, or@ponchia/ui/css/report.cssplus the specific leaves a smaller report needs. Sidecar claim/source contracts can validate against@ponchia/ui/schemas/report-claims.v1.schema.json. Full cookbook:docs/reporting.md. - Modern-platform motion — overlays (modal/drawer/popover), toasts and the
<details>accordion animate in and out with zero JS (@starting-style+allow-discrete,::details-content+interpolate-size). Progressive-enhancement extras:.ui-scroll-progress/.ui-scroll-reveal(scroll-driven, no JS) and.ui-vtfor View Transitions. All degrade to a static end-state and respectprefers-reduced-motion. For smooth cross-document navigations, add the document-global one-liner to your own top-level (unlayered) CSS:@view-transition { navigation: auto; }. - Editor IntelliSense — point VS Code at the shipped custom-data file so every token autocompletes in
var(--…):{ "css.customData": ["node_modules/@ponchia/ui/classes/vscode.css-custom-data.json"] } - For AI coding agents — the package ships
llms.txtat its root plusdocs/reference.md,docs/usage.md,docs/reporting.md,docs/theming.md,docs/contrast.md,docs/stability.md,docs/package-contract.md, the color constitutiondocs/adr/0001-color-system.mdand theCHANGELOGinside the tarball, so an offline agent has the full API and rationale without guessing.
The package root is CSS-only. Use
@import '@ponchia/ui'in CSS, orimport '@ponchia/ui'only as a CSS side-effect import in a CSS-aware bundler (Vite, Astro, SvelteKit, webpack). Do not import the package root from Node/runtime JS. CSS helper subpaths are explicit too, including/tailwindfor the Tailwind v4 theme/variant bridge. JS entrypoints are explicit subpaths:/tokens,/classes,/behaviors,/glyphs,/annotations,/connectors,/react,/solid,/qwik,/svelte,/vue,/skins,/charts,/mermaid,/d2, and/vega. JS subpaths are ESM-only. CommonJS consumers should use dynamicimport('@ponchia/ui/behaviors').
Recent-evergreen, by design. The framework targets the modern web platform — cascade layers, :has(), color-mix()/oklch(), logical properties, native <dialog>, and the 2026 interaction primitives (@starting-style, transition-behavior: allow-discrete, light-dark()). Floor: Chrome/Edge 125+, Safari 18+, Firefox 129+ (early–mid 2025). No fallback ships below this — pin an older tag if you need it. See ADR-0002 for the scope/greenfield rationale.
Pre-1.0 and deliberately so. Until 1.0.0, breaking changes ship in the minor (0.x.0); patches (0.x.y) are always non-breaking. Pin with the patch range — at 0.x, ~0.6.0 (and equivalently ^0.6.0) resolves to >=0.6.0 <0.7.0, giving you safe patches while holding back the next breaking minor. Every breaking change is called out under a BREAKING heading in the CHANGELOG with a migration note.
Contractual (changes are breaking): token names and documented token roles, .ui-* class and recipe names, data-bronto-* attributes, exported behavior/glyph/binding function names and each behavior's cleanup contract. Not contractual (may change any release): exact token values and generated colour math outputs (visual tuning) unless a doc explicitly says the value is stable, plus internal leaf-file / @layer boundaries. See docs/stability.md for the semantic public-surface matrix and docs/package-contract.md for the generated export/file/provenance inventory.
Release candidates publish to the next dist-tag, never to latest — opt in with npm i @ponchia/ui@next to try an upcoming version early. A plain npm i @ponchia/ui only ever resolves a stable release.
- Live demo · Theme playground
- Class reference · Usage guide · Theming · Contrast · Color system (ADR-0001) · Scope & 2026 baseline (ADR-0002)
- CHANGELOG · Roadmap · Contributing · Repository layout
MIT © Ponchia.
The bundled Doto font (fonts/*.woff2) is © 2024 The Doto Project Authors and licensed separately under the SIL Open Font License 1.1 — see fonts/OFL.txt.

