forty-cdk

Headless UI primitives for modern Angular.

forty-cdk ships the part that is hard to get right — roles, keyboard interaction, focus management and state — and none of the part that is yours. No styles, no theme to override, no NgModule.

Not a component library: nothing here is painted, so there is nothing to theme — you build your own design system on top of behaviour that is already accessible.

$npm install forty-cdk

v0.31.0Angular ^22.0.1MITpre-1.0

Liveforty-cdk/switch
data-state="checked"
<button forSwitch type="button"
        [(checked)]="on">
  <span class="thumb"></span>
</button>

One directive on your own <button>. It carries role="switch", keeps aria-checked in step, toggles on Space and Enter, and reflects its state for your CSS. The appearance above is thirty lines of ordinary CSS.

Why forty-cdk

Styleless by design

No CSS ships. You style your own class against the data-* state each piece reflects and the --for-* properties it measures.

Composed, not configured

A primitive is a set of directives you arrange in your own markup. They find each other through DI, so you can wrap any of them.

Accessibility is the API

Every primitive names the WAI-ARIA APG pattern it implements: roles, live ARIA, the full keyboard map, focus management and RTL.

Nothing to unsubscribe from

input(), output(), model(), inject(), standalone directives. State is a signal you read, not an observable you unsubscribe from.

Zoneless and SSR-safe

Works under provideZonelessChangeDetection(); Zone.js is never required. Every primitive carries a server-render smoke test.

Pay only for what you import

Import forty-cdk/dialog and your bundle never sees Table. Each primitive is its own entry point, so the isolation is structural, not a tree-shaking result.

Start here

Every primitive

57 entry points · hover one for what it is

Primitives 49

Utilities 8

MIT-licensed · built with ❤️ by tutkli