srcdev-nuxt-components 9.4.8 → 9.4.10

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 (113) hide show
  1. package/.claude/commands/migrate-component.md +20 -6
  2. package/.claude/component-ledger/audit.json +1 -1
  3. package/.claude/component-ledger/build.mjs +26 -0
  4. package/.claude/component-ledger/output.html +8 -3
  5. package/.claude/component-ledger/template.html +7 -2
  6. package/.claude/skills/component-dynamic-slots.md +1 -1
  7. package/.claude/skills/components/alert-mask-core.md +84 -0
  8. package/.claude/skills/components/canvas-switcher.md +1 -1
  9. package/.claude/skills/components/carousel-flip.md +112 -168
  10. package/.claude/skills/components/container-glow.md +123 -0
  11. package/.claude/skills/components/dashboard-quad-grid.md +98 -0
  12. package/.claude/skills/components/dashboard-stats-grid.md +103 -0
  13. package/.claude/skills/components/display-theme-switch.md +60 -0
  14. package/.claude/skills/components/glowing-border.md +66 -0
  15. package/.claude/skills/components/input-range-core.md +142 -0
  16. package/.claude/skills/components/section-parallax.md +32 -10
  17. package/.claude/skills/components/ui-block-decorated.md +56 -0
  18. package/.claude/skills/components/wipe-away-vertical.md +98 -0
  19. package/.claude/skills/icon-sets.md +1 -1
  20. package/.claude/skills/index.md +10 -1
  21. package/.vscode/css-custom-data.json +38 -0
  22. package/.vscode/settings.json +3 -0
  23. package/.vscode/srcdev-component-alert-mask-core.code-snippets +31 -0
  24. package/.vscode/srcdev-component-carousel-flip.code-snippets +41 -0
  25. package/.vscode/srcdev-component-container-glow.code-snippets +33 -0
  26. package/.vscode/srcdev-component-dashboard-quad-grid.code-snippets +36 -0
  27. package/.vscode/srcdev-component-dashboard-stats-grid.code-snippets +35 -0
  28. package/.vscode/srcdev-component-display-theme-switch.code-snippets +27 -0
  29. package/.vscode/srcdev-component-glowing-border.code-snippets +23 -0
  30. package/.vscode/srcdev-component-input-range.code-snippets +85 -0
  31. package/.vscode/srcdev-component-section-parallax.code-snippets +27 -0
  32. package/.vscode/srcdev-component-ui-block-decorated.code-snippets +24 -0
  33. package/.vscode/srcdev-component-wipe-away-vertical.code-snippets +25 -0
  34. package/README.md +18 -37
  35. package/app/components/{alert-mask → 01.atoms/alert-mask}/AlertMaskCore.vue +16 -17
  36. package/app/components/01.atoms/alert-mask/CONSUMER-STYLING.md +46 -0
  37. package/app/components/01.atoms/alert-mask/stories/AlertMaskCore.stories.ts +79 -0
  38. package/app/components/{alert-mask → 01.atoms/alert-mask}/tests/AlertMaskCore.spec.ts +10 -10
  39. package/app/components/01.atoms/animations/container-glow/CONSUMER-STYLING.md +86 -0
  40. package/app/components/01.atoms/animations/container-glow/ContainerGlow.vue +299 -0
  41. package/app/components/01.atoms/animations/container-glow/stories/ContainerGlow.stories.ts +175 -0
  42. package/app/components/01.atoms/animations/container-glow/tests/ContainerGlow.spec.ts +131 -0
  43. package/app/components/01.atoms/animations/container-glow/tests/__snapshots__/ContainerGlow.spec.ts.snap +14 -0
  44. package/app/components/01.atoms/animations/glowing-border/CONSUMER-STYLING.md +61 -0
  45. package/app/components/01.atoms/animations/glowing-border/GlowingBorder.vue +132 -0
  46. package/app/components/01.atoms/animations/glowing-border/stories/GlowingBorder.stories.ts +165 -0
  47. package/app/components/01.atoms/animations/glowing-border/tests/GlowingBorder.spec.ts +94 -0
  48. package/app/components/01.atoms/animations/glowing-border/tests/__snapshots__/GlowingBorder.spec.ts.snap +3 -0
  49. package/app/components/01.atoms/animations/section-parallax/CONSUMER-STYLING.md +44 -0
  50. package/app/components/{parallax → 01.atoms/animations/section-parallax}/SectionParallax.vue +11 -10
  51. package/app/components/01.atoms/animations/section-parallax/stories/SectionParallax.stories.ts +98 -0
  52. package/app/components/01.atoms/animations/section-parallax/tests/SectionParallax.spec.ts +102 -0
  53. package/app/components/01.atoms/animations/section-parallax/tests/__snapshots__/SectionParallax.spec.ts.snap +9 -0
  54. package/app/components/01.atoms/animations/view-timeline/CONSUMER-STYLING.md +70 -0
  55. package/app/components/01.atoms/animations/view-timeline/WipeAwayVertical.vue +181 -0
  56. package/app/components/01.atoms/animations/view-timeline/stories/WipeAwayVertical.stories.ts +269 -0
  57. package/app/components/01.atoms/animations/view-timeline/tests/WipeAwayVertical.spec.ts +127 -0
  58. package/app/components/01.atoms/canvas-switcher/CONSUMER-STYLING.md +1 -1
  59. package/app/components/01.atoms/canvas-switcher/stories/CanvasSwitcher.stories.ts +1 -1
  60. package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/CONSUMER-STYLING.md +42 -0
  61. package/app/components/{layout-grids/LayoutGridA.vue → 01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/DashboardQuadGrid.vue} +23 -13
  62. package/app/components/{layout-grids/stories/LayoutGridA.stories.ts → 01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/stories/DashboardQuadGrid.stories.ts} +151 -23
  63. package/app/components/{layout-grids/tests/LayoutGridA.spec.ts → 01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/tests/DashboardQuadGrid.spec.ts} +25 -25
  64. package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/CONSUMER-STYLING.md +41 -0
  65. package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/DashboardStatsGrid.vue +133 -0
  66. package/app/components/{layout-grids/stories/LayoutGridB.stories.ts → 01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/stories/DashboardStatsGrid.stories.ts} +24 -24
  67. package/app/components/{layout-grids/tests/LayoutGridB.spec.ts → 01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/tests/DashboardStatsGrid.spec.ts} +23 -23
  68. package/app/components/01.atoms/navigation/skip-links/SkipLinks.vue +7 -1
  69. package/app/components/01.atoms/ui-block-decorated/CONSUMER-STYLING.md +39 -0
  70. package/app/components/01.atoms/ui-block-decorated/UiBlockDecorated.vue +141 -0
  71. package/app/components/01.atoms/ui-block-decorated/stories/UiBlockDecorated.stories.ts +148 -0
  72. package/app/components/01.atoms/ui-block-decorated/tests/UiBlockDecorated.spec.ts +80 -0
  73. package/app/components/01.atoms/ui-block-decorated/tests/__snapshots__/UiBlockDecorated.spec.ts.snap +3 -0
  74. package/app/components/02.molecules/alert-masked-content/tests/AlertMaskedContent.spec.ts +6 -6
  75. package/app/components/02.molecules/display-theme-switch/CONSUMER-STYLING.md +34 -0
  76. package/app/components/02.molecules/display-theme-switch/DisplayThemeSwitch.vue +102 -0
  77. package/app/components/02.molecules/display-theme-switch/stories/DisplayThemeSwitch.stories.ts +115 -0
  78. package/app/components/02.molecules/display-theme-switch/tests/DisplayThemeSwitch.spec.ts +86 -0
  79. package/app/components/03.organisms/image-galleries/carousel-flip/CONSUMER-STYLING.md +46 -0
  80. package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/CarouselFlip.vue +63 -42
  81. package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/stories/CarouselFlip.stories.ts +96 -11
  82. package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/tests/CarouselFlip.spec.ts +80 -0
  83. package/app/components/05.forms/form-field/FormField.vue +1 -1
  84. package/app/components/05.forms/form-wrapper/FormWrapper.vue +1 -1
  85. package/app/components/05.forms/input-range/CONSUMER-STYLING.md +45 -0
  86. package/app/components/05.forms/input-range/InputRangeCore.vue +79 -143
  87. package/app/components/05.forms/input-range/stories/InputRangeCore.stories.ts +126 -0
  88. package/app/components/05.forms/input-range/tests/InputRangeCore.spec.ts +124 -0
  89. package/app/components/05.forms/input-range/variants/InputRangeDefault.vue +9 -32
  90. package/app/components/05.forms/input-range/variants/stories/InputRangeDefault.stories.ts +108 -0
  91. package/app/components/05.forms/input-range/variants/tests/InputRangeDefault.spec.ts +140 -0
  92. package/app/components/05.forms/patterns/stories/MigratedFieldsForm.stories.ts +135 -0
  93. package/app/components/05.forms/triple-toggle-switch/CONSUMER-STYLING.md +16 -2
  94. package/app/components/05.forms/triple-toggle-switch/TripleToggleSwitchCore.vue +6 -4
  95. package/app/types/components/container-glow.d.ts +8 -0
  96. package/app/types/components/index.ts +1 -1
  97. package/package.json +3 -2
  98. package/app/components/05.forms/input-range-fancy/InputRangeFancyCore.vue +0 -426
  99. package/app/components/05.forms/input-range-fancy/InputRangeFancyWithLabel.vue +0 -94
  100. package/app/components/carousels/CarouselBasic.vue +0 -317
  101. package/app/components/carousels/CarouselInfinite.vue +0 -358
  102. package/app/components/container-glow/ContainerGlowCore.vue +0 -296
  103. package/app/components/container-glow/stories/ContainerGlowCore.stories.ts +0 -337
  104. package/app/components/display-theme-switch/DisplayThemeSwitch.vue +0 -189
  105. package/app/components/display-theme-switch/stories/DisplayThemeSwitch.stories.ts +0 -154
  106. package/app/components/glowing-border/GlowingBorder.vue +0 -142
  107. package/app/components/glowing-border/stories/GlowingBorder.stories.ts +0 -141
  108. package/app/components/layout-grids/LayoutGridB.vue +0 -134
  109. package/app/components/test-storybook/TestStorybook.vue +0 -49
  110. package/app/components/test-storybook/stories/TestStorybook.stories.ts +0 -28
  111. package/app/components/ui-block-decorated/UiBlockDecorated.vue +0 -167
  112. package/app/components/view-timeline/WipeAwayVertical.vue +0 -180
  113. package/app/types/components/carousel-basic.d.ts +0 -19
