Kontour UI#
The shared design-token and component layer for Kontour product interfaces.
@kontourai/ui
Use Kontour UI when you are building a Kontour product interface (Surface
Console, Flow Console, Survey Review Workbench, Station, or a custom operator
surface) and need to stay visually consistent with the --k-* token contract
without copying CSS by hand. If you are building a general-purpose application
with its own design system, you do not need this package.
Kontour UI ships three layers:
@kontourai/ui/tokens— CSS custom properties for any renderer, no framework required.@kontourai/ui/react— class-driven React primitives: display (Badge,Button,Panel,Metric,Progress,Skeleton,Spinner,StatusBadge,StatusBar,Topbar,TrustState,TrustBasis,Empty,ProductIcon,BrandMark), form controls (Field,Input,Textarea,Select,Checkbox,Toggle), and overlays/feedback (Dialog,Toast,ToastHost,Tooltip,Popover) that read the token contract.@kontourai/ui/elements— light-DOM web-component wrappers for vanilla products.
It also exports @kontourai/ui/contrast, a dependency-free module for runtimes that apply a
white-label theme from data: it rejects a brand-slot override that fails the thresholds the
shipped themes meet (see "Validating a white-label theme" in docs/consumer-guide.md).
For products that build markup as HTML strings, @kontourai/ui/trust-state exports the
trust-state chip without React or custom elements: renderTrustStateHtml(state, { label, detail, className }) returns the markup <k-trust-state> renders, with every interpolated value HTML-escaped, beside
the presentation data (the frozen tables trustStates, trustStateLabels, trustStateGlyphs, and the helpers trustStateFor and
trustStatePresentation). Style it with the tokens plus @kontourai/ui/trust-state.css, which
holds only the chip rules (see "Trust States" in docs/consumer-guide.md).
Package docs:
docs/consumer-guide.mdcovers React, custom elements, static HTML, theme classes, and vendored asset sync.docs/release-readiness.mdrecords the release and adopter verification matrix.docs/gallery.htmlis the canonical static gallery for React/custom-element parity.
Themes#
Apply one product theme class on a stable root element to set the product identity:
<main class="theme-survey">...</main>
| Theme class | Product | Brand (--k-brand, dark / light) |
Design intent |
|---|---|---|---|
theme-survey |
Survey / Review Workbench | #5ce0c6 / #107e6d teal |
Evidence-forward; minimal overrides on the default dark shell |
theme-console |
Kontour Console | #c9ff4a / #577800 lime-green |
Dense operator plane; condensed font, zero radius, high-contrast palette |
theme-flow |
Flow | #3890ae / #1f6f88 blue |
Process-transparency; cool accent on the default dark shell |
theme-station |
Station | #966aff / #7c3aed violet |
Draft violet accent adjusted for readable brand text on dark panels |
theme-surface |
Surface | #14a37a / #0f6b52 green |
Trust-state inspection; earthy-green accent on the default dark shell |
All themes support [data-theme="light"] for light-mode overrides. See docs/gallery.html for rendered examples of each theme in both modes.
React#
Import primitive styles once at your app root:
import "@kontourai/ui/react/styles.css";
Then use the primitives:
import { Badge, Button, Panel, StatusBadge, Topbar } from "@kontourai/ui/react";
Custom elements#
Load the element module for vanilla or web-component-based products:
<script type="module" src="./vendor/ui/dist/elements/elements/src/index.js"></script>
Then render:
<k-badge value="verified"></k-badge>
<k-status-badge status="connected"></k-status-badge>
<k-button label="Accept" variant="positive"></k-button>
Token import#
Import the full token layer:
@import "@kontourai/ui/tokens";
That one import is sufficient: it pulls in the base tokens, the theme classes, and the
@font-face rules for the brand faces. The individual files stay exported for consumers
that compose them by hand:
@import "@kontourai/ui/fonts.css";
@import "@kontourai/ui/tokens.css";
@import "@kontourai/ui/themes.css";
Skipping fonts.css gives you the --k-font-* variables without the faces they name, and
the surface silently renders in fallback typography.
Fonts#
The brand faces ship inside the package as woff2 and are declared with real @font-face
rules — nothing is fetched from a third party at render time. A product with
default-src 'self', the right posture for a tool handling someone's documents, gets the
brand typography with no CSP allowance and no network request.
| Token | Family | Weights | License |
|---|---|---|---|
--k-font-display |
Fraunces (variable) | 100–900 | SIL OFL 1.1 |
--k-font-ui |
Hanken Grotesk (variable) | 100–900 | SIL OFL 1.1 |
--k-font-mono |
IBM Plex Mono | 400, 500, 600 | SIL OFL 1.1 |
Coverage is latin + latin-ext; anything outside those ranges falls through to the fallback
stacks in tokens.css. Licenses ship alongside the files, and tokens/fonts/README.md
records provenance. node scripts/vendor-fonts.mjs regenerates both the files and
tokens/fonts.css.
For static HTML consoles served without a bundler, add a local package dependency and copy the CSS assets into the product's asset tree during build or setup:
{
"devDependencies": {
"@kontourai/ui": "file:../ui"
}
}
<link rel="stylesheet" href="./vendor/ui/tokens/index.css">
<link rel="stylesheet" href="./vendor/ui/react/styles.css">
Products should style components with --k-* variables and treat the product theme class as the product identity boundary. Domain status words map to the shared semantic scale: positive, caution, negative, active, and neutral.
Checks#
npm run check:tokensverifies the token/theme contract and keeps React styles token-only.npm run check:contrastrates every shipped theme and mode against the WCAG pairs, through the same functions and thresholds@kontourai/ui/contrastexports;npm run test:unittests that module.npm run check:marksholds every mark inicons/to the static gates: generated from the path modules, valid and self-contained SVG, plain in-frame geometry,currentColoronly, no effects, and shape and size ceilings. The browser suite adds the pixel gates (ink at 16/24/32px that holds 3:1 on the shipped surfaces, ink structure, favicon rendering, distinctness). These measure ink, not whether a mark is recognisable; that is a human review (seeDESIGN.md).npm run check:tree-shakingbundles the built package with esbuild and fails if importing one primitive pulls in the brand marks, or if a side-effect import of the elements entry stops registering them.npm run check:wordmark(opt-in; needs Python with fontTools and brotli) re-derives the wordmark outline from the vendored Fraunces file and compares it with the committed path data.npm run test:unitruns the same comparison when that tooling is present and skips it otherwise.npm run check:exportsbuilds and verifies package export targets, ESM output, declaration files, package naming, and framework-free element output.npm run check:readinessverifies release docs, gallery, package metadata, and adopter contract markers.npm run check:packpreviews package contents withnpm pack --dry-run.npm run verifyruns all package readiness checks.