srcdev-nuxt-components 9.4.9 → 9.4.11
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/.claude/commands/migrate-component.md +38 -6
- package/.claude/component-ledger/audit.json +1 -1
- package/.claude/component-ledger/build.mjs +26 -0
- package/.claude/component-ledger/output.html +8 -3
- package/.claude/component-ledger/template.html +7 -2
- package/.claude/skills/component-dynamic-slots.md +1 -1
- package/.claude/skills/components/alert-mask-core.md +84 -0
- package/.claude/skills/components/canvas-switcher.md +1 -1
- package/.claude/skills/components/carousel-flip.md +112 -168
- package/.claude/skills/components/container-glow.md +123 -0
- package/.claude/skills/components/dashboard-quad-grid.md +98 -0
- package/.claude/skills/components/dashboard-stats-grid.md +103 -0
- package/.claude/skills/components/display-theme-switch.md +60 -0
- package/.claude/skills/components/glowing-border.md +66 -0
- package/.claude/skills/components/input-number-core.md +214 -0
- package/.claude/skills/components/input-range-core.md +142 -0
- package/.claude/skills/components/input-select-core.md +151 -0
- package/.claude/skills/components/input-text-core.md +16 -0
- package/.claude/skills/components/input-textarea-core.md +154 -0
- package/.claude/skills/components/section-parallax.md +32 -10
- package/.claude/skills/components/select-menu.md +195 -0
- package/.claude/skills/components/toggle-switch-core.md +150 -0
- package/.claude/skills/components/ui-block-decorated.md +56 -0
- package/.claude/skills/components/wipe-away-vertical.md +98 -0
- package/.claude/skills/icon-sets.md +1 -1
- package/.claude/skills/index.md +15 -1
- package/.vscode/css-custom-data.json +38 -0
- package/.vscode/settings.json +3 -0
- package/.vscode/srcdev-component-alert-mask-core.code-snippets +31 -0
- package/.vscode/srcdev-component-carousel-flip.code-snippets +41 -0
- package/.vscode/srcdev-component-container-glow.code-snippets +33 -0
- package/.vscode/srcdev-component-dashboard-quad-grid.code-snippets +36 -0
- package/.vscode/srcdev-component-dashboard-stats-grid.code-snippets +35 -0
- package/.vscode/srcdev-component-display-theme-switch.code-snippets +27 -0
- package/.vscode/srcdev-component-glowing-border.code-snippets +23 -0
- package/.vscode/srcdev-component-input-number.code-snippets +65 -0
- package/.vscode/srcdev-component-input-range.code-snippets +85 -0
- package/.vscode/srcdev-component-input-select.code-snippets +42 -0
- package/.vscode/srcdev-component-input-textarea.code-snippets +40 -0
- package/.vscode/srcdev-component-section-parallax.code-snippets +27 -0
- package/.vscode/srcdev-component-select-menu.code-snippets +65 -0
- package/.vscode/srcdev-component-toggle-switch.code-snippets +49 -0
- package/.vscode/srcdev-component-ui-block-decorated.code-snippets +24 -0
- package/.vscode/srcdev-component-wipe-away-vertical.code-snippets +25 -0
- package/README.md +18 -37
- package/app/components/{alert-mask → 01.atoms/alert-mask}/AlertMaskCore.vue +16 -17
- package/app/components/01.atoms/alert-mask/CONSUMER-STYLING.md +46 -0
- package/app/components/01.atoms/alert-mask/stories/AlertMaskCore.stories.ts +79 -0
- package/app/components/{alert-mask → 01.atoms/alert-mask}/tests/AlertMaskCore.spec.ts +10 -10
- package/app/components/01.atoms/animations/container-glow/CONSUMER-STYLING.md +86 -0
- package/app/components/01.atoms/animations/container-glow/ContainerGlow.vue +299 -0
- package/app/components/01.atoms/animations/container-glow/stories/ContainerGlow.stories.ts +175 -0
- package/app/components/01.atoms/animations/container-glow/tests/ContainerGlow.spec.ts +131 -0
- package/app/components/01.atoms/animations/container-glow/tests/__snapshots__/ContainerGlow.spec.ts.snap +14 -0
- package/app/components/01.atoms/animations/glowing-border/CONSUMER-STYLING.md +61 -0
- package/app/components/01.atoms/animations/glowing-border/GlowingBorder.vue +132 -0
- package/app/components/01.atoms/animations/glowing-border/stories/GlowingBorder.stories.ts +165 -0
- package/app/components/01.atoms/animations/glowing-border/tests/GlowingBorder.spec.ts +94 -0
- package/app/components/01.atoms/animations/glowing-border/tests/__snapshots__/GlowingBorder.spec.ts.snap +3 -0
- package/app/components/01.atoms/animations/marquee-scroller/CONSUMER-STYLING.md +1 -1
- package/app/components/01.atoms/animations/marquee-scroller/MarqueeScroller.vue +1 -1
- package/app/components/01.atoms/animations/marquee-scroller/stories/MarqueeScroller.stories.ts +1 -1
- package/app/components/01.atoms/animations/section-parallax/CONSUMER-STYLING.md +44 -0
- package/app/components/{parallax → 01.atoms/animations/section-parallax}/SectionParallax.vue +11 -10
- package/app/components/01.atoms/animations/section-parallax/stories/SectionParallax.stories.ts +98 -0
- package/app/components/01.atoms/animations/section-parallax/tests/SectionParallax.spec.ts +102 -0
- package/app/components/01.atoms/animations/section-parallax/tests/__snapshots__/SectionParallax.spec.ts.snap +9 -0
- package/app/components/01.atoms/animations/view-timeline/CONSUMER-STYLING.md +70 -0
- package/app/components/01.atoms/animations/view-timeline/WipeAwayVertical.vue +181 -0
- package/app/components/01.atoms/animations/view-timeline/stories/WipeAwayVertical.stories.ts +269 -0
- package/app/components/01.atoms/animations/view-timeline/tests/WipeAwayVertical.spec.ts +127 -0
- package/app/components/01.atoms/canvas-switcher/CONSUMER-STYLING.md +3 -3
- package/app/components/01.atoms/canvas-switcher/CanvasSwitcher.vue +2 -2
- package/app/components/01.atoms/canvas-switcher/stories/CanvasSwitcher.stories.ts +1 -1
- package/app/components/01.atoms/card/CardCore.vue +4 -4
- package/app/components/01.atoms/clipped-panel/CONSUMER-STYLING.md +2 -2
- package/app/components/01.atoms/clipped-panel/ClippedPanel.vue +2 -2
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/CONSUMER-STYLING.md +42 -0
- package/app/components/{layout-grids/LayoutGridA.vue → 01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/DashboardQuadGrid.vue} +23 -13
- package/app/components/{layout-grids/stories/LayoutGridA.stories.ts → 01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/stories/DashboardQuadGrid.stories.ts} +151 -23
- package/app/components/{layout-grids/tests/LayoutGridA.spec.ts → 01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/tests/DashboardQuadGrid.spec.ts} +25 -25
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/CONSUMER-STYLING.md +41 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/DashboardStatsGrid.vue +133 -0
- package/app/components/{layout-grids/stories/LayoutGridB.stories.ts → 01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/stories/DashboardStatsGrid.stories.ts} +24 -24
- package/app/components/{layout-grids/tests/LayoutGridB.spec.ts → 01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/tests/DashboardStatsGrid.spec.ts} +23 -23
- package/app/components/01.atoms/display-tooltip/CONSUMER-STYLING.md +8 -8
- package/app/components/01.atoms/display-tooltip/DisplayTooltip.vue +8 -8
- package/app/components/01.atoms/pop-over/CONSUMER-STYLING.md +6 -6
- package/app/components/01.atoms/pop-over/PopOver.vue +6 -6
- package/app/components/01.atoms/ui-block-decorated/CONSUMER-STYLING.md +39 -0
- package/app/components/01.atoms/ui-block-decorated/UiBlockDecorated.vue +141 -0
- package/app/components/01.atoms/ui-block-decorated/stories/UiBlockDecorated.stories.ts +148 -0
- package/app/components/01.atoms/ui-block-decorated/tests/UiBlockDecorated.spec.ts +80 -0
- package/app/components/01.atoms/ui-block-decorated/tests/__snapshots__/UiBlockDecorated.spec.ts.snap +3 -0
- package/app/components/02.molecules/alert-masked-content/tests/AlertMaskedContent.spec.ts +6 -6
- package/app/components/02.molecules/display-theme-switch/CONSUMER-STYLING.md +34 -0
- package/app/components/02.molecules/display-theme-switch/DisplayThemeSwitch.vue +102 -0
- package/app/components/02.molecules/display-theme-switch/stories/DisplayThemeSwitch.stories.ts +115 -0
- package/app/components/02.molecules/display-theme-switch/tests/DisplayThemeSwitch.spec.ts +86 -0
- package/app/components/02.molecules/navigation/deep-expanding-menu/CONSUMER-STYLING.md +1 -1
- package/app/components/02.molecules/navigation/deep-expanding-menu/DeepExpandingMenu.vue +1 -1
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/CONSUMER-STYLING.md +1 -1
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/DeepExpandingMenuClassic.vue +1 -1
- package/app/components/02.molecules/select-menu/CONSUMER-STYLING.md +168 -0
- package/app/components/02.molecules/select-menu/SelectMenu.vue +384 -0
- package/app/components/02.molecules/select-menu/stories/SelectMenu.stories.ts +359 -0
- package/app/components/02.molecules/select-menu/tests/SelectMenu.spec.ts +476 -0
- package/app/components/02.molecules/select-menu/tests/__snapshots__/SelectMenu.spec.ts.snap +42 -0
- package/app/components/03.organisms/image-galleries/carousel-flip/CONSUMER-STYLING.md +46 -0
- package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/CarouselFlip.vue +63 -42
- package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/stories/CarouselFlip.stories.ts +114 -29
- package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/tests/CarouselFlip.spec.ts +80 -0
- package/app/components/05.forms/form-field/FormField.vue +1 -1
- package/app/components/05.forms/form-wrapper/FormWrapper.vue +1 -1
- package/app/components/05.forms/input-button/CONSUMER-STYLING.md +2 -7
- package/app/components/05.forms/input-button/InputButtonCore.vue +1 -5
- package/app/components/05.forms/input-number/CONSUMER-STYLING.md +26 -6
- package/app/components/05.forms/input-number/InputNumberCore.vue +125 -85
- package/app/components/05.forms/input-number/stories/InputNumberCore.stories.ts +92 -0
- package/app/components/05.forms/input-number/tests/InputNumberCore.spec.ts +119 -0
- package/app/components/05.forms/input-number/variants/InputNumberDefault.vue +30 -22
- package/app/components/05.forms/input-number/variants/stories/InputNumberDefault.stories.ts +123 -0
- package/app/components/05.forms/input-number/variants/tests/InputNumberDefault.spec.ts +143 -0
- package/app/components/05.forms/input-range/CONSUMER-STYLING.md +45 -0
- package/app/components/05.forms/input-range/InputRangeCore.vue +79 -143
- package/app/components/05.forms/input-range/stories/InputRangeCore.stories.ts +126 -0
- package/app/components/05.forms/input-range/tests/InputRangeCore.spec.ts +124 -0
- package/app/components/05.forms/input-range/variants/InputRangeDefault.vue +9 -32
- package/app/components/05.forms/input-range/variants/stories/InputRangeDefault.stories.ts +108 -0
- package/app/components/05.forms/input-range/variants/tests/InputRangeDefault.spec.ts +140 -0
- package/app/components/05.forms/input-select/InputSelectCore.vue +23 -30
- package/app/components/05.forms/input-select/stories/InputSelectCore.stories.ts +2 -2
- package/app/components/05.forms/input-select/stories/InputSelectWithLabel.stories.ts +115 -0
- package/app/components/05.forms/input-select/tests/InputSelectCore.spec.ts +129 -0
- package/app/components/05.forms/input-select/variants/InputSelectWithLabel.vue +1 -1
- package/app/components/05.forms/input-select/variants/tests/InputSelectWithLabel.spec.ts +87 -0
- package/app/components/05.forms/input-text/tests/InputTextAsNumberWithLabel.spec.ts +14 -0
- package/app/components/05.forms/input-text/variants/InputTextAsNumberWithLabel.vue +1 -1
- package/app/components/05.forms/input-textarea/CONSUMER-STYLING.md +55 -0
- package/app/components/05.forms/input-textarea/InputTextareaCore.vue +24 -16
- package/app/components/05.forms/input-textarea/stories/InputTextareaWithLabel.stories.ts +1 -1
- package/app/components/05.forms/input-textarea/tests/InputTextareaCore.spec.ts +119 -0
- package/app/components/05.forms/input-textarea/variants/InputTextareaWithLabel.vue +3 -13
- package/app/components/05.forms/input-textarea/variants/tests/InputTextareaWithLabel.spec.ts +93 -0
- package/app/components/05.forms/patterns/stories/MigratedFieldsForm.stories.ts +220 -0
- package/app/components/05.forms/toggle-switch/CONSUMER-STYLING.md +29 -0
- package/app/components/05.forms/toggle-switch/ToggleSwitchCore.vue +120 -119
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchCore.stories.ts +1 -1
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabel.stories.ts +1 -1
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabelInline.stories.ts +2 -2
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchCore.spec.ts +101 -0
- package/app/components/05.forms/toggle-switch/variants/ToggleSwitchWithLabel.vue +3 -5
- package/app/components/05.forms/toggle-switch/variants/ToggleSwitchWithLabelInline.vue +1 -2
- package/app/components/05.forms/toggle-switch/variants/tests/ToggleSwitchWithLabel.spec.ts +77 -0
- package/app/components/05.forms/toggle-switch/variants/tests/ToggleSwitchWithLabelInline.spec.ts +66 -0
- package/app/components/05.forms/triple-toggle-switch/CONSUMER-STYLING.md +20 -6
- package/app/components/05.forms/triple-toggle-switch/TripleToggleSwitchCore.vue +12 -10
- package/app/types/components/container-glow.d.ts +8 -0
- package/app/types/components/index.ts +2 -1
- package/app/types/components/select-menu.d.ts +5 -0
- package/package.json +3 -2
- package/.vscode/srcdev-component-toggle-switch-core.code-snippets +0 -13
- package/app/components/05.forms/input-range-fancy/InputRangeFancyCore.vue +0 -426
- package/app/components/05.forms/input-range-fancy/InputRangeFancyWithLabel.vue +0 -94
- package/app/components/carousels/CarouselBasic.vue +0 -317
- package/app/components/carousels/CarouselInfinite.vue +0 -358
- package/app/components/container-glow/ContainerGlowCore.vue +0 -296
- package/app/components/container-glow/stories/ContainerGlowCore.stories.ts +0 -337
- package/app/components/display-theme-switch/DisplayThemeSwitch.vue +0 -189
- package/app/components/display-theme-switch/stories/DisplayThemeSwitch.stories.ts +0 -154
- package/app/components/glowing-border/GlowingBorder.vue +0 -142
- package/app/components/glowing-border/stories/GlowingBorder.stories.ts +0 -141
- package/app/components/layout-grids/LayoutGridB.vue +0 -134
- package/app/components/test-storybook/TestStorybook.vue +0 -49
- package/app/components/test-storybook/stories/TestStorybook.stories.ts +0 -28
- package/app/components/ui-block-decorated/UiBlockDecorated.vue +0 -167
- package/app/components/view-timeline/WipeAwayVertical.vue +0 -180
- 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,214 @@
|
|
|
1
|
+
# InputNumberCore Component
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`InputNumberCore` is the low-level native `<input type="number">` primitive for the `05.forms`
|
|
6
|
+
number-input family. It renders a single native number input with left/right button slots (for
|
|
7
|
+
step-down/step-up controls). It has no label, description, or error-message rendering of its
|
|
8
|
+
own — that's composed by the `InputNumberDefault` variant below.
|
|
9
|
+
|
|
10
|
+
Most consumers should reach for **InputNumberDefault** rather than `InputNumberCore` directly.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Props reference
|
|
15
|
+
|
|
16
|
+
> **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`.
|
|
17
|
+
|
|
18
|
+
| Prop (template form) | Type | Default | Notes |
|
|
19
|
+
|------|------|---------|-------|
|
|
20
|
+
| `:id` | `string` | (required) | Applied to the native input. |
|
|
21
|
+
| `:name` | `string` | (required) | Applied to the native input. |
|
|
22
|
+
| `:min` | `number` | (required) | Native `min`. |
|
|
23
|
+
| `:max` | `number` | (required) | Native `max`. |
|
|
24
|
+
| `:step` | `number` | `1` | Native `step`. |
|
|
25
|
+
| `:placeholder` | `string` | `""` | Native `placeholder`. |
|
|
26
|
+
| `:required` | `boolean` | `false` | Native `required`. |
|
|
27
|
+
| `:theme` | `FormUiTheme` | `"default"` | Sets `data-theme` on the wrapper. |
|
|
28
|
+
| `:weight` | `FormWeight` | `"normal"` | Adds an `input-number--{weight}` class to the input, for prop-shape consistency with sibling `05.forms` components. **No CSS in the library currently styles this class — it's a no-op today**, same convention as `InputRangeCore`'s `weight` prop. |
|
|
29
|
+
| `:input-variant` | `InputUiVariant` | `"normal"` | Adds the variant name as a class on the wrapper — `"normal"` (bordered box) and `"underlined"` (bottom-border only) have CSS; `"outlined"` doesn't (see CONSUMER-STYLING.md). Same prop/behaviour as `InputTextCore`. |
|
|
30
|
+
| `:field-has-error` | `boolean` | `false` | Drives `data-invalid` on the wrapper. |
|
|
31
|
+
| `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes applied to the input element. |
|
|
32
|
+
| `:aria-describedby` | `string` | `""` | Forwarded to the native input. |
|
|
33
|
+
|
|
34
|
+
### v-model
|
|
35
|
+
|
|
36
|
+
`v-model` — `number | readonly number[]`, **required** (`defineModel({ required: true })`). Bound
|
|
37
|
+
directly to the native number input. Required rather than defaulted deliberately — same reasoning
|
|
38
|
+
as `InputRangeCore`'s `v-model` (see that skill doc) — this also satisfies `vue/require-default-prop`.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Slots
|
|
43
|
+
|
|
44
|
+
| Slot | Notes |
|
|
45
|
+
|------|-------|
|
|
46
|
+
| `left` | Rendered before the input (e.g. a step-down button). Adds `has-left-slot` to the wrapper. |
|
|
47
|
+
| `right` | Rendered after the input (e.g. a step-up button). Adds `has-right-slot` to the wrapper. |
|
|
48
|
+
|
|
49
|
+
Any `.input-button-core` inside `.slot` gets its own border/outline stripped and is sized to
|
|
50
|
+
match the input (`aspect-ratio: 1`, `width: var(--input-min-height)`) regardless of which slot
|
|
51
|
+
it's in. The divider border is then added directly to that button element specifically when its
|
|
52
|
+
slot is present — `&.has-left-slot .left-slot .input-button-core { border-right: ... }` and the
|
|
53
|
+
right-hand mirror — so it sits flush against the button's own edge rather than around the
|
|
54
|
+
containing `.slot` div. This makes step buttons read as embedded segments of the same control
|
|
55
|
+
rather than separate floating buttons. The divider reuses the wrapper's own border colour token
|
|
56
|
+
(no separate divider token), same as `InputTextCore`/`InputTextAsNumberWithLabel`.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## CSS token API
|
|
61
|
+
|
|
62
|
+
See [CONSUMER-STYLING.md](../../app/components/05.forms/input-number/CONSUMER-STYLING.md) for the
|
|
63
|
+
full table with defaults.
|
|
64
|
+
|
|
65
|
+
**Common tokens:**
|
|
66
|
+
- `--input-number-surface` / `-surface-hover` — wrapper/input/embedded-button background, resting and hover, falls back to `--theme-input-surface`/`-surface-hover`
|
|
67
|
+
- `--input-number-border` / `-border-hover` / `-border-focus` — border colour resting, mouse-hover outline, and `:focus-visible` outline (also the divider colour and the embedded button's own focus outline), falls back to `--theme-border`/`--theme-border-focus`
|
|
68
|
+
- `--input-number-text-color` — input text colour, falls back to `--theme-input-text-color-normal`
|
|
69
|
+
- `--input-number-placeholder-color` — placeholder text colour, falls back to `--theme-input-placeholder`
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Fixed 2026-09-22: dead CSS from a missing base class and mismatched selectors
|
|
74
|
+
|
|
75
|
+
`InputNumberCore` migrated to full compliance from a `variants:true`, `eslint_issues:true`,
|
|
76
|
+
score-1/5 state. Several latent bugs were fixed alongside the standard checklist:
|
|
77
|
+
|
|
78
|
+
- The native `<input>` had no static `input-number-core` class — only the dynamic
|
|
79
|
+
`elementClasses` passthrough — so the entire `.input-number-core { ... }` style block (font,
|
|
80
|
+
padding, colour, focus box-shadow) never matched anything. Fixed by adding the class directly.
|
|
81
|
+
- `placeholder` was declared as a prop but never bound on the `<input>` — unlike `InputRangeCore`
|
|
82
|
+
(where a range input genuinely has no placeholder concept and the no-op is intentional and
|
|
83
|
+
documented), a number input *does* support `placeholder` meaningfully, so this was a real gap,
|
|
84
|
+
not a deliberate no-op. Fixed by binding `:placeholder`.
|
|
85
|
+
- The left/right slot divs rendered with classes `slot left`/`slot right`, but the CSS targeted
|
|
86
|
+
`.left-slot`/`.right-slot` and `.has-left-slot`/`.has-right-slot` — neither the slot divs nor
|
|
87
|
+
the wrapper ever carried those exact class names, so the divider-border and flex-alignment
|
|
88
|
+
rules for embedded step buttons never applied. Fixed by renaming the slot divs to
|
|
89
|
+
`slot left-slot`/`slot right-slot` and adding `has-left-slot`/`has-right-slot` to the wrapper
|
|
90
|
+
directly from `slots.left`/`slots.right` (Vue's own `useSlots()`), rather than a fragile,
|
|
91
|
+
cross-component class-toggling relay.
|
|
92
|
+
- Relatedly, `InputNumberDefault` called `updateElementClasses(["has-left-button",
|
|
93
|
+
"has-right-button"])` unconditionally on mount, which permanently added those two classes to
|
|
94
|
+
its own outer `.input-number-with-label` wrapper — a different element than
|
|
95
|
+
`InputNumberCore`'s `.input-number-wrapper`, so the `:has()` selector that was meant to key off
|
|
96
|
+
those classes could never match (the class lived on an ancestor, not a descendant). Replaced
|
|
97
|
+
entirely: `InputNumberCore` now detects an embedded button directly via
|
|
98
|
+
`:has(.left-slot .input-button-core)` / `:has(.right-slot .input-button-core)`, so no
|
|
99
|
+
JS-driven class relay is needed at all.
|
|
100
|
+
- `theme` was typed as an inline `"default" | "success" | "error" | "warning"` union in both
|
|
101
|
+
`InputNumberCore` and `InputNumberDefault` instead of importing the shared `FormUiTheme` type —
|
|
102
|
+
aligned with the rest of `05.forms`.
|
|
103
|
+
- `weight` was passed from `InputNumberDefault` to `InputNumberCore` but `InputNumberCore` had no
|
|
104
|
+
matching prop, so it fell through as a raw, meaningless `weight="normal"` HTML attribute on the
|
|
105
|
+
wrapper `<div>`. Fixed by declaring the prop on `InputNumberCore` too, matching `InputRangeCore`.
|
|
106
|
+
- `defineModel()` had no default, tripping `vue/require-default-prop` — fixed the same way as
|
|
107
|
+
`InputRangeCore`: `defineModel({ required: true })`, not a rule disable or an artificial default.
|
|
108
|
+
- A single-use private token, `--_border`, was inlined to the public
|
|
109
|
+
`var(--input-number-border, var(--theme-border))` directly at its point of use — it had no
|
|
110
|
+
composition or state-swap behind it, just a bare pass-through (see pitfall #20). `--_surface`
|
|
111
|
+
(used twice) and `--_min-width` (composed from a `v-bind()` value) correctly stayed private.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Fixed 2026-09-22 (follow-up): rewritten to visually mirror InputTextCore
|
|
116
|
+
|
|
117
|
+
The first migration pass above only made `InputNumberCore`'s *own* (pre-existing, previously dead)
|
|
118
|
+
CSS active — it didn't reconsider whether that CSS was actually the right design. Once live, it
|
|
119
|
+
was visually inconsistent with the rest of `05.forms`: no border-radius, no hover/focus outline
|
|
120
|
+
state, wrong padding-token family (`--form-textarea-padding-*` instead of the shared input
|
|
121
|
+
padding tokens), and a box-shadow-toggle focus mechanism instead of the `:has()`-based outline
|
|
122
|
+
approach every other input in this library uses. `InputNumberCore` is meant to be a native
|
|
123
|
+
`<input type="number">` sibling to the text-based `InputTextAsNumberWithLabel` variant (see
|
|
124
|
+
`input-text-core.md`'s Variants section) — both should look and behave the same. The CSS was rewritten
|
|
125
|
+
to mirror `InputTextCore` directly:
|
|
126
|
+
|
|
127
|
+
- Added `:input-variant` (`"normal"` / `"outlined"` / `"underlined"`), matching `InputTextCore`'s
|
|
128
|
+
prop and wrapper-class shape (`InputNumberDefault` forwards it the same way
|
|
129
|
+
`InputTextWithLabel` forwards it to `InputTextCore`, including to `InputLabel`).
|
|
130
|
+
- Replaced the flat, unconditional border/radius/box-shadow with `InputTextCore`'s `.normal`
|
|
131
|
+
variant shape: border + `border-radius: var(--form-input-border-radius)` (previously this read
|
|
132
|
+
`var(--form-element-border-width)` — a pre-existing bug that used the *border-width* token as
|
|
133
|
+
the *radius*, coincidentally invisible while the block was dead), plus `:has(input:hover)` /
|
|
134
|
+
`:has(input:focus-visible)` outline states instead of the old `--_focus-box-shadow` custom
|
|
135
|
+
property toggle.
|
|
136
|
+
- `--input-number-border-hover` and `--input-number-border-focus` are new public tokens for
|
|
137
|
+
these two states (both fall back to `--theme-border-focus`, same as `InputTextCore`).
|
|
138
|
+
- Padding moved from `--form-textarea-padding-*` (the wrong token family — textarea, not input)
|
|
139
|
+
to the shared `--input-padding-block`/`--input-padding-inline`/`--input-min-height` tokens every
|
|
140
|
+
other `05.forms` text-like input uses.
|
|
141
|
+
- The embedded step button now gets a hover background (`--input-number-surface-hover`, new
|
|
142
|
+
public token) and a visible `:focus-visible` outline (reusing `--input-number-border-focus`) —
|
|
143
|
+
previously it had neither, an accessibility gap beyond the visual mismatch (checklist item 9).
|
|
144
|
+
- The divider between the input and an embedded button now reuses the border colour
|
|
145
|
+
(`--_border`) directly instead of a separate `--input-number-divider-color` token — removed,
|
|
146
|
+
since `InputTextCore` doesn't have an equivalent separate token either (the border colour
|
|
147
|
+
*is* the divider colour there too).
|
|
148
|
+
- `--_border-hover` and `--_surface-hover` were each used only once with no composition or
|
|
149
|
+
state-swap, so per the public-token rule they were inlined directly
|
|
150
|
+
(`var(--input-number-border-hover, var(--theme-border-focus))` /
|
|
151
|
+
`var(--input-number-surface-hover, var(--theme-input-surface-hover))`) rather than kept as
|
|
152
|
+
private wrappers. `--_border-focus` stayed private since it's now genuinely reused (the
|
|
153
|
+
wrapper's own focus outline *and* the embedded button's focus outline both read it).
|
|
154
|
+
|
|
155
|
+
**Separately found in the same investigation** (not an `InputNumberCore` bug, but what the visual
|
|
156
|
+
comparison was originally checking against): `InputTextAsNumberWithLabel.vue` had its own,
|
|
157
|
+
unrelated pre-existing bug — see `input-text-core.md`'s Variants section for the fix.
|
|
158
|
+
|
|
159
|
+
**Correction, same day**: the first version of this rewrite still didn't visually match — the
|
|
160
|
+
divider border and button sizing (`aspect-ratio: 1`, `width: var(--input-min-height)`) had been
|
|
161
|
+
put on the `.left-slot`/`.right-slot` *wrapper divs*, not on `.input-button-core` itself, unlike
|
|
162
|
+
`InputTextCore`'s equivalent block. That left an unstyled gap between the button and the border
|
|
163
|
+
line instead of a flush edge, and the buttons weren't sized to match the input's height. Fixed by
|
|
164
|
+
moving the divider border and all button sizing/colour rules onto `.input-button-core` directly
|
|
165
|
+
(`&.has-left-slot .left-slot .input-button-core { border-right: ... }`, matching
|
|
166
|
+
`InputTextAsNumberWithLabel`'s `.left-slot .input-button-core { border-right: ... }` exactly)
|
|
167
|
+
rather than the containing `.slot` div. When mirroring another component's CSS structure, check
|
|
168
|
+
*which element* a rule targets, not just that an equivalent rule exists somewhere in the block —
|
|
169
|
+
a border/sizing rule one level off in the DOM tree produces a visually-close-but-not-matching
|
|
170
|
+
result that's easy to miss without a side-by-side screenshot comparison.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Variants
|
|
175
|
+
|
|
176
|
+
### InputNumberDefault
|
|
177
|
+
|
|
178
|
+
`InputNumberDefault` (`variants/InputNumberDefault.vue`) composes `InputNumberCore` with
|
|
179
|
+
`InputLabel`, `InputDescription`, and `InputError`, plus optional step-down/step-up buttons
|
|
180
|
+
(`InputButtonCore`) wired into the `left`/`right` slots.
|
|
181
|
+
|
|
182
|
+
**Additional props over InputNumberCore:**
|
|
183
|
+
|
|
184
|
+
| Prop (template form) | Type | Default | Notes |
|
|
185
|
+
|------|------|---------|-------|
|
|
186
|
+
| `:label` | `string` | (required) | Rendered via `InputLabel`. |
|
|
187
|
+
| `:error-message` | `object \| string` | (required) | Rendered via `InputError` when `field-has-error` is true. |
|
|
188
|
+
| `:step-down-label` | `string` | `"Step down"` | `button-text` on the step-down `InputButtonCore` — override for localisation. |
|
|
189
|
+
| `:step-up-label` | `string` | `"Step up"` | `button-text` on the step-up `InputButtonCore` — override for localisation. |
|
|
190
|
+
|
|
191
|
+
**Slots**: `descriptionHtml`, `descriptionText` (both forwarded to `InputDescription`), plus
|
|
192
|
+
`left`/`right` (forwarded straight through to the underlying `InputNumberCore` — the
|
|
193
|
+
step-down/step-up buttons only render when you use the `left`/`right` slots yourself, since the
|
|
194
|
+
button icon content itself is consumer-supplied).
|
|
195
|
+
|
|
196
|
+
```vue
|
|
197
|
+
<InputNumberDefault
|
|
198
|
+
name="quantity"
|
|
199
|
+
label="Quantity"
|
|
200
|
+
v-model="quantity"
|
|
201
|
+
:min="1"
|
|
202
|
+
:max="10"
|
|
203
|
+
error-message="Please choose a value between 1 and 10"
|
|
204
|
+
:field-has-error="hasError"
|
|
205
|
+
>
|
|
206
|
+
<template #left><Icon name="mdi:minus" /></template>
|
|
207
|
+
<template #right><Icon name="mdi:plus" /></template>
|
|
208
|
+
</InputNumberDefault>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`v-model` is `number | readonly number[]`, **required** — same as `InputNumberCore` above.
|
|
212
|
+
|
|
213
|
+
Step-down is disabled (`readonly` on its `InputButtonCore`) once the value reaches `min`, and
|
|
214
|
+
step-up once it reaches `max`; clicking either button while at that boundary is a no-op.
|