@@ -0,0 +1,98 @@
1
+ # DashboardQuadGrid Component
2
+
3
+ ## Overview
4
+
5
+ `DashboardQuadGrid` (renamed from `LayoutGridA`) is a fixed 4-panel dashboard layout. Unlike
6
+ `LayoutGridByCols`/`LayoutGridByWidth`, it does not take a column-count prop — it has four
7
+ **named** slots (`slot1`–`slot4`) with a fixed responsive arrangement that changes shape (not
8
+ just column count) across breakpoints, designed for dashboard-style layouts (one primary panel
9
+ plus three supporting panels).
10
+
11
+ ---
12
+
13
+ ## Slot pattern
14
+
15
+ Exactly four fixed slot names — `slot1`, `slot2`, `slot3`, `slot4`. Not dynamic; each is a named
16
+ slot with a specific position in the layout at each breakpoint.
17
+
18
+ ```vue
19
+ <DashboardQuadGrid>
20
+ <template #slot1><ArticleCard /></template>
21
+ <template #slot2><StatsPanel /></template>
22
+ <template #slot3><QuickActions /></template>
23
+ <template #slot4><ActivityFeed /></template>
24
+ </DashboardQuadGrid>
25
+ ```
26
+
27
+ ---
28
+
29
+ ## Props reference
30
+
31
+ | Prop (template form) | Type | Default | Notes |
32
+ |------|------|---------|-------|
33
+ | `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes on the root element. |
34
+
35
+ ---
36
+
37
+ ## Responsive behaviour
38
+
39
+ Uses **CSS container queries** (`container-type: inline-size`) on the wrapper — responds to its
40
+ own container width, not the viewport.
41
+
42
+ - **Below 768px**: all four slots stack vertically in order (slot1 → slot2 → slot3 → slot4).
43
+ - **768px–1059px**: 2-column, 3-row grid. `slot1` and `slot2` each span the full width (rows 1
44
+ and 2); `slot3`/`slot4` sit side by side in row 3.
45
+ - **≥1060px**: 3-column, 2-row grid. `slot1` spans vertically down the left column; `slot2` spans
46
+ horizontally across the top-right; `slot3`/`slot4` sit in the bottom-right.
47
+
48
+ This shape is structural — there is no prop to reconfigure it. If you need an arbitrary
49
+ column count instead, use `LayoutGridByCols`/`LayoutGridByWidth`.
50
+
51
+ ---
52
+
53
+ ## CSS token API
54
+
55
+ See [CONSUMER-STYLING.md](../../app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/CONSUMER-STYLING.md).
56
+
57
+ | Token | Default | Controls |
58
+ |---|---|---|
59
+ | `--dashboard-quad-grid-gap` | `2rem` | Gap between the four slot panels |
60
+ | `--dashboard-quad-grid-padding` | `2rem` | Padding inside each slot panel |
61
+ | `--dashboard-quad-grid-outline-width` | `0.1rem` | Slot panel outline width |
62
+ | `--dashboard-quad-grid-outline-colour` | `light-dark(black, white)` | Slot panel outline colour |
63
+ | `--dashboard-quad-grid-outline-offset` | `0` | Slot panel outline offset |
64
+ | `--dashboard-quad-grid-border-width` | `0` | Slot panel border width (off by default — the panel edge is drawn by the outline) |
65
+ | `--dashboard-quad-grid-border-colour` | `light-dark(black, white)` | Slot panel border colour, only visible once `--dashboard-quad-grid-border-width` is set |
66
+ | `--dashboard-quad-grid-border-radius` | `0.5rem` | Slot panel corner radius |
67
+ | `--dashboard-quad-grid-rule-width` | `0.1rem` | Width of the grid gap rule line (CSS Gap Decorations, limited browser support) |
68
+ | `--dashboard-quad-grid-rule-colour` | `transparent` | Colour of the grid gap rule line, invisible by default |
69
+ | `--dashboard-quad-grid-rule-inset` | `0` | Inset of the grid gap rule line from the panel edges |
70
+
71
+ ---
72
+
73
+ ## Local style override scaffold
74
+
75
+ ```vue
76
+ <DashboardQuadGrid :style-class-passthrough="['my-grid']">
77
+ ...
78
+ </DashboardQuadGrid>
79
+
80
+ <style>
81
+ .dashboard-quad-grid-wrapper {
82
+ &.my-grid {
83
+ --dashboard-quad-grid-gap: 1.6rem;
84
+ }
85
+ }
86
+ </style>
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Notes
92
+
93
+ - Auto-imported in Nuxt — no manual import needed.
94
+ - Renamed from `LayoutGridA` during the 2026-09-21 compliance migration; previously lived
95
+ unplaced at `app/components/layout-grids/LayoutGridA.vue` with options-style props and no
96
+ public CSS tokens (its geometry values were hardcoded px literals).
97
+ - For a generic, arbitrary-column-count grid, use `LayoutGridByCols`/`LayoutGridByWidth` instead
98
+ — this component's 4-slot shape is fixed and specifically dashboard-oriented.
@@ -0,0 +1,103 @@
1
+ # DashboardStatsGrid Component
2
+
3
+ ## Overview
4
+
5
+ `DashboardStatsGrid` (renamed from `LayoutGridB`) is a dashboard layout combining a multi-panel
6
+ top row (a cluster of small stat/summary panels plus two static side panels) with a strip of
7
+ panels in the bottom row. Item counts in the two dynamic clusters are prop-driven; the two static
8
+ top-row slots and overall shape are fixed.
9
+
10
+ ---
11
+
12
+ ## Slot pattern
13
+
14
+ - `top-row-slot1-{n}-content` — dynamic, `n` from `1` to `topRowSlot1ItemCount`
15
+ - `top-row-slot-2` — static
16
+ - `top-row-slot-3` — static
17
+ - `bottom-row-{n}-content` — dynamic, `n` from `1` to `bottomRowItemCount`
18
+
19
+ ```vue
20
+ <DashboardStatsGrid :top-row-slot1-item-count="3" :bottom-row-item-count="2">
21
+ <template #top-row-slot1-1-content><StatCard label="Uptime" value="98.5%" /></template>
22
+ <template #top-row-slot1-2-content><StatCard label="Users" value="1,247" /></template>
23
+ <template #top-row-slot1-3-content><StatCard label="Latency" value="342ms" /></template>
24
+
25
+ <template #top-row-slot-2><FeaturedPanel /></template>
26
+ <template #top-row-slot-3><InfoPanel /></template>
27
+
28
+ <template #bottom-row-1-content><FeaturePanel /></template>
29
+ <template #bottom-row-2-content><FeaturePanel /></template>
30
+ </DashboardStatsGrid>
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Props reference
36
+
37
+ | Prop (template form) | Type | Default | Notes |
38
+ |------|------|---------|-------|
39
+ | `:top-row-slot1-item-count` | `number` | `6` | Number of dynamic panels rendered in the top-row cluster (`top-row-slot1-{n}-content`). |
40
+ | `:bottom-row-item-count` | `number` | `4` | Number of dynamic panels rendered in the bottom row (`bottom-row-{n}-content`). |
41
+ | `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes on the root element. |
42
+
43
+ ---
44
+
45
+ ## Responsive behaviour
46
+
47
+ Uses **CSS container queries** (`container-type: inline-size`) — responds to its own container
48
+ width, not the viewport.
49
+
50
+ - **Top row, below 1024px**: `slot1` cluster, `slot2`, `slot3` stack vertically.
51
+ - **Top row, ≥1024px**: two-column layout — `slot1` cluster + `slot3` on the left
52
+ (`minmax(46rem, 33%)` on the right for `slot2`), with `slot2` spanning both rows on the right.
53
+ - **`slot1` cluster, below 680px**: 2 columns.
54
+ - **`slot1` cluster, ≥680px**: 3 columns.
55
+ - **Bottom row**: always a fixed 2-column grid regardless of container width.
56
+
57
+ This shape is structural — there is no prop to reconfigure it.
58
+
59
+ ---
60
+
61
+ ## CSS token API
62
+
63
+ See [CONSUMER-STYLING.md](../../app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/CONSUMER-STYLING.md).
64
+
65
+ | Token | Default | Controls |
66
+ |---|---|---|
67
+ | `--dashboard-stats-grid-gap` | `1.2rem` | Gap between panels, in every row |
68
+ | `--dashboard-stats-grid-panel-padding` | `1.2rem` | Padding inside each panel |
69
+ | `--dashboard-stats-grid-border-width` | `0.1rem` | Panel border width |
70
+ | `--dashboard-stats-grid-border-colour` | `light-dark(hsl(0, 29%, 3%), hsl(0, 0%, 92%))` | Panel border colour |
71
+ | `--dashboard-stats-grid-border-radius` | `1.2rem` | Panel corner radius |
72
+ | `--dashboard-stats-grid-rule-width` | `0.1rem` | Width of the grid gap rule line (CSS Gap Decorations, limited browser support) |
73
+ | `--dashboard-stats-grid-rule-colour` | `transparent` | Colour of the grid gap rule line, invisible by default |
74
+ | `--dashboard-stats-grid-rule-inset` | `0` | Inset of the grid gap rule line from the panel edges |
75
+
76
+ ---
77
+
78
+ ## Local style override scaffold
79
+
80
+ ```vue
81
+ <DashboardStatsGrid :style-class-passthrough="['my-grid']">
82
+ ...
83
+ </DashboardStatsGrid>
84
+
85
+ <style>
86
+ .dashboard-stats-grid {
87
+ &.my-grid {
88
+ --dashboard-stats-grid-gap: 1.6rem;
89
+ }
90
+ }
91
+ </style>
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Notes
97
+
98
+ - Auto-imported in Nuxt — no manual import needed.
99
+ - Renamed from `LayoutGridB` during the 2026-09-21 compliance migration; previously lived
100
+ unplaced at `app/components/layout-grids/LayoutGridB.vue` with options-style props. Its old
101
+ `--_color` custom property was dead code (declared, never consumed) and was removed rather than
102
+ promoted; `--_gap`/`--_border-color` had no public fallback and are now
103
+ `--dashboard-stats-grid-gap`/`--dashboard-stats-grid-border-colour`.
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: DisplayThemeSwitch
3
+ description: DisplayThemeSwitch system/light/dark theme picker wired to useSettingsStore, wraps TripleToggleSwitchCore
4
+ type: reference
5
+ ---
6
+
7
+ # DisplayThemeSwitch
8
+
9
+ ## Overview
10
+
11
+ `DisplayThemeSwitch` is a system/light/dark colour-scheme picker. It's a thin wrapper around
12
+ `TripleToggleSwitchCore` (`app/components/05.forms/triple-toggle-switch/`), wired to
13
+ `useSettingsStore` (`app/stores/useSettingsStore.ts`) so selecting an option updates the app's
14
+ `colourScheme` state and applies the matching class (`system`/`light`/`dark`) to
15
+ `<html>`. Renders inside `<ClientOnly>` since the store's persisted value isn't known until
16
+ hydration.
17
+
18
+ ## Props
19
+
20
+ | Prop | Type | Default | Description |
21
+ |------|------|---------|-------------|
22
+ | `styleClassPassthrough` | `string \| string[]` | `[]` | Extra classes on the root. Pass `"small"` for compact sizing. |
23
+ | `systemLabel` | `string` | `"System"` | Accessible label for the system option. |
24
+ | `lightLabel` | `string` | `"Light"` | Accessible label for the light option. |
25
+ | `darkLabel` | `string` | `"Dark"` | Accessible label for the dark option. |
26
+ | `systemIcon` | `string` | `"material-symbols:night-sight-auto-sharp"` | Icon name for the system option. |
27
+ | `lightIcon` | `string` | `"radix-icons:sun"` | Icon name for the light option. |
28
+ | `darkIcon` | `string` | `"radix-icons:moon"` | Icon name for the dark option. |
29
+
30
+ ## Basic usage
31
+
32
+ ```vue
33
+ <DisplayThemeSwitch />
34
+
35
+ <!-- Compact, for a header/nav slot -->
36
+ <DisplayThemeSwitch style-class-passthrough="small" />
37
+
38
+ <!-- Translated labels -->
39
+ <DisplayThemeSwitch system-label="Automatique" light-label="Clair" dark-label="Sombre" />
40
+ ```
41
+
42
+ ## Styling
43
+
44
+ No `--display-theme-switch-*` tokens of its own — every visual aspect (colour, marker gradient,
45
+ sizing) is delegated to `TripleToggleSwitchCore`'s public `--triple-toggle-switch-*` tokens. See
46
+ CONSUMER-STYLING.md here and `TripleToggleSwitchCore`'s own CONSUMER-STYLING.md.
47
+
48
+ ## Notes
49
+
50
+ - 2026-09-20 migration: moved from `app/components/display-theme-switch/` (unplaced) into
51
+ `02.molecules/`. Added `systemLabel`/`lightLabel`/`darkLabel`/`systemIcon`/`lightIcon`/`darkIcon`
52
+ props — the three option labels/icons were previously hardcoded English strings with no override
53
+ hook. Removed a large block of dead/duplicate CSS in this component's own `<style>` that either
54
+ no-op-duplicated `TripleToggleSwitchCore`'s own default styling (same selectors, same values) or
55
+ actively bypassed its public `--triple-toggle-switch-*` tokens with hardcoded literals (a
56
+ consumer setting e.g. `--triple-toggle-switch-surface` globally would have been silently
57
+ overridden back). Only the genuine `small`-variant sizing override remains, now driving
58
+ `TripleToggleSwitchCore`'s own public sizing tokens (`--triple-toggle-switch-gap`/`-padding`/
59
+ `-option-padding`/`-icon-size`, added in the same migration) instead of reaching into its
60
+ private `--_form-*`/`--_scheme-icon-font-size` locals directly.
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: GlowingBorder
3
+ description: GlowingBorder decorative wrapper with an animated conic-gradient glow border in 5 colour variants
4
+ type: reference
5
+ ---
6
+
7
+ # GlowingBorder
8
+
9
+ ## Overview
10
+
11
+ `GlowingBorder` wraps its default slot content in a rotating conic-gradient border effect, built
12
+ with `@property`-registered custom properties so the hue rotation animates smoothly. It renders as
13
+ a configurable root element via the `tag` prop.
14
+
15
+ ## Props
16
+
17
+ | Prop | Type | Default | Description |
18
+ |------|------|---------|-------------|
19
+ | `tag` | `"div" \| "p" \| "span" \| "section" \| "article" \| "aside" \| "header" \| "footer" \| "main" \| "nav" \| "ul" \| "ol"` | `"div"` | Root element tag. |
20
+ | `variant` | `"subtle" \| "vivid" \| "silver" \| "steel" \| "green"` | `"subtle"` | Glow colour variant. |
21
+ | `styleClassPassthrough` | `string \| string[]` | `[]` | Extra classes applied to the root element. |
22
+
23
+ ## Slots
24
+
25
+ | Slot | Description |
26
+ |---|---|
27
+ | `default` | Content rendered inside the glowing border. |
28
+
29
+ ## Basic usage
30
+
31
+ ```vue
32
+ <GlowingBorder variant="vivid">
33
+ <p>Content wrapped in a glowing border</p>
34
+ </GlowingBorder>
35
+ ```
36
+
37
+ ## Styling
38
+
39
+ Border width, radius, surface colour, animation duration, and every variant's 5 gradient colour
40
+ stops are public CSS custom properties — see `CONSUMER-STYLING.md` for the full token table.
41
+
42
+ ```vue
43
+ <GlowingBorder
44
+ variant="steel"
45
+ style="--glowing-border-width: 2px; --glowing-border-animation-duration: 6s;"
46
+ >
47
+ <p>Slower, thinner steel glow</p>
48
+ </GlowingBorder>
49
+ ```
50
+
51
+ ## Motion
52
+
53
+ The glow animation stops automatically under `prefers-reduced-motion: reduce`.
54
+
55
+ ## Notes
56
+
57
+ - 2026-09-20 migration: moved from `app/components/glowing-border/` (unplaced) into
58
+ `01.atoms/animations/glowing-border/`; converted from options-style `defineProps({...})` to
59
+ `interface Props` + `withDefaults`; promoted border width/radius/surface/animation-duration and
60
+ every variant's 5 colour stops (previously hardcoded or unprefixed `--border-width`/`--surface`)
61
+ to public `--glowing-border-*` tokens; renamed internal composition custom properties
62
+ (`--clr-1..5`, `--glow-deg`, `--gradient-glow`) to private `--_clr-1..5`/`--_glow-deg`/
63
+ `--_gradient-glow`; added a `prefers-reduced-motion` guard. No behaviour change beyond the new
64
+ override surface.
65
+ - 2026-09-20: added `green` variant (vibrant, saturated greens: `#00ff87`, `#39ff14`, `#00c853`,
66
+ `#64dd17`, `#00e676`), each stop overridable via `--glowing-border-green-color-{1-5}`.
@@ -0,0 +1,142 @@
1
+ # InputRangeCore Component
2
+
3
+ ## Overview
4
+
5
+ `InputRangeCore` is the low-level native `<input type="range">` primitive for the `05.forms`
6
+ range-slider family. It renders a single native range input with left/right button slots (for
7
+ step-down/step-up controls), an optional `markers` slot for custom tick marks overlaid on the
8
+ track, and an optional `datalist` slot for native tick-mark snapping. It has no label,
9
+ description, or error-message rendering of its own — that's composed by the `InputRangeDefault`
10
+ variant below.
11
+
12
+ Most consumers should reach for **InputRangeDefault** rather than `InputRangeCore` directly.
13
+
14
+ ---
15
+
16
+ ## Props reference
17
+
18
+ > **Hyphenation rule**: Vue's ESLint config enforces `vue/attribute-hyphenation`. Always write camelCase prop names hyphenated in templates: `:field-has-error`, `:style-class-passthrough`, `:aria-describedby`.
19
+
20
+ | Prop (template form) | Type | Default | Notes |
21
+ |------|------|---------|-------|
22
+ | `:id` | `string` | (required) | Applied to the native input. |
23
+ | `:name` | `string` | (required) | Applied to the native input, and used to derive the `list` id when the `datalist` slot is used (`${name}-datalist`). |
24
+ | `:min` | `number` | (required) | Native `min`. |
25
+ | `:max` | `number` | (required) | Native `max`. |
26
+ | `:step` | `number` | `1` | Native `step`. |
27
+ | `:required` | `boolean` | `false` | Native `required`. |
28
+ | `:placeholder` | `string` | `""` | Declared but not rendered — range inputs have no placeholder concept. Kept for prop-shape consistency with sibling `05.forms` components. |
29
+ | `:theme` | `FormUiTheme` | `"default"` | Sets `data-theme` on the wrapper. |
30
+ | `:weight` | `FormWeight` | `"normal"` | Adds an `input-range--{weight}` class to the input. **No CSS in the library currently styles this class — it's a no-op today**, kept for prop-shape consistency with sibling components. |
31
+ | `:field-has-error` | `boolean` | `false` | Declared, but doesn't drive any visible state inside `InputRangeCore` itself — `InputRangeDefault` handles the actual error styling (`data-invalid`/`.error` class) on its own wrapper. |
32
+ | `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes applied to the input element. |
33
+ | `:aria-describedby` | `string` | `""` | Forwarded to the native input. |
34
+
35
+ ### v-model
36
+
37
+ `v-model` — `number`, **required** (`defineModel<number>({ required: true })`). Bound directly to
38
+ the native range input. Required rather than defaulted deliberately — a real `<input type="range">`
39
+ always has a concrete value (there's no meaningful "empty" range slider), so a default here would
40
+ be an artificial value rather than reflecting actual native behaviour. This also satisfies
41
+ `vue/require-default-prop`, which otherwise flags any `defineModel()` without a default — see
42
+ `project_ledger_eslint_issues_column` in memory for why that's the correct fix (not a rule
43
+ disable, not a fake default).
44
+
45
+ ---
46
+
47
+ ## Slots
48
+
49
+ | Slot | Notes |
50
+ |------|-------|
51
+ | `left` | Rendered before the track (e.g. a step-down button). |
52
+ | `right` | Rendered after the track (e.g. a step-up button). |
53
+ | `markers` | Rendered as an overlay on top of the track (`grid-area: element-stack`, same cell as the input). Adds a `has-markers` class to the input, which shrinks its visible track to a thin `0.2rem` strip and hides the native thumb, so your marker content becomes the primary visual. Expected shape: a `.input-range-markers` wrapper containing `.marker` elements (each optionally containing a `.marker-icon`) — see the component's own scoped CSS for the classes it pre-styles. |
54
+ | `datalist` | Rendered after the input. Must contain a real `<datalist>` element whose `id` matches `${name}-datalist` for the browser to link it via the input's `list` attribute — the component computes that id string for the `list` attribute itself, but does not set the `id` on your slotted `<datalist>` for you. |
55
+
56
+ ---
57
+
58
+ ## CSS token API
59
+
60
+ See [CONSUMER-STYLING.md](../../app/components/05.forms/input-range/CONSUMER-STYLING.md) for the
61
+ full table with defaults.
62
+
63
+ **Common tokens:**
64
+ - `--input-range-accent-colour` — native `accent-color` (track/thumb tint), falls back to `--theme-accent`
65
+ - `--input-range-marker-background-colour` / `-outline-colour` — marker dot styling
66
+ - `--input-range-marker-icon-colour` — falls back to `--input-range-accent-colour`
67
+
68
+ Thumb/track height and the button-slot's button size come from the shared global geometry token
69
+ `--input-range-button-size` (declared in `03.generic-input-geometry.css`, not owned by this
70
+ component), matching the convention `theming-form-geometry-tokens.md` documents for the rest of
71
+ `05.forms`.
72
+
73
+ ---
74
+
75
+ ## Fixed 2026-09-21: dead `accent-color` token
76
+
77
+ `accent-color` previously read `--theme-form-range-accent-color`, a custom property that was never
78
+ declared anywhere in the codebase, with no fallback. Per the CSS spec, an undefined `var()` with
79
+ no fallback makes the whole declaration invalid at computed-value time — so `accent-color` was
80
+ silently falling back to the browser's native default (an OS-dependent blue) instead of the
81
+ intended theme colour, in every place it was used (the input itself, its `.has-markers` state, and
82
+ the marker icon colour). This is the same bug class found in `CarouselFlip`'s edge-preview tokens
83
+ around the same time — check any component reaching for a `--theme-*`-prefixed custom property for
84
+ a real declaration of that name before trusting it renders correctly.
85
+
86
+ Also removed: a broken `::-webkit-slider-thumb` rule (`background-color: 0.1rem solid green` is
87
+ invalid CSS and was silently dropped; `accent-color`/`color` don't apply to that pseudo-element at
88
+ all) and ~60 lines of dead commented-out alternative thumb/track styling — none of it had any
89
+ visible effect, since `appearance: none` (required for custom thumb/track styling to take effect)
90
+ was itself commented out.
91
+
92
+ ---
93
+
94
+ ## Variants
95
+
96
+ ### InputRangeDefault
97
+
98
+ `InputRangeDefault` (`variants/InputRangeDefault.vue`) composes `InputRangeCore` with `InputLabel`,
99
+ `InputDescription`, and `InputError`, plus optional step-down/step-up buttons (`InputButtonCore`)
100
+ wired into the `left`/`right` slots.
101
+
102
+ **Additional props over InputRangeCore:**
103
+
104
+ | Prop (template form) | Type | Default | Notes |
105
+ |------|------|---------|-------|
106
+ | `:label` | `string` | (required) | Rendered via `InputLabel`. |
107
+ | `:error-message` | `object \| string` | (required) | Rendered via `InputError` when `field-has-error` is true. |
108
+ | `:step-down-label` | `string` | `"Step down"` | `button-text` on the step-down `InputButtonCore` — override for localisation. |
109
+ | `:step-up-label` | `string` | `"Step up"` | `button-text` on the step-up `InputButtonCore` — override for localisation. |
110
+
111
+ **Slots**: `descriptionHtml`, `descriptionText` (both forwarded to `InputDescription`), plus
112
+ `left`/`right`/`markers`/`datalist` (forwarded straight through to the underlying
113
+ `InputRangeCore` — the step-down/step-up buttons only render when you use the `left`/`right`
114
+ slots yourself, since the button icon content itself is consumer-supplied via `#iconOnly`).
115
+
116
+ ```vue
117
+ <InputRangeDefault
118
+ name="priceRange"
119
+ label="Price range"
120
+ v-model="price"
121
+ :min="0"
122
+ :max="500"
123
+ :step="10"
124
+ error-message="Please choose a value within the allowed range"
125
+ :field-has-error="hasError"
126
+ >
127
+ <template #left><Icon name="mdi:minus" /></template>
128
+ <template #right><Icon name="mdi:plus" /></template>
129
+ </InputRangeDefault>
130
+ ```
131
+
132
+ `v-model` is `number`, **required** — same as `InputRangeCore` above.
133
+
134
+ **Fixed 2026-09-21**: removed a dead `deepCssClassPassthrough` prop (declared, never consumed
135
+ anywhere in the component or referenced elsewhere in the codebase) and a dead `<style>` block
136
+ targeting `.input-range-label`/`.label-description` — classes that nothing in this component (or
137
+ `InputLabel`/`InputDescription`) actually renders, so the block had no effect. Also removed two
138
+ dead commented-out lines (`// const id = useId();`, a duplicate no-op `FormUiTheme` computed), and
139
+ (same day, follow-up pass) a `const props = withDefaults(...)` in both `InputRangeCore` and
140
+ `InputRangeDefault` that had become entirely unused once the dead `FormUiTheme` computed was
141
+ removed — the template's `:id`/`:name`/etc. shorthand bindings resolve straight from `defineProps`
142
+ automatically in `<script setup>`, no destructuring or `props.` reference needed.
@@ -52,20 +52,37 @@ type: reference
52
52
  ## How it works
