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.
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.
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.
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.
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.
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.
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?
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.
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.
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.
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 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.
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.
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.
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.
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`).
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.
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.
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.
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.
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.
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.
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-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`.
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.
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.
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.
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.
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.