@aceshooting/lyra-ui 11.2.0 → 12.0.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +388 -0
  2. package/README.md +11 -8
  3. package/custom-elements.json +1 -1
  4. package/dist/all.d.ts +1 -0
  5. package/dist/all.js +1 -1
  6. package/dist/components/charts/chart/chart.class.d.ts +13 -1
  7. package/dist/components/data/funnel/funnel.class.d.ts +92 -0
  8. package/dist/components/data/funnel/funnel.class.js +25 -0
  9. package/dist/components/data/funnel/funnel.d.ts +1 -0
  10. package/dist/components/data/funnel/funnel.js +1 -0
  11. package/dist/components/data/funnel/funnel.styles.d.ts +1 -0
  12. package/dist/components/data/funnel/funnel.styles.js +127 -0
  13. package/dist/components/data/heatmap/heatmap.class.d.ts +195 -13
  14. package/dist/components/data/heatmap/heatmap.class.js +30 -22
  15. package/dist/components/data/heatmap/heatmap.styles.js +36 -0
  16. package/dist/components/data/index.d.ts +1 -0
  17. package/dist/components/data/index.js +1 -1
  18. package/dist/components/forms/date-picker/date-input.class.d.ts +23 -0
  19. package/dist/components/forms/date-picker/date-input.class.js +1 -1
  20. package/dist/components/layout/app-rail/app-rail-item.class.d.ts +16 -0
  21. package/dist/components/layout/app-rail/app-rail-item.class.js +4 -4
  22. package/dist/components/layout/filter-bar/filter-bar.class.d.ts +32 -3
  23. package/dist/components/layout/filter-bar/filter-bar.class.js +2 -1
  24. package/dist/components/layout/widget/widget.class.d.ts +16 -0
  25. package/dist/components/layout/widget/widget.class.js +2 -2
  26. package/dist/components/lr-funnel.d.ts +1 -0
  27. package/dist/components/lr-funnel.js +1 -0
  28. package/dist/components/media/flag/flag-peer-bulk-standard.d.ts +25 -0
  29. package/dist/components/media/flag/flag-peer-bulk-standard.js +1 -0
  30. package/dist/components/media/flag/flag.class.d.ts +8 -0
  31. package/dist/components/media/flag/flag.class.js +1 -1
  32. package/dist/components/media/map/map.class.d.ts +196 -15
  33. package/dist/components/media/map/map.class.js +1 -1
  34. package/dist/components/overlays/chip/chip-group.class.d.ts +16 -0
  35. package/dist/components/overlays/chip/chip-group.class.js +10 -3
  36. package/dist/custom-elements-jsx.d.ts +12 -4
  37. package/dist/internal/autoloader-manifest.js +1 -1
  38. package/dist/internal/autoloader-tags.d.ts +1 -1
  39. package/dist/internal/autoloader-tags.js +1 -1
  40. package/dist/internal/lyra-element.d.ts +7 -1
  41. package/dist/internal/lyra-element.js +1 -1
  42. package/dist/internal/package-metadata.d.ts +1 -1
  43. package/dist/internal/package-metadata.js +1 -1
  44. package/dist/internal/root-registration-allowlist.d.ts +1 -1
  45. package/dist/internal/root-registration-allowlist.js +1 -1
  46. package/dist/lyra.d.ts +4 -2
  47. package/dist/lyra.js +1 -1
  48. package/dist/ssr/all.d.ts +1 -0
  49. package/dist/ssr/all.js +1 -1
  50. package/dist/ssr.d.ts +4 -4
  51. package/dist/styles/tokens-root.css +488 -0
  52. package/dist/svelte.d.ts +13 -4
  53. package/dist/vue.d.ts +12 -4
  54. package/llms/components/lr-app-rail-item.md +8 -1
  55. package/llms/components/lr-app-rail.md +6 -0
  56. package/llms/components/lr-chip-group.md +7 -0
  57. package/llms/components/lr-chip.md +7 -0
  58. package/llms/components/lr-date-input.md +19 -1
  59. package/llms/components/lr-date-picker.md +19 -1
  60. package/llms/components/lr-filter-bar.md +50 -1
  61. package/llms/components/lr-flag.md +14 -0
  62. package/llms/components/lr-funnel.md +122 -0
  63. package/llms/components/lr-heatmap.md +51 -7
  64. package/llms/components/lr-map.md +65 -8
  65. package/llms/components/lr-widget.md +7 -1
  66. package/llms/index.md +3 -2
  67. package/llms/peers.md +1 -1
  68. package/llms/shared.md +110 -1
  69. package/llms-full.txt +468 -18
  70. package/llms.txt +1 -1
  71. package/package.json +25 -6
  72. package/vscode-css-data.json +42 -0
  73. package/vscode-html-data.json +96 -17
  74. package/web-types.json +32714 -317