53
53
 
54
54
  The component sets:
55
+
55
56
  - `background-image: url(...)` via `v-bind`
56
- - `background-position: center`
57
- - `background-size: inherit` (fallback when fixed is not supported)
58
- - `min-height: 120vh`
59
- - `background-color: light-dark(var(--slate-01), var(--slate-08))` (visible if image fails to load)
57
+ - `background-position: var(--section-parallax-background-position, center)`
58
+ - `background-size: var(--section-parallax-background-size, cover)`
59
+ - `min-height: var(--section-parallax-min-height, 100svh)`
60
+ - `background-color: var(--section-parallax-background-colour, light-dark(var(--slate-01), var(--slate-08)))` (visible if image fails to load)
61
+
62
+ Inside `@media (hover: hover) and (pointer: fine) { @supports (background-attachment: fixed) { ... } }`:
60
63
 
61
- Inside `@supports (background-attachment: fixed)`:
62
64
  - `background-attachment: fixed` — pins the image to the viewport
63
- - `background-size: cover` — ensures the image fills the viewport
64
- - `min-height: 120vh` — ensures enough scroll travel to see the parallax motion
65
+ - `min-height: var(--section-parallax-min-height-fixed, 120vh)` — ensures enough scroll travel to see the parallax motion
66
+
67
+ Under `@media (prefers-reduced-motion: reduce)`, the parallax is disabled regardless of hover/
68
+ `@supports` support: `background-attachment` reverts to `scroll` and height reverts to
69
+ `--section-parallax-min-height`.
65
70
 
