Guides
Cross-cutting documentation that spans primitives — how to style a headless library, how to wrap primitives in a design system, and the deeper table and virtualization compositions.
Styling
- Styling forty-cdkforty-cdk ships no styles. Every primitive exposes state, ARIA, focus management, and keyboard behavior; the visual design is entirely yours. This guide explains the three hooks you style against, so the appearance is yours while the behavior stays the library's.
- Styling floating contentEvery primitive that portals positioned content to document.body (Popover, Tooltip, HoverCard, DropdownMenu, ContextMenu, and nested Menu sub-menus) styles under the same four rules, because the positioner owns the content's translate and leaves the rest of the box to you. Follow them and enter / exit animations, arrow offsets and stacking all work without fighting the positioner.
- Selected-indicator alignment patternMenu checkbox and radio items line their labels up only when every row reserves the checkmark's width, checked or not. [forMenuItemIndicator] is the one selection indicator that still keeps a [forceMount] opt-in for exactly that, and hiding the glyph with opacity rather than display: none keeps the slot it reserves.
Composition patterns
- Your first overlayThis guide walks one overlay from an empty template to a styled, animated popover. It covers the two essential concepts every overlay in forty-cdk shares (the @if / open-state model and the portal → global CSS requirement) and points to the per-primitive references when you're ready to go deeper.
- Wrapping non-form rootsDesign systems built on forty-cdk rarely expose the raw primitives. They wrap each one in a styled component carrying the system's selector and classes. For form-value controls that story lives in Wrapping form primitives, which owns the Signal Forms contract, the FOR_*_HOST_DIRECTIVE_INPUTS name tuples, and the [formField] discovery rules.
Forms & selection
- Wrapping form primitivesDesign systems built on forty-cdk usually don't expose the raw primitives. They wrap each form control in a styled component with the system's selector and classes. The wrapper must re-expose the primitive's full API by exact public name: the value model (value / checked), the touched model, the touch output, and the shared form-state inputs (disabled, readonly, required, invalid, pending, dirty, name, errors), plus every control-specific member. Any omission fails silently: an unbound name falls back to a native DOM property and [formField] discovery degrades.
- Selection value-type contractEvery selection primitive in forty-cdk models its value the same way, so you learn one shape across the whole library and it flows through [formField] unchanged whether the control is single- or multi-select.
Dates & time
Table & virtualization
- Table: declarative columnsAuthor one [forTableColumnDef] per column and <for-table-body> stamps the header row and one data row per item out of the same cell primitives, so a column is declared in one place instead of being kept in sync by hand across a header row and a data row.
- Table: column & row reordering[forTableColumnReorder] and [forTableRowReorder] are opt-in companion directives that make header columns and data rows reorderable by pointer and by keyboard, each wrapping [forDropList] through hostDirectives so a table drags exactly as a standalone drop list does. The table never mutates your data: every committed drop reports { from, to } for you to apply yourself.
- Table: virtualized rows[forTableVirtualized] renders a window of rows instead of the whole dataset, while aria-rowcount and each row's aria-rowindex go on reporting the true totals, so a grid of tens of thousands of rows scrolls at a fixed cost and still tells a screen reader where the user is.