@@ -16,8 +16,9 @@
16
16
  ## `lr-map`
17
17
 
18
18
  A `maplibre-gl` wrapper with a declarative legend, a single choropleth GeoJSON fill layer, markers,
19
- and additive plain-GeoJSON `dataLayers`, plus a peer-neutral `map` getter for common imperative
20
- operations. Its runtime value is the underlying MapLibre map.
19
+ and additive `dataLayers` — plain GeoJSON, natively clustered points, or a heatmap density surface
20
+ plus a peer-neutral `map` getter for common imperative operations. Its runtime value is the
21
+ underlying MapLibre map.
21
22
 
22
23
  **Properties:**
23
24
 
@@ -100,7 +101,8 @@ string; geojson: GeoJSON.FeatureCollection; field: string; stops: [number, strin
100
101
  - `dataLayers: LyraMapGeoJsonDataLayer[] = []` (attribute: false) —
101
102
  `LyraMapGeoJsonDataLayer { sourceId: string; geojson: GeoJSON.Feature |
102
103
  GeoJSON.FeatureCollection; tone?: 'accent' | 'success' | 'warning' |
103
- 'danger' | 'neutral'; color?: string; strokeColor?: string }`. `sourceId` is trimmed and must be nonempty; the first layer for a
104
+ 'danger' | 'neutral'; color?: string; strokeColor?: string; kind?: LyraMapDataLayerKind;
105
+ heatmap?: LyraMapHeatmapOptions; cluster?: LyraMapClusterOptions }`. `sourceId` is trimmed and must be nonempty; the first layer for a
104
106
  `sourceId` wins and blank or later duplicate records are ignored. Each retained entry adds one
105
107
  GeoJSON source plus three geometry-filtered layers
106
108
  (fill, line, and circle, so a mixed `FeatureCollection` renders correctly), colored from the
@@ -123,6 +125,55 @@ GeoJSON.FeatureCollection; tone?: 'accent' | 'success' | 'warning' |
123
125
  persists across a `dataLayers` reassignment gets its GeoJSON updated in place (`setData()`), one
124
126
  that's dropped has its private source/layers removed, and a genuinely new `sourceId` gets new
125
127
  resources — nothing leaks on removal, style change, or disconnect.
128
+
129
+ `cluster` and `kind` (both new in 11.3.0) opt one entry out of that three-layer geometry split.
130
+ **Both are strictly additive: an entry that sets neither renders exactly what it rendered before,
131
+ down to the layer ids and the point layer's filter.**
132
+
133
+ `cluster?: LyraMapClusterOptions { radius?: number; maxZoom?: number; radiusSteps?: [number,
134
+ number][]; colorSteps?: [number, string][]; countFont?: string[] }` turns the entry's source into a
135
+ natively clustered one — `cluster`/`clusterRadius` (default 50)/`clusterMaxZoom` (default 14) on
136
+ the source, plus a `${sourceId}-cluster` circle filtered on `has('point_count')`, a
137
+ `${sourceId}-cluster-count` label, and a `${sourceId}-circle` layer for the points that stayed
138
+ unclustered. `cluster: {}` opts in at every default. This is what a thousands-of-points map needs
139
+ and what `markers` cannot be: `markers` mints one real, individually focusable DOM element per
140
+ entry, which is right for tens of pins and both unreadable and expensive for thousands.
141
+ `radiusSteps` and `colorSteps` are `['step', …]` breaks keyed on `point_count`, in the same
142
+ ascending `[value, output]` vocabulary as `choropleth.stops` — including the same base rule, where
143
+ the first entry's own output covers everything below the first threshold, and the same colour
144
+ resolution, where a `var(--lr-…)` reference in a `colorSteps` entry is resolved against the host
145
+ before it reaches MapLibre (which paints to a WebGL canvas and never sees the CSS cascade), so a
146
+ retheme moves the cluster breaks with everything else. No fill or line layer is created for a
147
+ clustered entry, because MapLibre's clustering keeps point features only. The count
148
+ label needs glyphs: a style that declares none gets the graduated circles without the numbers
149
+ (adding a text layer against a glyph-less style paints nothing and only emits peer errors), and
150
+ `countFont` names the font stack when your style's glyph source lacks MapLibre's spec default.
151
+ Cluster options are baked into the source at creation time by MapLibre and have no setter, so
152
+ changing them (or `kind`) rebuilds that one entry's source and layers; every other update still
153
+ reconciles in place.
154
+
155
+ `kind?: 'auto' | 'heatmap'` (`LyraMapDataLayerKind`, default `'auto'` — today's geometry split)
156
+ renders the source as MapLibre's own first-class `heatmap` layer instead, which the geometry split
157
+ cannot express at all: thousands of overlapping circles read as one opaque blob rather than as
158
+ where the data is concentrated. `heatmap?: LyraMapHeatmapOptions { weightField?: string;
159
+ weightRange?: [number, number]; stops?: [number, string][]; radius?: number; intensity?: number }`
160
+ configures it. `weightField` weights each point by a feature property, and `weightRange` maps that
161
+ property's own units onto the 0–1 weight MapLibre expects — without it the raw value is passed
162
+ through, which saturates the surface for any quantity above ~1; with neither, every point weighs 1.
163
+ `stops` are `[density, color]` pairs with density in `[0, 1]`, **the same `[value, color]`
164
+ vocabulary `choropleth.stops` and `legendGradient` already share**, so a `legendGradient` bar can
165
+ describe the ramp without a second copy of it, and `var(--lr-…)` stops resolve against the host the
166
+ same way `color`/`strokeColor` do. A ramp that doesn't start at density 0 gets a fully transparent
167
+ stop prepended, because a coloured zero tints the entire map — so **a single stop is already a
168
+ complete ramp**, as long as it sits above density 0: `stops: [[1, '#ff0000']]` is exactly
169
+ transparent → red. The one authored ramp that can't be honoured is a lone stop AT density 0, which
170
+ describes a flat colour rather than a gradient; that one — like an unset or wholly unusable
171
+ `stops` — falls back to the built-in ramp, which runs transparent → `--lr-color-brand` →
172
+ `--lr-color-success` → `--lr-color-warning` → `--lr-color-danger`, so a retheme moves the density
173
+ surface with everything else. `radius`
174
+ (default 30) and `intensity` (default 1) are MapLibre's own. `cluster` is ignored on a heatmap
175
+ entry: a heatmap already aggregates density, and clustering its input would feed it one point per
176
+ cluster instead of the real distribution.
126
177
  - `maxBounds: LyraMapBounds | null = null` (attribute: false) — box the map may not pan outside,
127
178
  `[[west, south], [east, north]]`. Prefer it over calling `map.setMaxBounds()` through the `.map`
128
179
  escape hatch: constraining the camera can wedge maplibre-gl at a sub-1 fractional zoom in a wide
@@ -160,7 +211,8 @@ payload beside the map.
160
211
  empty canvas name. The non-semantic `[part="base"]` wrapper is not named instead.
161
212
 
162
213
  **Authoring types:** `LyraMapLegendEntry`, `LyraMapLegendPattern`, `LyraMapLegendProjection`, `LyraMapChoroplethLayer`,
163
- `LyraMapGeoJsonDataLayer`, `LyraMapMarker`, `LyraMapStyleSpecification`, and `LyraMapInstance`.
214
+ `LyraMapGeoJsonDataLayer`, `LyraMapDataLayerKind`, `LyraMapClusterOptions`, `LyraMapHeatmapOptions`,
215
+ `LyraMapMarker`, `LyraMapStyleSpecification`, and `LyraMapInstance`.
164
216
  The former `LegendEntry`, `ChoroplethLayer`, `GeoJsonDataLayer`, and `MapMarker` names are removed
165
217
  in v9 rather than retained as aliases.
166
218
 
@@ -179,10 +231,15 @@ an element.
179
231
  (frozen `detail: { readonly lngLat: readonly [lng, lat], readonly feature?, readonly origin?,
180
232
  readonly sourceId? }`; the tuple and any hit GeoJSON feature are detached and recursively frozen).
181
233
  `feature` resolves against the choropleth fill layer **and** every applied `dataLayers`
182
- fill/line/circle layer, topmost first — so a shape painted through `dataLayers` is identifiable
183
- instead of being indistinguishable from empty space. `origin` is `'choropleth'` or `'data-layer'`,
184
- and `sourceId` carries the authored `dataLayers[].sourceId` for a data-layer hit; both are
185
- `undefined` whenever `feature` is
234
+ fill/line/circle/cluster layer, topmost first — so a shape painted through `dataLayers` is
235
+ identifiable instead of being indistinguishable from empty space. `origin` is `'choropleth'`,
236
+ `'data-layer'` or `'cluster'`, and `sourceId` carries the authored `dataLayers[].sourceId` for a
237
+ data-layer or cluster hit; both are `undefined` whenever `feature` is. A cluster hit is reported
238
+ separately because it is a synthetic aggregate rather than one of your features: its useful payload
239
+ is MapLibre's `point_count`/`point_count_abbreviated`/`cluster_id` properties, which is what a
240
+ zoom-to-cluster handler reads. The count label is deliberately not hit-tested (it sits exactly on
241
+ the circle already queried and would only make the label the topmost hit), and a `kind: 'heatmap'`
242
+ layer is never queried at all — MapLibre returns no features for a rendered density surface
186
243
 
187
244
  **Slots:** `legend` — custom legend content, rendered inside the legend panel's own layout so it
188
245
  stays positioned with the map instead of floating beside it. Supplying it opens the panel even
@@ -6,7 +6,7 @@
6
6
  - **Class** `LyraWidget`, also available unregistered from `@aceshooting/lyra-ui/components/layout/widget/widget.class.js`
7
7
  - **Family** `components/layout/` — see `llms/index.md` for its siblings
8
8
  - **Status** `stable` since `4.0.0` — see the maturity and deprecation policy in `llms/shared.md`
9
- - **Deprecations** none
9
+ - **Deprecated property** `activeView` since `11.2.0`; use property `activeViewId`; removal not before `13.0.0` — `activeView` was this property's original public name; the string does not appear anywhere in CHANGELOG.md, so the rename to `activeViewId` was never announced at all. A shipped consumer's `.activeView=` binding became a dead expando and the widget fell back to its first view with no error. The alias seeds `activeViewId` rather than being read alongside it, because the component itself writes that property (a view-toggle click, and the fallback when `views` drops the active id), and a read-alongside alias would undo a later interactive change on the next update.
10
10
  - **Optional peers** none
11
11
  - **Themeable via** 16 parts, 9 custom properties — see this component's own `@csspart`/`@cssprop` list below
12
12
  - **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`