66
- ## Controlling height
71
+ ## Styling
72
+
73
+ Height, background position/size/colour are all public CSS custom properties — see
74
+ `CONSUMER-STYLING.md` for the full token table.
75
+
76
+ ```vue
77
+ <SectionParallax
78
+ background-image="/images/candle-and-stones.jpg"
79
+ style="--section-parallax-min-height: 60vh; --section-parallax-min-height-fixed: 70vh;"
80
+ >
81
+ <p>Shorter atmospheric break</p>
82
+ </SectionParallax>
83
+ ```
67
84
 
68
- Override `min-height` with a consuming-page style:
85
+ Alternatively, override `min-height` with a consuming-page style:
69
86
 
70
87
  ```css
71
88
  .my-page {
@@ -98,5 +115,10 @@ Override `min-height` with a consuming-page style:
98
115
  ## Notes
99
116
 
100
117
  - The `@supports` guard means the parallax activates only when the browser supports `background-attachment: fixed`. No JS is involved.
101
- - Slot content is only rendered when the `default` slot is provided (`v-if="slots.default"`).
102
118
  - The component has no built-in overlay or gradient — add one via the slot or a `::before` pseudo-element in your consuming-page styles.
119
+ - 2026-09-20 migration: moved from `app/components/parallax/` (unplaced) into
120
+ `01.atoms/animations/section-parallax/`; props pattern was already `interface Props` +
121
+ `withDefaults` (no change needed); promoted min-height/background-position/background-size/
122
+ background-colour (previously hardcoded) to public `--section-parallax-*` tokens; added a
123
+ `prefers-reduced-motion: reduce` guard that disables the fixed-attachment parallax. No behaviour
124
+ change beyond the new override surface and the reduced-motion opt-out.
@@ -0,0 +1,56 @@
1
+ ---
2
+ name: UiBlockDecorated
3
+ description: UiBlockDecorated plain block wrapper with independent border/shadow/inner-shadow strength levels — props, CSS tokens
4
+ type: reference
5
+ ---
6
+
7
+ # UiBlockDecorated
8
+
9
+ ## Overview
10
+
11
+ `UiBlockDecorated` renders its slot content in a plain block element (configurable `tag`) with
12
+ three independent, optional decorations: a border, a drop shadow, and an inset ("inner") shadow.
13
+ Each decoration has its own 0-based "strength" prop that selects a level from a built-in scale;
14
+ `0` (the default) applies no class for that decoration, so all three are opt-in and combinable.
15
+
16
+ ## Props
17
+
18
+ | Prop | Type | Default | Description |
19
+ |------|------|---------|-------------|
20
+ | `tag` | `"div" \| "p" \| "span" \| "section" \| "article" \| "aside" \| "header" \| "footer" \| "main" \| "nav" \| "ul" \| "ol"` | `"div"` | Root element tag. |
21
+ | `borderStrength` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `0` | Border strength level; `0` = no border. |
22
+ | `shadowStrength` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `0` | Drop shadow strength level; `0` = no shadow. |
23
+ | `innerShadowStrength` | `0 \| 1 \| 2 \| 3 \| 4` | `0` | Inset shadow strength level; `0` = no inner shadow. |
24
+ | `styleClassPassthrough` | `string \| string[]` | `[]` | Extra classes applied to the root element. |
25
+
26
+ ## Basic usage
27
+
28
+ ```vue
29
+ <UiBlockDecorated :shadow-strength="3">
30
+ <p>Block content</p>
31
+ </UiBlockDecorated>
32
+
33
+ <UiBlockDecorated tag="section" :border-strength="2" :inner-shadow-strength="1">
34
+ <p>Block content</p>
35
+ </UiBlockDecorated>
36
+ ```
37
+
38
+ ## CSS custom properties
39
+
40
+ Each strength level has its own public token — see CONSUMER-STYLING.md for the full table. In
41
+ short: `--ui-block-decorated-border-1` .. `-6`, `--ui-block-decorated-shadow-1` .. `-6`,
42
+ `--ui-block-decorated-inner-shadow-1` .. `-4`, and `--ui-block-decorated-inner-shadow-highlight`
43
+ (layered on top of every inner shadow level).
44
+
45
+ ## Notes
46
+
47
+ - 2026-09-16 migration: previously used options-style `defineProps` and referenced six families of
48
+ global tokens (`--border-1`..`-6`, `--shadow-1`..`-6`, `--inner-shadow-1`..`-4`,
49
+ `--inner-shadow-highlight`) that were never declared anywhere in the codebase — every border,
50
+ shadow and inner shadow rendered as nothing. Fixed by giving each strength level its own
51
+ component-scoped public token (`--ui-block-decorated-*`) with a real fallback value baked in, so
52
+ the decorations work out of the box and are still fully overridable.
53
+ - The applied classes were also renamed from bare `.border-1`/`.shadow-1`/`.inner-shadow-1` to
54
+ `.ui-block-decorated-border-1` etc. — the un-prefixed names risked colliding with a consumer
55
+ app's own utility classes, since this component renders inline in the consumer's DOM (not
56
+ teleported/portaled).