roadmap.
what we're building, what we're thinking about, and what's already shipped. dates slip — we'll be honest about it. ideas are a public sketchpad; the ones marked candidate have a real shot at the next version.
Enhance accessibility — runtime services + missing primitives
KjReducedMotion service, live-region pile-up coalescer, skip-to-content, focus-return on async routes, high-contrast detection. Cheap to add, hard to retrofit.
The behavioural primitives are solid; these fill the runtime-service gap that every component currently solves on its own: - **`KjReducedMotion` service** — a single signal you read once, drives every conditional animation across the app. No more per-component media-query queries. - **`KjLiveRegion` pile-up handler** — coalesce announcements within a debounce window so AT users don't get spammed by ten "row added" messages in a second. - **Skip-to-content link primitive** with auto-targeting of the main landmark. - **Focus-return on async route** — when a route loads via `loadComponent`, restore focus to the link that triggered the navigation automatically. - **High-contrast mode detection** signal + a `provideKjHighContrastFallback({ when: 'forced-colors', use: 'kouji-hc' })` provider to auto-swap to a high-contrast theme.
candidate Command palette pro — pinned, recent, groups, async, multi-level
The next layer on top of the shipped command palette — frecency ranking, collapsible result groups, per-item shortcuts, async loader with skeleton, and drill-in navigation.
The base palette is shipped; these are the high-leverage extensions on top: - **Pinned + recent items** at the top, automatic frecency ranking, swappable persistence adapter. - **Result groups** with collapsible headers and per-group icons. - **Per-item keyboard shortcut display** using the existing `kj-kbd` component, with optional global hotkey wiring. - **Async loader** with debounce, cancel-on-keystroke, and skeleton rows — today it's a single-pass list. - **Multi-level navigation** — drill into a section (`Settings → Notifications → …`) within the same palette frame, back-arrow to return, breadcrumb at the top. - **Inline action preview** — hovering an action shows a small preview of the change it would make.
Date / Time / Calendar polish
Range presets, multi-date select, time-zone awareness, inline calendar mode, faster year-month navigation — the small wins that turn a working date picker into a great one.
- **Range presets** ("Last 7 days", "This quarter", "Year to date") as a slot on the range picker. - **Multi-date select** mode for picking a non-contiguous set of days. - **Time-zone-aware** date display + a `provideKjTimezone()` token that participates in the i18n locale provider. - **Inline (non-popover) calendar mode** for booking-style flows where the calendar is the primary surface, not a dropdown. - **Year-month jump scroller** — currently navigation through months is one-month-at-a-time; a virtualized year-month grid lets you jump.
Docs site v2 — sandbox, recipes, per-example theme picker
Live "try it" sandbox per example, theme picker per example, recipes for multi-component patterns, migration guides, manifest-driven API tables on every page.
The docs already have working examples and a typed manifest; this turns them into something developers actually open daily: - **"Try it" sandbox** per example — StackBlitz / WebContainer embed (or a built-in editor) so you can tweak the code and see the change without leaving the page. - **Theme picker per example** — every code preview gets a small theme dropdown; switch instantly to see "yes, this *does* look right in cyberpunk too". - **Recipes section** — multi-component patterns (dashboard with KPI cards + DataTable + filter drawer, sign-in flow with multi-step form + password rules, etc.), each with full source. - **Per-version migration guides** auto-generated from the codemod-CLI's transform metadata. - **Component-API table generated from the manifest** — props / events / slots / a11y contract on every component page, always in sync with the source.
candidate Form-flow primitives — wizard, conditional fields, auto-save
The base inputs are shipped, but the form *flow* is thin. This is the missing 60% — multi-step, conditional, async-validated, auto-saving, dirty-tracking forms.
The pieces every real form ends up reimplementing: - **Multi-step wizard** — `kj-form-step` + progress indicator, per-step validation gates, "you've got unsaved changes" guard when navigating away mid-flow. - **Conditional fields** — declarative `kjShowWhen` directive driven by other field signals, no boilerplate `effect()` per dependency. - **Async validation orchestration** — debounce, cancel-on-input, race-aware. The kind of plumbing every form currently rewrites. - **Auto-save** — debounced submit + a small status pill (`saving…` → `saved` → `error, retry`). - **Dirty / pristine tracking** + a `kjConfirmOnLeave` route-guard primitive so navigation away from an in-progress form prompts before discarding.
candidate Form schema engine
Generate a full kj form from a TypeScript schema. JSON-schema interop for backend reuse.
Not committed. Need to validate the API against 3+ real apps first.Define a schema in TS, get a fully-validated form with the right kj components per field type. Inspired by Formly but tighter integration with our typed inputs (so a `z.number()` becomes `<kj-number-input>` and not a hint to compose one manually). Open question: does it belong in `@kouji-ui/core` or a separate `@kouji-ui/forms` package?
candidate Motion preset library
Named entrance/exit/transition presets. Composable, reduced-motion aware, designable in pure CSS.
Move beyond per-component animation strings to a kit of named presets — `slide-up-fade`, `scale-spring`, `drift-in`, `pulse-attention`. Each preset is a CSS custom-property bundle that any overlay/drawer/dialog can opt into. All presets respect `prefers-reduced-motion`. Designers can author new presets without touching TS.
Dialog + Drawer power features
Resizable dialogs, fullscreen toggle, multi-dialog stacking, side-peek drawer mode, "don't show again" pattern — the edge cases real apps run into once they've outgrown the basics.
The overlay system is solid for the 80% case. These are the 20% that everyone hits eventually: - **Resizable dialog** — drag a corner / edge to enlarge for content-heavy modals (rich-text editors, log viewers). - **Fullscreen toggle** as a built-in header action; remembered per-dialog-id. - **Stacked dialog management** — z-index, focus, escape-key, and backdrop orchestration when a confirm-popup opens from inside a dialog. Currently a footgun. - **Side-peek drawer mode** — drawer that doesn't backdrop-block the underlying surface, for inspectors that pair with a list. - **"Don't show again"** option pattern, baked in with a swappable persistence adapter.
Select / Combobox / Tree-select polish
Virtual scrolling for huge option sets, async loaders with cancellation, free-text "create new" entries, recent-selections section, sticky group headers.
The dropdown family covers the common case but breaks down at scale: - **Virtual scrolling for huge option sets** (10k+ options) — today the listbox renders every option upfront. - **Async option loaders** with cancel-on-keystroke and skeleton rows during the request. - **"Create new" inline option** for tag-style multi-select (free-text + suggest from the existing options). - **Recent selections / favorites** section at the top of the listbox, swappable persistence adapter. - **Sticky group headers** when scrolling long grouped lists — the group label stays visible until you scroll into the next group.
candidate Theme generator pro — exports, share, diff, history
Extend the existing visual generator with multi-target exports, shareable draft URLs, two-theme diff, undo history, and a one-click "open PR" against a repo.
The generator is already the killer feature; this is the natural follow-on: - **Export targets**: CSS file (today), W3C `design-tokens.json`, Tailwind config, Style Dictionary, iOS/Android-flavored token bundle. Same draft, multiple outputs. - **Import from existing CSS** — paste a `.css` file with `--my-var` declarations, kj remaps onto its own token slots and seeds a draft. - **Side-by-side diff** — pick two themes, see which tokens diverge and which contrast pairs change tier. - **History timeline** — undo / redo / named snapshots, all in-session. - **Live share URL** — encode the draft state in a URL hash; paste to a teammate, they see the exact draft. - **Open PR** — given a GitHub repo URL, kj generates a PR adding the theme file. The theme contract test makes the PR safe to merge.
Toast system — actions, progress, queues, history
Toast is shipped as a primitive; this promotes it to a notification system with action buttons, progress, replace-by-id, overflow strategies, and a history panel.
- **Action toasts**: a slot for undo / retry / dismiss buttons (today: text only). - **Progress toasts** — a thin progress bar inside the toast that updates while a long-running operation runs. - **Group / replace by id** — same id collapses repeats instead of stacking the same message four times. - **Queue overflow strategy** — `drop-oldest` / `compact` / `merge-by-channel`, configurable per app. - **Persistent toasts** that survive route changes (separate channel from transient ones). - **History panel** — opt-in, replays the last N notifications; pairs nicely with the audit-log story.
candidate Per-theme visual regression harness
Playwright + screenshot snapshots × 13 themes per component. Catches design drift on every PR.
Storage cost of N components × 13 themes × multiple states is real — need a sampling strategy.Run the playground of every component through Playwright across all 13 themes, capture a screenshot per state (default / hover / focus / disabled / loading), and diff on PRs. Catches the "I styled it for kouji but it's broken on terminal" class of regression — currently caught manually, sometimes weeks late.
React port
Same components, same themes, React APIs.
Not soon. Want kj's Angular story to be airtight first.A `@kouji-ui/react` package that exposes the same component surface with React idioms (`ref`, `children`, controlled inputs). The headless behaviours port directly — focus traps, portals, ARIA — but the rendering layer is reimplemented in React. The theme CSS is framework-agnostic, so it'd reuse the existing `@kouji-ui/themes` package unchanged.
Per-component bundle budgets in CI
Each wrapper locks a maximum gzipped size. CI fails on regression. Tree-shaking lints catch accidental side-effects.
Per-component bundlewatch config. Core package target: 8kb gzipped, no exceptions. Wrappers budget against their headless cousin so the styling layer is always quantifiable. Tree-shaking lints catch `import` side-effects (CSS-in-JS, eager DI) that would otherwise pull in the whole package.
Chart primitives — engine-adapter + first-party simple charts
A KjChartHost primitive handles theme tokens, a11y, reduced-motion, and resize wiring. First-party simple charts (line/bar/area/donut/sparkline) ship on top of it; consumers swap in ECharts / visx / d3 / Recharts for heavier work.
Charts are a huge surface area, so kj ships an **adapter primitive** rather than trying to win every chart category. The kj-side wiring lives in one place; engines plug in. - **`KjChartHost`** — theme-token resolution, dark/light auto-swap, `ResizeObserver` plumbing, `prefers-reduced-motion` opt-out, a11y description + live-region announcements. Every chart composes this. - **First-party simple charts** for the common cases — line, bar, area, donut, sparkline — built on `KjChartHost` with ground-up SVG. Themed via tokens, axe-clean, keyboard-navigable data points. Covers the dashboard-KPI use case without a heavy dep. - **First-party adapters** for ECharts, visx, Recharts, and d3 — each adapter consumes `KjChartHost`'s resolved tokens and renders into its slot. Pick the engine that fits the chart type. - **BYO adapter** — small contract (`registerAdapter({ name, render, dispose, onResize, onThemeChange })`) so any chart engine can hook into the kj wiring. - **Stable a11y story** — every chart, regardless of engine, exposes the same `aria-label` summary + keyboard-navigable data-point list (built on the existing `KjLiveRegion` + `KjRovingTabindex`).
i18n — typed translation strings
KjTranslate directive + KjTranslateService for every visible string in kj — toast close, dialog dismiss, pagination labels, a11y live-region announcements. Tree-shakable per-locale catalogs.
The visible-text surface across the library, made replaceable per locale: - **Built-in keys for every internal label** — toast close, dialog dismiss, pagination "page X of Y", a11y live-region announcements ("sort applied, N rows", "selected", "page 3 of 12"). No more strings hard-coded in TypeScript. - **Per-locale message catalogs** — drop a `@kouji-ui/i18n/fr.ts` next to the locale provider; missing keys fall through to the source language so the UI never blanks out. - **ICU-style placeholders** — `{count, plural, one {# item} other {# items}}`. Pluralisation + gender + select all out of the box. - **Tree-shakable imports** — catalogs ship as `import`-able modules so consumers only bundle the languages they actually use. - **Consumer keys** — a `KjTranslate` directive lets app authors use the same catalog system for their own strings, with a typed key namespace per scope. Separate from the locale-provider ticket — this one is about *the words themselves*, not about how data renders across locales.
Locale provider — formatting, calendars, RTL switch
One provider feeds every locale-sensitive primitive — DatePicker, NumberInput, TimePicker, currency formatting, calendar systems, the RTL direction switch.
`provideKjLocale({ locale, direction, calendar, currency })` (or the implicit `Intl.Locale` resolved from the browser) propagates through DI. Every locale-sensitive primitive picks it up — DatePicker, NumberInput, TimePicker, the RTL switch, all `Intl`-backed formatting. No per-component locale prop drilling. Defaults to `Intl.DateTimeFormat` so anything Angular's `LOCALE_ID` already supports works out of the box. Separate from the translation-strings ticket — this one is about *how the same data renders* across locales, not about translating visible-text labels.
Mobile-first interaction patterns
Bottom sheet variant, swipe-to-reveal list rows, action sheet, gesture-aware overlays. Cohesive mobile API on top of existing primitives.
The drawer primitive already covers the right-side and left-side cases. v0.2 fills in the mobile gap: a bottom-sheet variant with drag-to-dismiss, swipe-to-reveal actions on `kj-list` rows, and a system-style action sheet built on the dialog primitive. Each piece reuses existing focus-trap + portal layers — no new behavioural surface, just composition.
RTL support across the board
Right-to-left layout for Arabic, Hebrew, Farsi. Mirrors overlays, calendars, sliders, pagination, breadcrumbs.
Layout primitives switch via `[dir="rtl"]` on the host. Icons and chevrons mirror automatically; calendars/sliders/progress get a writing-mode-aware traversal; the toast layer flips its default positioning. Visual regression covers two sample docs (Arabic + Hebrew) across every shipped theme.
TanStack-backed DataTable with a11y baked in
Production table wrapping @tanstack/angular-table — sort, filter, multi-sort, column resize + pinning, virtual scroll, server mode, bulk actions, persisted state — wrapped in a WAI-ARIA grid pattern.
`KjTableDirective` wraps `@tanstack/angular-table` so consumers get TanStack's row model, sort/filter/group plumbing, and column virtualization for free. The styled wrapper layers the WAI-ARIA Grid pattern on top: `role="grid"` with proper row/cell roles, `aria-rowindex` / `aria-colindex` for virtualized rows, `aria-sort` reflection on sortable headers, full keyboard nav (Arrow / Home / End / PageUp-Down, type-ahead), and a live-region announcement when sort or filter mutates the row set. **Must-haves baked into v1** (so consumers don't all reinvent the same thing): - **Column pinning** (sticky left / right) and **row pinning** (sticky top / bottom summary rows). - **Persisted state** — sort / filter / column widths / visibility, per user, via a swappable storage adapter (default in-memory; localStorage opt-in). - **Density toggle** (compact / standard / comfortable) wired to theme spacing tokens. - **Bulk-action toolbar** — appears when ≥1 row is selected; host fills an action slot, kj ships the clear-selection chip + selection count. - **Column visibility menu** built on `kj-dropdown-menu`. - **Export to CSV / JSON / clipboard** — generic, uses the column accessors so it works regardless of cell template.
v0.1 API stability audit
Lock the public surface, deprecate experimental APIs, document migration paths. The big "exit alpha" moment.
Every public input / output / token gets audited for naming, defaults, and CVA wiring. Anything still subject to change is marked `@experimental`. A new "stability" badge ships in the docs sidebar, and breaking changes after v0.1 follow the SemVer + Angular update contract.
Headless primitive foundation
a11y helpers, overlay+portal, focus management, live region, roving tabindex — the directives every other component composes.
`@kouji-ui/core` ships the behavioural surface every wrapper builds on: `KjAriaLabelledBy` / `KjAriaDescribedBy`, `KjFocusTrap`, `KjLiveRegion`, `KjVisuallyHidden`, `KjRovingTabindex`, the overlay primitive (CDK Portal under the hood), and a shared dropdown-menu / popover / tooltip layer. Every behavioural detail follows the WAI-ARIA Authoring Practices; verified against NVDA, JAWS, VoiceOver.
kj typography surface
`.kj-prose` container + five tone directives (kjLead, kjMuted, kjCode, kjBlockquote, kjTruncate).
Drop `.kj-prose` on a container and every descendant `h1`–`h6`, `p`, `a`, `blockquote`, `code`, `pre`, `ul`, `ol`, `dl`, `figure`, `table` follows the kj type system — no per-element class soup. The five attribute directives reflect `data-tone` so a hand-typed `data-tone="muted"` reads the same as `kjMuted`.
Base components + overlay system
dialog, drawer, popover, tooltip, dropdown-menu, toast, confirm-popup — all sitting on one shared overlay primitive with portal, focus trap, scroll lock, and aria wiring.
One overlay primitive (`KjOverlay` + `KjOverlayPanel` + CDK Portal under the hood) underpins every floating surface: dialog, drawer, popover, tooltip, dropdown-menu, toast, confirm-popup, command-palette. Each composes the shared primitive for portalling, focus trap, scroll lock, escape-to-close, and the WAI-ARIA contract for its role — so positioning, animation defaults, and a11y semantics never drift across the family.
Command palette + global Cmd-K search
Modal command palette with substring + fuzzy filters and a global Cmd-K hotkey listener. Powers the docs search.
`<kj-command-palette>` ships modal-by-default with `[(kjOpen)]`, `[kjHotkey]` for the global chord, and either `[kjItems]` + `<ng-template kjCommandPaletteItemTemplate>` or `<kj-command-item>` projection. Backdrop, animations, ESC + backdrop-click to close, auto-focus, and a customisable keyboard-hint footer are baked in.
Specialty data-input surface
cascade-select, tree-select, input-mask, input-otp, input-group + addon. All composable and CVA-wired.
Five new input primitives that compose on top of `kj-input` / `kj-select`: - `<kj-cascade-select>` — hierarchical flyout-chain picker. - `<kj-tree-select>` — flat-rendered hierarchical dropdown with single + multi-select modes. - `<kj-input-mask>` — fixed-format input with built-in `NG_VALIDATORS`. - `<kj-input-otp>` — N-cell one-time-code with auto-advance, paste distribution, completion announcement. - `<kj-input-group>` + `<kj-input-group-addon>` — addon-wrapped fields with `aria-labelledby` composition.
Visual theme generator
Fork any theme, tweak tokens, see eight preview scenes update live, score contrast against WCAG, then export or save.
Lives at `/theme-generator`. Fork any of the 13 base themes, edit any token, watch eight preview scenes update instantly. The a11y panel checks contrast on every fg/bg pair, simulates the three colorblindness gamuts, and runs a focus-ring test pass.
13 ship-ready themes
kouji, dark, light, retro, cyberpunk, corporate, sakura, bauhaus, dune, mint, forest, nord, terminal.
Every theme ships a full token contract (color, type, shape, spacing, motion) and is swapped at runtime via `[data-theme]` on `<html>`. Themes are tree-shakable — only the CSS files you import ship. A `themes.spec.ts` contract test verifies every shared-layer token is declared in every theme.