fold-ng 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,71 @@ All notable changes to **fold-ng** are documented here. The format follows
8
8
 
9
9
  _Nothing yet._
10
10
 
11
+ ## [0.4.0] - 2026-07-25
12
+
13
+ ### Added
14
+
15
+ - **Auto-inverting accent surface.** A new `[data-surface="accent"]` region
16
+ (stamped by `fold-card surface="accent"`, `fold-hero-card surface="accent"`,
17
+ or the `foldSurface` directive) fills with the brand accent and re-points its
18
+ **whole content sub-tree** to an on-accent palette — text, borders, band
19
+ gradation, and even nested buttons / links / icon-tiles read on the accent
20
+ with no per-component code. It is theme-agnostic and derived (every value a
21
+ `color-mix` of the captured accent pair), and swaps the brand pair
22
+ (`primary` ↔ `on-primary`) without a CSS custom-property cycle by capturing on
23
+ the surface and inverting on descendants. A theme can override any role by
24
+ nesting a rule for its own `[data-theme=…]` under `[data-surface="accent"]`.
25
+ `FoldSurfaceName` gains `'accent'`. See `docs/surfaces.md`.
26
+ - **On-accent contrast contract.** A contract test derives the on-accent text
27
+ ramp per theme and asserts it clears a documented WCAG floor (the accent is an
28
+ emphasis surface — AA-large 3:1 — or the theme must override the ramp). No
29
+ eyeballing.
30
+ - **`fold-card` — an accessible interactive contract.** `interactive` cards are
31
+ now real controls: `role="button"`, `tabindex`, `Enter`/`Space` activation, an
32
+ `(activated)` output, an `ariaLabel` input, a visible focus ring, and
33
+ `prefers-reduced-motion` respected. The projected bands are neutral `<div>`s
34
+ (not `<header>`/`<footer>`), so a card is a single control with no nested
35
+ landmarks.
36
+ - **`fold-link` — `target` + `rel` for external links.** A linked `fold-link`
37
+ takes `target` (e.g. `_blank`) and `rel`; `rel` defaults to a safe
38
+ `noopener noreferrer` whenever `target="_blank"`, and `(clicked)` now emits the
39
+ `MouseEvent` (so cmd/middle-click and modifier state are observable).
40
+ - **`titan` theme — brushed titanium.** A fifth `[data-theme]`: a light, warm
41
+ brushed-steel read. A cool `steel` ground with the header + rails at the page's
42
+ own tint (a frameless top), bright polished cards floating off it on the shared
43
+ elevation shadow, and a heat-anodized `titanium` **copper-orange** brand.
44
+ Borders re-point to a **solid** steel primitive (a palpable machined seam, not
45
+ the alpha hairline the light themes share); corners soften a step in
46
+ `scales.css`. In the gallery both rails float as steel plates while the header
47
+ stays flat. Uniform-polarity, so no chrome override. The token contract (theme
48
+ parity, no-hex, no dead primitives) stays green.
49
+
50
+ ### Changed
51
+
52
+ - **BREAKING — `fold-card` band chrome is per-band.** `separators` and
53
+ `raisedBands` change from booleans to a `FoldCardBandChrome`
54
+ (`'none' | 'header' | 'footer' | 'both'`), so a header and a footer are dressed
55
+ independently. Migration: `<fold-card separators>` → `separators="both"`;
56
+ drop the attribute for `'none'`.
57
+
58
+ ### Fixed
59
+
60
+ - **`fold-avatar` — a broken image falls back to the initials** instead of the
61
+ browser's broken-image glyph, and retries when `imageUrl` changes. `ghost`
62
+ combined with `imageUrl` is no longer a silent no-op — a guest keeps the
63
+ dashed edge even with a photo.
64
+ - **`fold-avatar` — initials stay legible on every palette fill.** The initials
65
+ ink is now the higher-contrast of the dark/light pair (was a magic-threshold
66
+ guess), and a contrast contract asserts every built-in palette fill clears AA
67
+ (≥ 4.5:1). A status-ring perceivability contract locks the `ring` colours
68
+ against a WCAG 1.4.11 regression.
69
+
70
+ ### Docs
71
+
72
+ - **`docs/surfaces.md`** documents the auto-inversion principle and the
73
+ per-theme override seam; a `/surfaces` gallery page shows the live plain-vs-
74
+ accent proof. `docs/STRENGTHS.md` captures the top-tier design arguments.
75
+
11
76
  ## [0.3.0] - 2026-07-25