@@ -49,6 +49,12 @@ TemplateResult; ariaLabel?: string }`. Each entry gets a header toggle button
49
49
  - `activeViewId: string = ''` (attribute: false) — the currently active view's `viewId`; defaults to the
50
50
  first entry of `views` (or `''` when `views` is empty). Settable directly to control the active
51
51
  view externally; also updated internally when a view toggle is clicked.
52
+ - `activeView: string = ''` (attribute: false) — **deprecated alias for `activeViewId`**, which it
53
+ seeds. `activeView` was this member's original public name and the rename was never announced, so
54
+ a shipped `.activeView=${…}` binding silently became inert and the widget fell back to its first
55
+ view. It seeds rather than being read alongside, because the component itself writes
56
+ `activeViewId` (a toggle click, and the fallback when `views` drops the active id) — so a stale
57
+ alias must not undo a later interactive change. Prefer `activeViewId` in new code.
52
58
  - `accessibleLabel: string | null = null` (attribute `aria-label`) — overrides the label-derived
53
59
  fullscreen dialog name. An explicitly empty value is retained; property, slotted-label, and
54
60
  localized fallbacks apply only when it is absent.
package/llms/index.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Component index
4
4
 
5
- 284 custom elements, grouped by the source family they live in.
5
+ 285 custom elements, grouped by the source family they live in.
6
6
 
7
7
  **Reading one component.** Its reference file path is derived from the tag — no search needed:
8
8
  `llms/components/<tag>.md` (e.g. `llms/components/lr-table.md`). Each is self-contained: import
@@ -50,7 +50,7 @@ Optional peers: `llms/peers.md`. Safe `wa-*`/`sl-*` migration: `llms/migration.m
50
50
  - `lr-rubric-form` · lr-rubric-form.js · `stable` since `4.0.0` — a configurable annotation rubric (LangSmith annotation-queue style): score, category, and freeform-comment keys with a submit-and-next fl...
51
51
  - `lr-locale-picker` · lr-locale-picker.js · `stable` since `6.0.0` — a closed-list locale switcher over the library's own locale registry.
52
52
 
53
- ## Data display, dashboards & flow canvas — `components/data/` (25)
53
+ ## Data display, dashboards & flow canvas — `components/data/` (26)
54
54
 
55
55
  - `lr-sparkline` · lr-sparkline.js · `stable` since `4.0.0` — a zero-dependency inline SVG trend chart.
56
56
  - `lr-stat` · lr-stat.js · `stable` since `4.0.0` — a KPI/stat card.
@@ -58,6 +58,7 @@ Optional peers: `llms/peers.md`. Safe `wa-*`/`sl-*` migration: `llms/migration.m
58
58
  - `lr-table` · lr-table.js · `stable` since `4.0.0` — a sort/select-aware data table.
59
59
  - `lr-pagination` · lr-pagination.js · `stable` since `4.0.0` — controlled, server-friendly page navigation: a numbered page list with elided gaps, optional first/last controls, an optional item-range...
60
60
  - `lr-gauge` · lr-gauge.js · `stable` since `4.0.0` — a radial, full-circle ring, or linear meter.
61
+ - `lr-funnel` · lr-funnel.js · `experimental` since `12.0.0` — a dependency-free conversion funnel: an ordered set of stages, each drawn as a bar whose length is that stage's share of the FIRST stage,...
61
62
  - `lr-word-cloud` · lr-word-cloud.js · `stable` since `4.0.0` — a zero-dependency SVG word/tag cloud.
62
63
  - `lr-heatmap` · lr-heatmap.js · `stable` since `4.0.0` — a Canvas heatmap with a DPR-aware, resize-aware redraw loop.
63
64
  - `lr-sequence-strip` · lr-sequence-strip.js · `stable` since `4.0.0` — a compact, one-thin-cell-per-item strip visualizing a sequence of categorical states, with an optional secondary per-cell marker.
package/llms/peers.md CHANGED
@@ -37,7 +37,7 @@ for the page.
37
37
 
38
38
  | Peer | Range | Needed by |
39
39
  |---|---|---|
40
- | `@aceshooting/lyra-flags` | `workspace:^2.1.0` | `lr-flag`, `lr-locale-picker`, `lr-phone-input` |
40
+ | `@aceshooting/lyra-flags` | `workspace:^2.2.0` | `lr-flag`, `lr-locale-picker`, `lr-phone-input` |
41
41
  | `@aiden0z/pptx-renderer` | `^1.2.4` | `lr-pptx-viewer` |
42
42
  | `@sgratzl/chartjs-chart-boxplot` | `^4.4.5` | `lr-box-plot` |
43
43
  | `chart.js` | `^4.5.1` | `lr-bar-chart`, `lr-box-plot`, `lr-bubble-chart`, `lr-chart`, `lr-doughnut-chart`, `lr-histogram`, `lr-line-chart`, `lr-pie-chart`, `lr-polar-area-chart`, `lr-radar-chart`, `lr-scatter-chart` |
package/llms/shared.md CHANGED
@@ -150,6 +150,8 @@ The entry points, then:
150
150
  a pre-resolved `src`) additionally needs
151
151
  `import '@aceshooting/lyra-ui/components/media/flag/flag-peer.js';` once.
152
152
  - **Other subpaths.** `@aceshooting/lyra-ui/theme.css` (ready-made light/dark theme),
153
+ `@aceshooting/lyra-ui/tokens-root.css` (opt-in: the curated resolved `--lr-*` tokens at `:root`,
154
+ so your own components can read them),
153
155
  `@aceshooting/lyra-ui/native.css` (opt-in native-element styles inside `.lr-native`),
154
156
  `@aceshooting/lyra-ui/utilities.css` (opt-in light-DOM layout/text utilities),
155
157
  `@aceshooting/lyra-ui/theme.js` (the zero-dependency mode/accent runtime),
@@ -644,10 +646,110 @@ reaches plain application CSS, and it never reaches your own custom elements, si
644
646
  descendant of an `lr-*` shadow root — `body { color: var(--lr-color-text) }` in application CSS
645
647
  resolves to nothing, silently, not an error. Retheme through layer 1 (`--lr-theme-*`), which
646
648
  `theme.css` supplies at document scope and which inherits normally into every nested shadow root.
649
+ To _read_ (not retheme) the resolved values from your own components, import the opt-in
650
+ [`tokens-root.css`](#reading-the-resolved-tokens-from-your-own-components--tokens-rootcss), which
651
+ declares a curated subset of layer 2 at `:root`.
647
652
  See [Where an override actually reaches](#where-an-override-actually-reaches) below for the full
648
653
  inheritance rules, including the one documented exception (per-component `--lr-<component>-*`
649
654
  hooks, layer 3, which do inherit through wrappers).
650
655
 
656
+ ### Reading the resolved tokens from your own components — `tokens-root.css`
657
+
658
+ The paragraph above is a real problem for any application that has custom elements of its own: they
659
+ are not descendants of an `lr-*` shadow root either, so `var(--lr-color-border)` inside **your**
660
+ component resolves to nothing, and `var(--lr-space-m, 0.5rem)` quietly runs on its literal fallback
661
+ forever. Both failures are invisible without reading computed styles in a browser.
662
+
663
+ Import one optional stylesheet and the curated part of layer 2 exists at document scope:
664
+
665
+ ```css
666
+ @import "@aceshooting/lyra-ui/theme.css"; /* the --lr-theme-* input layer */
667
+ @import "@aceshooting/lyra-ui/tokens-root.css"; /* the resolved --lr-* layer, at :root */
668
+ ```
669
+
670
+ ```css
671
+ /* Now valid in your own component's stylesheet, in plain application CSS, anywhere. */
672
+ .app-panel {
673
+ padding: var(--lr-space-m);
674
+ border: var(--lr-border-width-thin) solid var(--lr-color-border);
675
+ border-radius: var(--lr-radius);
676
+ background: var(--lr-color-surface-raised);
677
+ color: var(--lr-color-text);
678
+ font-family: var(--lr-font);
679
+ }
680
+ .app-panel:focus-visible {
681
+ outline: var(--lr-focus-ring);
682
+ outline-offset: var(--lr-focus-ring-offset);
683
+ }
684
+ ```
685
+
686
+ **It is opt-in, and it is a curated subset — not all of layer 2.** `--lr-*` is internal precisely so
687
+ it can change without a major version; publishing all of it at `:root` would freeze several hundred
688
+ internal decisions as permanent API. What ships is what an application's own component needs to sit
689
+ inside a Lyra UI without looking foreign:
690
+
691
+ | Family | Names |
692
+ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
693
+ | Ambient colour | `--lr-color-surface`, `--lr-color-surface-raised`, `--lr-color-surface-overlay`, `--lr-color-overlay`, `--lr-color-text`, `--lr-color-text-quiet`, `--lr-color-border`, `--lr-color-border-strong` |
694
+ | The semantic grid | all 45 `--lr-color-{brand,success,warning,danger,neutral}-{fill,border,on}-{quiet,normal,loud}` slots |
695
+ | Flat colour aliases | `--lr-color-{brand,success,warning,danger,neutral}`, `--lr-color-{brand,success,warning,danger}-quiet`, `--lr-color-on-{brand,success,warning,danger,neutral}` |
696
+ | Spacing | `--lr-space-2xs`, `--lr-space-xs`, `--lr-space-s`, `--lr-space-m`, `--lr-space-l`, `--lr-space-2xl` |
697
+ | Geometry | `--lr-radius-xs`, `--lr-radius`, `--lr-radius-pill`, `--lr-border-width-thin`, `--lr-border-width-medium`, `--lr-border-width-thick` |
698
+ | Elevation | `--lr-shadow-color`, `--lr-shadow-xs`, `--lr-shadow-s`, `--lr-shadow-m`, `--lr-shadow-l`, `--lr-shadow-xl`, `--lr-shadow` |
699
+ | Typography | `--lr-font`, `--lr-font-mono`, the ten `--lr-font-size-*` steps, the four `--lr-font-weight-*` steps |
700
+ | State and motion | `--lr-focus-ring`, `--lr-focus-ring-color`, `--lr-focus-ring-width`, `--lr-focus-ring-offset`, `--lr-opacity-disabled`, `--lr-opacity-muted`, `--lr-duration-fast`, `--lr-duration-base`, `--lr-easing-standard`, `--lr-easing-emphasized`, `--lr-transition-fast`, `--lr-transition-base` |
701
+
702
+ The grid ships whole because its contrast guarantee is **per tier** — a `fill-quiet` background is
703
+ only guaranteed legible under the matching `on-quiet` foreground — so shipping the flat aliases
704
+ alone would hand you a pairing with nothing behind it.
705
+
706
+ **Deliberately not published**, and each for a reason that makes reading it a bug rather than a
707
+ convenience: `--lr-ramp-*` (a step encodes a light-mode choice and has no theme hook),
708
+ `--lr-size-*` (value-named geometry constants, frozen internals), the chart, graph and terminal
709
+ palettes (generated ramps that move with the palette tooling), `--lr-layer-*` (stacking order is
710
+ your decision), `--lr-color-mix-*` and `--lr-hover-brightness` (inputs to the library's own
711
+ interaction recipe), `--lr-line-height-*`, the per-control internals (`--lr-icon-button-size`,
712
+ `--lr-otp-input-segment-size`, `--lr-scroll-fade-size`, `--lr-popover-viewport-clamp`,
713
+ `--lr-safe-area-*`, `--lr-mask-opaque`, `--lr-color-no-data`), and the nine variant-following slots
714
+ (`--lr-color-fill-loud` and friends), which mean "the variant _this_ element is set to" and are
715
+ meaningless on `:root`. If you need one of these, ask for it to be added rather than reading it out
716
+ of a component's shadow root.
717
+
718
+ **Stability promise.** Every name in the table is public API from the release that introduced it: it
719
+ will not be renamed or removed outside a major version, and its meaning will not change. Its _value_
720
+ may change in a minor exactly as it may inside a component — a palette retune moves your elements
721
+ and the kit's together, which is the point. Names absent from the file stay internal and may change
722
+ in any release.
723
+
724
+ **Modes work the way the components' do.** Light on `:root` and on `.lr-light` /
725
+ `[data-lr-theme="light"]`, dark under `prefers-color-scheme: dark` (unless an explicit light scope
726
+ opts out) and on `.lr-dark` / `[data-lr-theme="dark"]`, plus the same `forced-colors` and
727
+ `prefers-reduced-motion` overrides the components apply — and those two reach you on **every** one
728
+ of those routes, including a dark OS with no explicit scope, because they repeat the compound
729
+ `:root:not(.lr-light):not([data-lr-theme="light"])` selector the dark route uses rather than a bare
730
+ `:root` that the dark route would out-specify. Every declaration keeps its `--lr-theme-*`
731
+ input in front of a resolved fallback, so `theme.css` and your own overrides still win here exactly
732
+ as they do inside a component.
733
+
734
+ **One caveat, and it is the same shape as layer 2's rule everywhere else.** A `--lr-theme-*` input
735
+ set on a mid-tree element retunes every `lr-*` component below it, because each component re-derives
736
+ the resolved layer on its own `:host`. The document-scope copy cannot: it is substituted where it is
737
+ declared, and what inherits past that point is the finished value. So if an application element
738
+ carries a subtree override and expects its **own** descendants to follow, give that element a mode
739
+ scope too — `class="lr-light"`, `class="lr-dark"`, or `data-lr-theme` — which is what the file's
740
+ mode rules key on, and the whole subset resolves again there:
741
+
742
+ ```html
743
+ <!-- Both the lr-* components and the app's own elements below follow the override. -->
744
+ <section class="lr-light" style="--lr-theme-color-brand-fill-loud: #7c3aed">…</section>
745
+ ```
746
+
747
+ Everything sits in the `lr-theme` cascade layer, like `theme.css`, so any unlayered application rule
748
+ beats it regardless of load order, and the file declares custom properties only — notably not
749
+ `color-scheme` — so importing it paints nothing by itself. `--lr-focus-ring` and its three parts are
750
+ also declared at document scope by `theme.css`; both spell the same chain, so importing both is a
751
+ no-op either way round.
752
+
651
753
  ### The colour ramp and the semantic grid
652
754
 
653
755
  Colour has two layers beneath the `--lr-*` tokens you normally read.
@@ -1194,7 +1296,10 @@ stay in light DOM: they do not pierce a component's shadow root.
1194
1296
  `utilities.css` places exact, zero-specificity `:where(.lr-*)` classes in `lr-utilities`. It never
1195
1297
  uses a substring class selector, so a class such as `app-lr-flex-preview` does not opt in. Both
1196
1298
  assets repeat `@layer lr-base, lr-theme, lr-utilities, lr-overrides`; an ordinary unlayered
1197
- application rule therefore beats them regardless of load order.
1299
+ application rule therefore beats them regardless of load order. A third opt-in asset,
1300
+ [`tokens-root.css`](#reading-the-resolved-tokens-from-your-own-components--tokens-rootcss), is not a
1301
+ style sheet in the same sense — it declares custom properties only, and exists so your own
1302
+ components can read the resolved `--lr-*` tokens these two are written against.
1198
1303
 
1199
1304
  ### Utility class inventory
1200
1305
 
@@ -2705,6 +2810,7 @@ These named interfaces and helper signatures are available to typed integrations
2705
2810
  "lr-format-bytes": unknown;
2706
2811
  "lr-format-date": unknown;
2707
2812
  "lr-format-number": unknown;
2813
+ "lr-funnel": unknown;
2708
2814
  "lr-gauge": unknown;
2709
2815
  "lr-generation-metrics": unknown;
2710
2816
  "lr-geojson-view": unknown;
@@ -3090,6 +3196,7 @@ These named interfaces and helper signatures are available to typed integrations
3090
3196
  "lr-format-bytes": unknown;
3091
3197
  "lr-format-date": unknown;
3092
3198
  "lr-format-number": unknown;
3199
+ "lr-funnel": unknown;
3093
3200
  "lr-gauge": unknown;
3094
3201
  "lr-generation-metrics": unknown;
3095
3202
  "lr-geojson-view": unknown;
@@ -3376,6 +3483,7 @@ These named interfaces and helper signatures are available to typed integrations
3376
3483
  "lr-format-bytes": unknown;
3377
3484
  "lr-format-date": unknown;
3378
3485
  "lr-format-number": unknown;
3486
+ "lr-funnel": unknown;
3379
3487
  "lr-gauge": unknown;
3380
3488
  "lr-generation-metrics": unknown;
3381
3489
  "lr-geojson-view": unknown;
@@ -3702,6 +3810,7 @@ These named interfaces and helper signatures are available to typed integrations
3702
3810
  "lr-format-bytes": unknown;
3703
3811
  "lr-format-date": unknown;
3704
3812
  "lr-format-number": unknown;
3813
+ "lr-funnel": unknown;
3705
3814
  "lr-gauge": unknown;
3706
3815
  "lr-generation-metrics": unknown;
3707
3816
  "lr-geojson-view": unknown;