12
77
 
13
78
  ### Added
@@ -133,7 +198,8 @@ design-token stylesheet.
133
198
  `currentColor`; `prefers-reduced-motion` + `forced-colors` are respected;
134
199
  strings localise via inputs / providers (`provideFoldPanelLabels`).
135
200
 
136
- [unreleased]: https://github.com/hugoheynard/fold-ng/compare/v0.3.0...HEAD
201
+ [unreleased]: https://github.com/hugoheynard/fold-ng/compare/v0.4.0...HEAD
202
+ [0.4.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.4.0
137
203
  [0.3.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.3.0
138
204
  [0.2.1]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.2.1
139
205
  [0.2.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.2.0
package/README.md CHANGED
@@ -90,12 +90,14 @@ set `data-theme` on an ancestor — usually `<html>`:
90
90
  <html data-theme="lumen"></html>
91
91
  ```
92
92
 
93
- Four themes ship: `umbra` (the dark base, no attribute), `lumen` (light),
94
- `bubbly`
95
- (festive lavender, violet brand, rounded) and `navi` (dark chrome, light page).
96
- The extras exist to prove the point each is the umbra or lumen block with its
97
- primitive families re-pointed. Adding a fifth is a new `[data-theme]` block in
98
- `semantic.css` plus the primitives it names.
93
+ Five themes ship: `umbra` (the dark base, no attribute), `lumen` (light),
94
+ `bubbly` (festive lavender, violet brand, rounded), `navi` (dark chrome, light
95
+ page) and `titan` (brushed titanium — a light brushed-steel ground with a
96
+ frameless top and floating rails, bright cards, a heat-anodized copper-orange
97
+ brand, solid steel borders, iPhone-soft corners). The extras exist to prove the point each
98
+ is the umbra or lumen block with its primitive families re-pointed. Adding a
99
+ sixth is a new `[data-theme]` block in `semantic.css` plus the primitives it
100
+ names.
99
101
 
100
102
  `bubbly` and `navi` also change their corners — **radius is the one scale a theme may
101
103
  re-declare**, because corner softness is a brand axis (friendly vs
@@ -111,6 +113,81 @@ roles cannot express. It gets there by re-declaring those roles on
111
113
  theme never names a component's internals. Variables only. If mixed chrome ever
112
114
  stops being a demo, the catalogue should grow real `*-on-chrome` roles.
113
115
 
116
+ ## Auto-inverting surfaces
117
+
118
+ The same `[data-surface]` seam powers an **accent** surface — a region filled
119
+ with the brand accent whose entire content sub-tree flips to a compatible
120
+ on-accent palette **with no per-component code**. Drop a `fold-card` in a grid
121
+ and set `surface="accent"` (or stamp `foldSurface="accent"` on any element):
122
+
123
+ ```html
124
+ <fold-card surface="accent">
125
+ <h3>Studio plan</h3>
126
+ <p>Everything in Pro, plus shared workspaces.</p>
127
+ <fold-badge content="Popular" variant="accent" />
128
+ <fold-link href="/plans">Compare plans</fold-link>
129
+ <button foldButton>Choose</button>
130
+ </fold-card>
131
+ ```
132
+
133
+ Every nested thing — the text ramp, the hairline border, a raised band, the
134
+ badge, the link, the button, even a filled icon tile — reads correctly on the
135
+ accent. Not because the card special-cases them, but because the region
136
+ **re-points the semantic roles** they already resolve against.
137
+
138
+ ### How it works (and why it survives a solid button)
139
+
140
+ An inverting surface has to do something a chrome surface never does: **swap the
141
+ brand pair**. On the accent, `--fold-color-primary` should become the light ink
142
+ (so a link or a filled tile's ground reads), and `--fold-color-on-primary` should
143
+ become the accent itself (so the label _on_ that tile reads). Written naively
144
+ that's a CSS custom-property cycle (`a: var(b); b: var(a)` → both invalid).
145
+
146
+ The trick: **capture on the surface, invert on the descendants.** The surface
147
+ element records the accent and its ink into two private vars _while the tokens
148
+ still hold their normal values_, and the inverted role-set is applied to
149
+ descendants from those captures — so a role can reference the pre-inversion value
150
+ it is replacing, no cycle:
151
+
152
+ ```css
153
+ [data-surface="accent"] {
154
+ --_accent-ink: var(--fold-color-on-primary); /* captured here… */
155
+ --_accent-fill: var(--fold-color-primary);
156
+ }
157
+ [data-surface="accent"] * {
158
+ --fold-color-text: var(--_accent-ink); /* …consumed here */
159
+ --fold-color-primary: var(--_accent-ink); /* fill → light ink */
160
+ --fold-color-on-primary: var(--_accent-fill); /* on-fill → the accent */
161
+ /* …surfaces/borders as color-mix of the captured pair… */
162
+ }
163
+ ```
164
+
165
+ Every value is a `color-mix` of the captured pair, so it is **derived, not
166
+ authored** — one definition holds on all five themes, and the surface/band steps
167
+ stay a lighter shade of the accent (gradation kept in-hue).
168
+
169
+ ### Overriding per theme
170
+
171
+ None of this is a cage. Writing your own CSS in a card is always free — raw
172
+ values don't reference the roles, so a surface never touches them. An _override_
173
+ isn't a hack: it re-anchors one **relationship** (text ↔ ground ↔ accent) inside
174
+ a controlled frame, and coherence follows because the ratios are preserved. Speak
175
+ in roles and gain the adaptation, or paint a pixel and own it — both coexist.
176
+
177
+ The derived defaults are good, not sacred. A theme that wants a different
178
+ on-accent ramp (a light accent might want darker text, say) overrides any role by
179
+ nesting its own theme selector under the surface — the same seam chrome uses:
180
+
181
+ ```css
182
+ [data-theme="titan"] [data-surface="accent"] * {
183
+ --fold-color-text: var(--fold-ref-steel-900); /* dark ink on a light accent */
184
+ }
185
+ ```
186
+
187
+ The one honest limit: an accent surface is a _single_ ground, so `surface`
188
+ stays one axis — there is no `accent` × `sunken`. That is a feature (a card in a
189
+ grid has one job: stand out), not a gap.
190
+
114
191
  Then style against the semantic tokens — never a raw colour:
115
192
 
116
193
  ```css
@@ -210,50 +287,50 @@ radius / text / icon-size / space / motion / blur scales — see
210
287
  All standalone, signals-first, styled against the semantic tokens. Import from
211
288
  the package root.
212
289
 
213
- | Component | Selector | What it is |
214
- | -------------------------------------------------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
215
- | `FoldAppShellComponent` | `fold-app-shell` | Responsive app skeleton (rails + header + content + self-collapsing `footer` slots; `headerLayout`/`footerLayout` inset·full, `footerBehavior` pinned·scroll; `mobileNav` drawer·none — `[(mobileNavOpen)]` off-canvas drawer for the primary rail on mobile, or `none` to compose an `fold-nav-launcher`; `contentScroll` clip·auto; built-in skip-link to a focusable `<main>`. Regions float per-surface via `foldElevated`, not a shell flag). |
216
- | `FoldMenuComponent` (+ `Item` / `Section` / `Separator`) | `fold-menu` | Collapsible nav rail — coloured sections, `tint="follow"`, depth `level`, collapse-toggle placement. Items are `a[fold-menu-item]`. |
217
- | `FoldNavLauncherComponent` (+ `FoldNavTileComponent`) | `fold-nav-launcher` | Full-screen mobile nav launcher — a centred tile grid over a blurred scrim (scrim / `Escape` / close dismissal, focus-trap, scroll-lock). `columns="auto"` scales tiles to the count. Pairs with `fold-app-shell mobileNav="none"`. Tiles are `a[fold-nav-tile]` — `variant="surface"`·`filled`. |
218
- | `FoldPageLayoutComponent` | `fold-page-layout` | Page scaffold — gutter + header + body rhythm; fills its container (width is a content concern). Tokens `--fold-page-gutter` / `--fold-page-gap`; sections can `bleed` edge-to-edge. |
219
- | `FoldPageSectionComponent` | `fold-page-section` | Semantic `<section>` grouping — eyebrow title (names the region via `aria-labelledby`) + description + actions; `stack` / `bleed` helpers. Not a box — compose a `fold-card` inside for that. |
220
- | `FoldHeroSectionComponent` | `fold-hero-section` | Full-bleed page splash — the borderless intro band at the top of a page (carries the `<h1>`). Direct child of `fold-page-layout`: cancels the gutter + top pad to sit flush, brand-tinted wash + hairline. `align` center·start, `wash`, `[heroBackdrop]` decorative lane. (For a bordered header card, see `fold-hero-card`.) |
221
- | `FoldAsideLayoutComponent` | `fold-aside-layout` | Detail-page grid — a centred column flanked by up to two sticky rails (`[asideLeft]` / `[asideRight]`), collapsing to one column on its own container width (`:has()`-driven, container queries). Labelled rails become `complementary` landmarks; every track is a CSS var. |
222
- | `FoldNavLayoutComponent` | `fold-nav-layout` | Places a bar (`[tabNav]` — a `fold-view-nav` or a `fold-tabs`) with its content — `placement="top"` or a `side` rail that folds back on top (hysteretic, on its own width) below `foldAt`. `exportAs="foldNavLayout"` exposes `stacked()` so the projected bar follows in one binding. |
223
- | `FoldCardComponent` | `fold-card` | Raised content surface (`surface-card` + consistent radius). |
224
- | `FoldContextCardComponent` | `fold-context-card` | Titled info card: icon header + body + optional footer action. |
225
- | `FoldHeroCardComponent` | `fold-hero-card` | Prominent header **card** — bordered surface × accent overlay + optional accent bar. (For a full-bleed page splash, see `fold-hero-section`.) |
226
- | `FoldElementTitleComponent` | `fold-element-title` | Uppercase section/card mini-title (eyebrow · bar variants). |
227
- | `FoldFieldListComponent` / `…Field` | `fold-field-list` | Read-only `dl/dt/dd` recap — label/value pairs (`[empty]` placeholder). The _display_ half of a record; `fold-input` is the _edit_ half. |
228
- | `FoldInputComponent` | `fold-input` | Text-input control (`value: string`) — Signal Forms (`[formField]`) or standalone `[(value)]`; size × align × variant, `label` / `required` / `hint`. The _edit_ half of a record (`fold-field` reads). |
229
- | `FoldNumberInputComponent` | `fold-number-input` | Numeric sibling of `fold-input` (`value: number \| null`, empty ⇒ `null`); owns `min` / `max` / `step` + `label` / `required` / `hint`. Split so each control keeps its true type. |
230
- | `FoldSearchComponent` | `fold-search` | Debounced search box — an `fold-input` that emits `searchChange` once typing settles (`delayMs`), trimmed + de-duplicated. |
231
- | `FoldSliderComponent` | `fold-slider` | Single-value range slider — a styled native `<input type="range">` (design-system track/fill/thumb), two-way `[(value)]`. |
232
- | `FoldRangeSliderComponent` | `fold-range-slider` | Dual-thumb slider selecting a `{ min, max }` window over `[min, max]`; shares the slider track/fill/thumb. |
233
- | `FoldFileDropzoneComponent` | `fold-file-dropzone` | File-picker dropzone — drag-over visuals, keyboard activation, hidden `<input type=file>` plumbing; emits the picked `File[]` (presentational — never uploads). |
234
- | `FoldLinkComponent` | `fold-link` | Inline text link / link-button (icons, accent · muted). |
235
- | `FoldButtonComponent` | `button[foldButton]` · `a[foldButton]` | Action button — applied to a real `<button>` **or** `<a>` (link that looks like a button, gets `href`/`routerLink`); orthogonal `emphasis` (solid·soft·outline) × `intent` (primary·neutral·warning·danger) × 3 sizes × shape/`block`; `icon`/`iconTrailing` shorthand (auto-sized) or project content; `loading` (spinner + `aria-busy`). Use native `(click)`. |
236
- | `FoldButtonIconComponent` | `fold-button-icon` | Icon-only **momentary** button — shape × size × tone; a one-shot action (no pressed state). For a text button use `fold-button`; for on/off use `fold-toggle-icon`. |
237
- | `FoldToggleIconComponent` | `fold-toggle-icon` | Icon-only **toggle** — the same surface as `fold-button-icon`, plus `[(active)]` + `aria-pressed` (true/false) and a pressed state. Emits `toggled`. |
238
- | `FoldDataTableComponent` | `fold-data-table` | Controlled roster table (sortable, tone rows, mobile cards). |
239
- | `FoldPaginatorComponent` | `fold-paginator` | Server-side paginator (size selector + range + page nav). |
240
- | `FoldTimelineComponent` | `fold-timeline` | Connected rail of nodes (dot + optional date + label) — `vertical` navigable history or `horizontal` step progress; nodes optionally clickable. |
241
- | `FoldBadgeComponent` | `fold-badge` | Status / count pill (accent/info/warning/alert/success). |
242
- | `FoldStatusBadgeComponent` | `fold-status-badge` | Status→colour badge (maps a domain status key to a tone). |
243
- | `FoldChoiceRowComponent` | `fold-choice-row` | Segmented / chip selector. |
244
- | `FoldViewNavComponent` | `fold-view-nav` | Navigation bar styled as tabs. Items carry a `link` (routerLink → a real `<a>`: cmd-click, deep-links, active state auto), an `href`, or nothing (a button); `aria-current="page"` on the active one. `direction="auto"` follows a wrapping `fold-nav-layout`; `collapsed` for an icon rail. For in-page panel switching use `fold-tabs` instead. |
245
- | `FoldTabsComponent` + `FoldTabPanelComponent` | `fold-tabs` + `fold-tab-panel` | The in-page ARIA Tabs widget: `role="tablist"` + roving arrow-key keyboard, `aria-selected`/`aria-orientation`, each tab wired to its `fold-tab-panel` (`aria-controls` ↔ `aria-labelledby`). Panels take the bar by ref (`[tabs]="t"`) so they coordinate across `fold-nav-layout` slots. |
246
- | `FoldIconComponent` | `fold-icon` | SVG icon (114-icon built-in set + `FoldIconRegistry`). |
247
- | `FoldSpinnerComponent` | `fold-spinner` | Indeterminate loading arc (`currentColor`, icon-sized, reduced-motion aware). Decorative by default; `label` → `role="status"`. Powers `loading` on the buttons. |
248
- | `FoldAvatarComponent` / `…Detail` | `fold-avatar` | Initials/image avatar (square, muted, status ring) + identity cell. |
249
- | `FoldAvatarListComponent` | `fold-avatar-list` | Overlapping avatar cluster (per-face variant, `limit` + a `+N` overflow chip). |
250
- | `FoldToastComponent` / `…Container` | `fold-toast` | Frosted snackbar (variant glyph + dismiss) + queue host (+ `FoldToastService`). |
251
- | `FoldLoadingStateComponent` | `fold-loading` | Loading placeholder — `fold-spinner` + message, in a `role="status"` region; `size` input; stretches to fill. |
252
- | `FoldEmptyStateComponent` | `fold-empty-state` | Empty-state block (icon + title + message + optional action). |
253
- | `FoldCalloutComponent` | `fold-callout` | Tinted message row — status colour + icon + message + optional trailing actions; `inset` (bordered, in-flow) appearance. |
254
- | `FoldDisclosureComponent` | `fold-disclosure` | One summary toggling one collapsible panel — the accordion primitive (open-state is the consumer's to bind); keeps content mounted, unlike native `<details>`. |
255
- | `FoldPanelHostComponent` | `fold-panel-host` | Side-panel / overlay host (+ `FoldPanelHostService` / `FoldPanelRef` / `FoldPanelToggle`). Modal: accessible name, `inert` background barrier, top-most focus trap, scroll-lock. Localise the close label once via `provideFoldPanelLabels({ close })`. |
256
- | `FoldPanelHeaderComponent` | `fold-panel-header` | Standard panel header (title/eyebrow, self-closing). Names its dialog (`aria-labelledby`) and reads the app-wide close label. |
290
+ | Component | Selector | What it is |
291
+ | -------------------------------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
292
+ | `FoldAppShellComponent` | `fold-app-shell` | Responsive app skeleton (rails + header + content + self-collapsing `footer` slots; `headerLayout`/`footerLayout` inset·full, `footerBehavior` pinned·scroll; `mobileNav` drawer·none — `[(mobileNavOpen)]` off-canvas drawer for the primary rail on mobile, or `none` to compose an `fold-nav-launcher`; `contentScroll` clip·auto; built-in skip-link to a focusable `<main>`. Regions float per-surface via `foldElevated`, not a shell flag). |
293
+ | `FoldMenuComponent` (+ `Item` / `Section` / `Separator`) | `fold-menu` | Collapsible nav rail — coloured sections, `tint="follow"`, depth `level`, collapse-toggle placement. Items are `a[fold-menu-item]`. |
294
+ | `FoldNavLauncherComponent` (+ `FoldNavTileComponent`) | `fold-nav-launcher` | Full-screen mobile nav launcher — a centred tile grid over a blurred scrim (scrim / `Escape` / close dismissal, focus-trap, scroll-lock). `columns="auto"` scales tiles to the count. Pairs with `fold-app-shell mobileNav="none"`. Tiles are `a[fold-nav-tile]` — `variant="surface"`·`filled`. |
295
+ | `FoldPageLayoutComponent` | `fold-page-layout` | Page scaffold — gutter + header + body rhythm; fills its container (width is a content concern). Tokens `--fold-page-gutter` / `--fold-page-gap`; sections can `bleed` edge-to-edge. |
296
+ | `FoldPageSectionComponent` | `fold-page-section` | Semantic `<section>` grouping — eyebrow title (names the region via `aria-labelledby`) + description + actions; `stack` / `bleed` helpers. Not a box — compose a `fold-card` inside for that. |
297
+ | `FoldHeroSectionComponent` | `fold-hero-section` | Full-bleed page splash — the borderless intro band at the top of a page (carries the `<h1>`). Direct child of `fold-page-layout`: cancels the gutter + top pad to sit flush, brand-tinted wash + hairline. `align` center·start, `wash`, `[heroBackdrop]` decorative lane. (For a bordered header card, see `fold-hero-card`.) |
298
+ | `FoldAsideLayoutComponent` | `fold-aside-layout` | Detail-page grid — a centred column flanked by up to two sticky rails (`[asideLeft]` / `[asideRight]`), collapsing to one column on its own container width (`:has()`-driven, container queries). Labelled rails become `complementary` landmarks; every track is a CSS var. |
299
+ | `FoldNavLayoutComponent` | `fold-nav-layout` | Places a bar (`[tabNav]` — a `fold-view-nav` or a `fold-tabs`) with its content — `placement="top"` or a `side` rail that folds back on top (hysteretic, on its own width) below `foldAt`. `exportAs="foldNavLayout"` exposes `stacked()` so the projected bar follows in one binding. |
300
+ | `FoldCardComponent` | `fold-card` | Raised content surface (`surface` = `card`/`sunken`/`accent`, hairline border, consistent radius). `accent` is an **auto-inverting** accent-filled card: the whole content sub-tree re-points to a compatible on-accent palette (text, borders, band gradation, nested buttons/links/icon-tiles) — every value a `color-mix` of the accent, so it holds on all themes (see [auto-inverting surfaces](#auto-inverting-surfaces)). Optional projected `[cardHeader]`/`[cardFooter]` bands with per-band chrome (`separators`/`raisedBands` = `none`/`header`/`footer`/`both`); the body padding never shifts when a band toggles. `interactive` makes the whole card an accessible button (`role`/`tabindex`, focus ring, Enter/Space/click → `(activated)`). |
301
+ | `FoldContextCardComponent` | `fold-context-card` | Titled info card: icon header + body + optional footer action. |
302
+ | `FoldHeroCardComponent` | `fold-hero-card` | Prominent header **card** — bordered surface × accent overlay + optional accent bar. (For a full-bleed page splash, see `fold-hero-section`.) |
303
+ | `FoldElementTitleComponent` | `fold-element-title` | Uppercase section/card mini-title (eyebrow · bar variants). |
304
+ | `FoldFieldListComponent` / `…Field` | `fold-field-list` | Read-only `dl/dt/dd` recap — label/value pairs (`[empty]` placeholder). The _display_ half of a record; `fold-input` is the _edit_ half. |
305
+ | `FoldInputComponent` | `fold-input` | Text-input control (`value: string`) — Signal Forms (`[formField]`) or standalone `[(value)]`; size × align × variant, `label` / `required` / `hint`. The _edit_ half of a record (`fold-field` reads). |
306
+ | `FoldNumberInputComponent` | `fold-number-input` | Numeric sibling of `fold-input` (`value: number \| null`, empty ⇒ `null`); owns `min` / `max` / `step` + `label` / `required` / `hint`. Split so each control keeps its true type. |
307
+ | `FoldSearchComponent` | `fold-search` | Debounced search box — an `fold-input` that emits `searchChange` once typing settles (`delayMs`), trimmed + de-duplicated. |
308
+ | `FoldSliderComponent` | `fold-slider` | Single-value range slider — a styled native `<input type="range">` (design-system track/fill/thumb), two-way `[(value)]`. |
309
+ | `FoldRangeSliderComponent` | `fold-range-slider` | Dual-thumb slider selecting a `{ min, max }` window over `[min, max]`; shares the slider track/fill/thumb. |
310
+ | `FoldFileDropzoneComponent` | `fold-file-dropzone` | File-picker dropzone — drag-over visuals, keyboard activation, hidden `<input type=file>` plumbing; emits the picked `File[]` (presentational — never uploads). |
311
+ | `FoldLinkComponent` | `fold-link` | Inline text link / link-button (icons, accent · muted). |
312
+ | `FoldButtonComponent` | `button[foldButton]` · `a[foldButton]` | Action button — applied to a real `<button>` **or** `<a>` (link that looks like a button, gets `href`/`routerLink`); orthogonal `emphasis` (solid·soft·outline) × `intent` (primary·neutral·warning·danger) × 3 sizes × shape/`block`; `icon`/`iconTrailing` shorthand (auto-sized) or project content; `loading` (spinner + `aria-busy`). Use native `(click)`. |
313
+ | `FoldButtonIconComponent` | `fold-button-icon` | Icon-only **momentary** button — shape × size × tone; a one-shot action (no pressed state). For a text button use `fold-button`; for on/off use `fold-toggle-icon`. |
314
+ | `FoldToggleIconComponent` | `fold-toggle-icon` | Icon-only **toggle** — the same surface as `fold-button-icon`, plus `[(active)]` + `aria-pressed` (true/false) and a pressed state. Emits `toggled`. |
315
+ | `FoldDataTableComponent` | `fold-data-table` | Controlled roster table (sortable, tone rows, mobile cards). |
316
+ | `FoldPaginatorComponent` | `fold-paginator` | Server-side paginator (size selector + range + page nav). |
317
+ | `FoldTimelineComponent` | `fold-timeline` | Connected rail of nodes (dot + optional date + label) — `vertical` navigable history or `horizontal` step progress; nodes optionally clickable. |
318
+ | `FoldBadgeComponent` | `fold-badge` | Status / count pill (accent/info/warning/alert/success). |
319
+ | `FoldStatusBadgeComponent` | `fold-status-badge` | Status→colour badge (maps a domain status key to a tone). |
320
+ | `FoldChoiceRowComponent` | `fold-choice-row` | Segmented / chip selector. |
321
+ | `FoldViewNavComponent` | `fold-view-nav` | Navigation bar styled as tabs. Items carry a `link` (routerLink → a real `<a>`: cmd-click, deep-links, active state auto), an `href`, or nothing (a button); `aria-current="page"` on the active one. `direction="auto"` follows a wrapping `fold-nav-layout`; `collapsed` for an icon rail. For in-page panel switching use `fold-tabs` instead. |
322
+ | `FoldTabsComponent` + `FoldTabPanelComponent` | `fold-tabs` + `fold-tab-panel` | The in-page ARIA Tabs widget: `role="tablist"` + roving arrow-key keyboard, `aria-selected`/`aria-orientation`, each tab wired to its `fold-tab-panel` (`aria-controls` ↔ `aria-labelledby`). Panels take the bar by ref (`[tabs]="t"`) so they coordinate across `fold-nav-layout` slots. |
323
+ | `FoldIconComponent` | `fold-icon` | SVG icon (114-icon built-in set + `FoldIconRegistry`). |
324
+ | `FoldSpinnerComponent` | `fold-spinner` | Indeterminate loading arc (`currentColor`, icon-sized, reduced-motion aware). Decorative by default; `label` → `role="status"`. Powers `loading` on the buttons. |
325
+ | `FoldAvatarComponent` / `…Detail` | `fold-avatar` | Initials/image avatar (square, muted, status ring) + identity cell. |
326
+ | `FoldAvatarListComponent` | `fold-avatar-list` | Overlapping avatar cluster (per-face variant, `limit` + a `+N` overflow chip). |
327
+ | `FoldToastComponent` / `…Container` | `fold-toast` | Frosted snackbar (variant glyph + dismiss) + queue host (+ `FoldToastService`). |
328
+ | `FoldLoadingStateComponent` | `fold-loading` | Loading placeholder — `fold-spinner` + message, in a `role="status"` region; `size` input; stretches to fill. |
329
+ | `FoldEmptyStateComponent` | `fold-empty-state` | Empty-state block (icon + title + message + optional action). |
330
+ | `FoldCalloutComponent` | `fold-callout` | Tinted message row — status colour + icon + message + optional trailing actions; `inset` (bordered, in-flow) appearance. |
331
+ | `FoldDisclosureComponent` | `fold-disclosure` | One summary toggling one collapsible panel — the accordion primitive (open-state is the consumer's to bind); keeps content mounted, unlike native `<details>`. |
332
+ | `FoldPanelHostComponent` | `fold-panel-host` | Side-panel / overlay host (+ `FoldPanelHostService` / `FoldPanelRef` / `FoldPanelToggle`). Modal: accessible name, `inert` background barrier, top-most focus trap, scroll-lock. Localise the close label once via `provideFoldPanelLabels({ close })`. |
333
+ | `FoldPanelHeaderComponent` | `fold-panel-header` | Standard panel header (title/eyebrow, self-closing). Names its dialog (`aria-labelledby`) and reads the app-wide close label. |
257
334
 
258
335
  Directives worth knowing: **`foldSurface`** (`page`·`chrome` — the seam a mixed
259
336
  theme re-colours across), **`foldElevated`** (raise any bg-owning element into