srcdev-nuxt-components 9.3.11 → 9.3.13
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/skills/component-aria-landmark.md +3 -3
- package/.claude/skills/components/auto-grid.md +1 -1
- package/.claude/skills/components/page-hero-highlights.md +1 -1
- package/.claude/skills/components/service-detail.md +13 -14
- package/.claude/skills/components/service-summary-grid.md +141 -0
- package/.claude/skills/components/service-summary.md +165 -0
- package/.claude/skills/icon-sets.md +1 -1
- package/.claude/skills/index.md +3 -2
- package/.claude/skills/theming-component-token-pattern.md +126 -0
- package/.vscode/srcdev-component-service-summary.code-snippets +67 -0
- package/app/assets/styles/setup/04.elements/forms/06.button-geometry.css +10 -1
- package/app/components/02.molecules/input-copy/CONSUMER-STYLING.md +3 -3
- package/app/components/02.molecules/input-copy/InputCopy.vue +9 -3
- package/app/components/02.molecules/pricing-card/CONSUMER-STYLING.md +3 -3
- package/app/components/02.molecules/pricing-card/PricingCard.vue +6 -3
- package/app/components/03.organisms/services/service-detail/CONSUMER-STYLING.md +7 -8
- package/app/components/03.organisms/services/service-detail/ServiceDetail.vue +0 -1
- package/app/components/03.organisms/services/service-detail/stories/ServiceDetail.stories.ts +2 -2
- package/app/components/03.organisms/services/service-summary/CONSUMER-STYLING.md +116 -0
- package/app/components/03.organisms/services/service-summary/ServiceSummary.vue +183 -0
- package/app/components/03.organisms/services/{services-section/stories/ServicesSection.stories.ts → service-summary/stories/ServiceSummary.stories.ts} +78 -148
- package/app/components/03.organisms/services/service-summary/tests/ServiceSummary.spec.ts +210 -0
- package/app/components/03.organisms/services/service-summary/tests/__snapshots__/ServiceSummary.spec.ts.snap +17 -0
- package/app/components/03.organisms/services/services-grids/{ServicesSectionGrid.vue → ServiceSummaryGrid.vue} +8 -10
- package/app/components/03.organisms/services/services-grids/stories/{ServicesSectionGrid.stories.ts → ServiceSummaryGrid.stories.ts} +21 -21
- package/app/components/03.organisms/services/services-grids/tests/{ServicesSectionGrid.spec.ts → ServiceSummaryGrid.spec.ts} +28 -28
- package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServiceSummaryGrid.spec.ts.snap +45 -0
- package/app/components/05.forms/input-button/CONSUMER-STYLING.md +117 -0
- package/app/components/05.forms/input-button/InputButtonCore.vue +83 -37
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/combo-icons-secondary-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/combo-icons-tertiary-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/combo-primary-pending-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/combo-secondary-pill-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/combo-tertiary-readonly-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/default-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/default-theme-default-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/default-theme-error-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/default-theme-success-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/default-theme-warning-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/default-variant-primary-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/default-variant-secondary-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/default-variant-tertiary-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/nuxt-icon-only-component-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/state-pending-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/state-pill-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/state-readonly-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/playwright/input-button-core.playwright.ts-snapshots/with-both-nuxt-icon-components-chromium-darwin.png +0 -0
- package/app/components/05.forms/input-button/stories/InputButtonCore.stories.ts +35 -0
- package/app/components/05.forms/input-checkbox-radio/CONSUMER-STYLING.md +68 -0
- package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioButton.vue +19 -9
- package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioCore.vue +8 -5
- package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadioButton.stories.ts +93 -0
- package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadioCore.stories.ts +75 -0
- package/app/components/05.forms/input-number/CONSUMER-STYLING.md +41 -0
- package/app/components/05.forms/input-number/InputNumberCore.vue +12 -6
- package/app/components/05.forms/input-select/CONSUMER-STYLING.md +35 -29
- package/app/components/05.forms/input-select/InputSelectCore.vue +32 -20
- package/app/components/05.forms/input-select/stories/InputSelectCore.stories.ts +69 -0
- package/app/components/05.forms/input-text/CONSUMER-STYLING.md +38 -48
- package/app/components/05.forms/input-text/InputTextCore.vue +35 -18
- package/app/components/05.forms/patterns/stories/ContactForm.stories.ts +184 -0
- package/app/components/05.forms/patterns/stories/LoginForm.stories.ts +125 -0
- package/app/components/05.forms/toggle-switch/CONSUMER-STYLING.md +49 -0
- package/app/components/05.forms/toggle-switch/ToggleSwitchCore.vue +23 -16
- package/app/components/05.forms/triple-toggle-switch/CONSUMER-STYLING.md +72 -0
- package/app/components/05.forms/triple-toggle-switch/TripleToggleSwitchCore.vue +42 -20
- package/app/components/05.forms/triple-toggle-switch/stories/TripleToggleSwitchCore.stories.ts +63 -0
- package/app/components/display-theme-switch/DisplayThemeSwitch.vue +13 -6
- package/package.json +1 -1
- package/.claude/skills/components/services-section-grid.md +0 -130
- package/.claude/skills/components/services-section.md +0 -211
- package/.vscode/srcdev-component-services-section.code-snippets +0 -84
- package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServicesSectionGrid.spec.ts.snap +0 -87
- package/app/components/03.organisms/services/services-section/CONSUMER-STYLING.md +0 -149
- package/app/components/03.organisms/services/services-section/ServicesSection.vue +0 -345
- package/app/components/03.organisms/services/services-section/tests/ServicesSection.spec.ts +0 -297
- package/app/components/03.organisms/services/services-section/tests/__snapshots__/ServicesSection.spec.ts.snap +0 -87
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryFn } from "@nuxtjs/storybook";
|
|
2
2
|
import { ref } from "vue";
|
|
3
3
|
import StorybookComponent from "../InputButtonCore.vue";
|
|
4
|
+
import InputTextCore from "../../input-text/InputTextCore.vue";
|
|
4
5
|
|
|
5
6
|
interface InputButtonCoreStoryArgs {
|
|
6
7
|
type: "submit" | "button" | "reset";
|
|
@@ -240,7 +241,17 @@ const Template: StoryFn<InputButtonCoreStoryArgs> = (args) => ({
|
|
|
240
241
|
},
|
|
241
242
|
template: `
|
|
242
243
|
<div style="display: flex; flex-direction: column; gap: 1rem; align-items: flex-start;margin: 36px;">
|
|
244
|
+
<!-- :key forces a remount when any slot-visibility arg changes. Verified the args
|
|
245
|
+
themselves ARE reactive here (a plain {{ args.useLeftSlot }} interpolation updates
|
|
246
|
+
live on toggle) — the bug is narrower: Vue's compiled-at-runtime string template
|
|
247
|
+
doesn't reliably re-evaluate a <template v-if="..."> conditionally attached to a named
|
|
248
|
+
slot when only the v-if's condition changes, on this component/Storybook combination.
|
|
249
|
+
Toggling a control changed the fallthrough attribute (confirmed via useleftslot="true"
|
|
250
|
+
in the rendered DOM) but never actually added/removed the slot content until a hard
|
|
251
|
+
refresh. Remounting sidesteps whatever the underlying slot-stability quirk is, rather
|
|
252
|
+
than chasing it further. -->
|
|
243
253
|
<StorybookComponent
|
|
254
|
+
:key="[args.useLeftSlot, args.useLeftIcon, args.useRightSlot, args.useRightIcon, args.useIconOnlySlot, args.useIconOnly].join(',')"
|
|
244
255
|
v-bind="args"
|
|
245
256
|
@click="handleClick"
|
|
246
257
|
>
|
|
@@ -302,3 +313,27 @@ NuxtIconOnlyComponent.args = {
|
|
|
302
313
|
useIconOnly: true,
|
|
303
314
|
iconOnlyName: "mdi:chevron-right-circle-outline",
|
|
304
315
|
};
|
|
316
|
+
|
|
317
|
+
// Composition check, not a props demo: confirms InputButtonCore's --button-min-height (defaults
|
|
318
|
+
// to var(--input-min-height)) actually keeps it height-aligned with an inline text input, the
|
|
319
|
+
// most common "usual suspect" pairing (e.g. newsletter signup, search bar). Textareas are the
|
|
320
|
+
// deliberate exception to this alignment and aren't part of this story.
|
|
321
|
+
const InlineTemplate: StoryFn = () => ({
|
|
322
|
+
components: { StorybookComponent, InputTextCore },
|
|
323
|
+
template: `
|
|
324
|
+
<div style="margin: 36px; max-width: 480px;">
|
|
325
|
+
<div style="display: flex; align-items: flex-start; gap: 0.8rem;">
|
|
326
|
+
<InputTextCore
|
|
327
|
+
id="inline-email"
|
|
328
|
+
name="email"
|
|
329
|
+
type="email"
|
|
330
|
+
placeholder="you@example.com"
|
|
331
|
+
style="flex: 1;"
|
|
332
|
+
/>
|
|
333
|
+
<StorybookComponent type="submit" variant="primary" button-text="Subscribe" />
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
`,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
export const InlineWithTextInput = InlineTemplate.bind({});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# InputCheckboxRadioCore / InputCheckboxRadioButton — Consumer Styling Guide
|
|
2
|
+
|
|
3
|
+
## Public token API
|
|
4
|
+
|
|
5
|
+
Every colour token below is `var(--input-checkbox-*, {default})` — set the `--input-checkbox-*`
|
|
6
|
+
name to override just this component; leave it unset and it inherits the shared `--theme-*` slot
|
|
7
|
+
(or `--theme-checkbox-symbol-surface`/`--theme-input-surface` family) every other themed component
|
|
8
|
+
also falls back to. See `theming-component-token-pattern.md` for why this two-tier shape exists.
|
|
9
|
+
|
|
10
|
+
Defaults are unchanged from before this migration — this is a token-rename pass (giving each
|
|
11
|
+
property its own override point), not a redesign, unlike `InputButtonCore`'s flat/darker-hover
|
|
12
|
+
treatment. Geometry (`--input-checked-element-size`, `--form-element-*`, etc.) is separate and
|
|
13
|
+
untouched — see `theming-form-geometry-tokens.md`.
|
|
14
|
+
|
|
15
|
+
### The control itself (`InputCheckboxRadioCore` — the square/circle)
|
|
16
|
+
|
|
17
|
+
| Token | Falls back to | Controls |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `--input-checkbox-surface` | `var(--theme-checkbox-symbol-surface)` | Background |
|
|
20
|
+
| `--input-checkbox-border` | `var(--theme-border)` | Border colour |
|
|
21
|
+
| `--input-checkbox-border-focus` | `var(--theme-border-focus)` | Outline colour on `:focus-visible` (non-button style only — see note) |
|
|
22
|
+
| `--input-checkbox-icon-color` | `var(--theme-text)` | Checked-state icon colour |
|
|
23
|
+
|
|
24
|
+
> The `:focus-visible` outline only applies to the plain checkbox/radio style
|
|
25
|
+
> (`:not(.button)`) — `InputCheckboxRadioButton`'s pill/button style has its own focus treatment,
|
|
26
|
+
> below, via `:has(.input-checkbox-radio-core:focus-visible)` on the outer label.
|
|
27
|
+
|
|
28
|
+
### The pill/button style (`InputCheckboxRadioButton` — e.g. a "Services of interest" option list)
|
|
29
|
+
|
|
30
|
+
| Token | Falls back to | Controls |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `--input-checkbox-button-surface` | `var(--theme-input-surface)` | Background (resting) |
|
|
33
|
+
| `--input-checkbox-button-border` | `var(--theme-border)` | Border colour (resting and hover — unchanged between the two by default) |
|
|
34
|
+
| `--input-checkbox-button-surface-hover` | `var(--theme-input-surface-hover)` | Background on hover |
|
|
35
|
+
| `--input-checkbox-button-ring-hover` | `var(--theme-ring)` | Outline colour on hover |
|
|
36
|
+
| `--input-checkbox-button-surface-focus` | `var(--theme-surface-subtle)` | Background when the inner control has `:focus-visible` |
|
|
37
|
+
| `--input-checkbox-button-ring-focus` | `var(--theme-border-focus)` | Outline colour when the inner control has `:focus-visible` |
|
|
38
|
+
| `--input-checkbox-button-label-color` | `var(--colour-text-default)` | Option label text colour |
|
|
39
|
+
| `--input-checkbox-button-icon-color` | `var(--theme-text)` | Trailing decorator icon colour (the `+` in the corner) |
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Global theming — app-level CSS file
|
|
44
|
+
|
|
45
|
+
```css
|
|
46
|
+
:where(html) {
|
|
47
|
+
--input-checkbox-button-surface: var(--rose-09);
|
|
48
|
+
--input-checkbox-button-surface-hover: var(--rose-08);
|
|
49
|
+
--input-checkbox-button-ring-focus: var(--rose-04);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Only declare the tokens you want to change — everything else keeps inheriting the shared
|
|
54
|
+
`--theme-*`/`--theme-input-*` tokens.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Per-instance overrides
|
|
59
|
+
|
|
60
|
+
```vue
|
|
61
|
+
<InputCheckboxRadioButton
|
|
62
|
+
id="balayage"
|
|
63
|
+
type="checkbox"
|
|
64
|
+
name="services"
|
|
65
|
+
label="Balayage"
|
|
66
|
+
style="--input-checkbox-button-surface-hover: var(--gold-08);"
|
|
67
|
+
/>
|
|
68
|
+
```
|
|
@@ -100,23 +100,28 @@ const flexDirection = ref(props.direction);
|
|
|
100
100
|
&.inline {
|
|
101
101
|
--_white-space: nowrap;
|
|
102
102
|
}
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
|
|
104
|
+
/* Public --input-checkbox-button-* tokens, inline-fallback to the shared theme tokens (see
|
|
105
|
+
theming-component-token-pattern.md) — overriding one here doesn't touch every other themed
|
|
106
|
+
input/control that also reads --theme-input-surface/--theme-border, etc. Defaults are
|
|
107
|
+
unchanged from before this migration — this is a token-rename pass, not a redesign. */
|
|
108
|
+
background-color: var(--input-checkbox-button-surface, var(--theme-input-surface));
|
|
109
|
+
border: var(--form-element-border-width) solid var(--input-checkbox-button-border, var(--theme-border));
|
|
105
110
|
outline: var(--form-element-outline-width) solid transparent;
|
|
106
111
|
|
|
107
112
|
box-shadow: 0.1rem 0.1rem 0.8rem 0.1rem transparent;
|
|
108
113
|
|
|
109
114
|
&:hover {
|
|
110
|
-
background-color: var(--theme-input-surface-hover);
|
|
111
|
-
border-color: var(--theme-border);
|
|
112
|
-
outline-color: var(--theme-ring);
|
|
115
|
+
background-color: var(--input-checkbox-button-surface-hover, var(--theme-input-surface-hover));
|
|
116
|
+
border-color: var(--input-checkbox-button-border, var(--theme-border));
|
|
117
|
+
outline-color: var(--input-checkbox-button-ring-hover, var(--theme-ring));
|
|
113
118
|
outline-offset: var(--form-element-outline-offset-focus);
|
|
114
119
|
cursor: pointer;
|
|
115
120
|
}
|
|
116
121
|
|
|
117
122
|
&:has(.input-checkbox-radio-core:focus-visible) {
|
|
118
|
-
background-color: var(--theme-surface-subtle);
|
|
119
|
-
outline-color: var(--theme-border-focus);
|
|
123
|
+
background-color: var(--input-checkbox-button-surface-focus, var(--theme-surface-subtle));
|
|
124
|
+
outline-color: var(--input-checkbox-button-ring-focus, var(--theme-border-focus));
|
|
120
125
|
outline-offset: var(--form-element-outline-offset-focus);
|
|
121
126
|
}
|
|
122
127
|
|
|
@@ -127,12 +132,17 @@ const flexDirection = ref(props.direction);
|
|
|
127
132
|
.input-checkbox-radio-options-button-label {
|
|
128
133
|
display: flex;
|
|
129
134
|
flex-grow: 1;
|
|
130
|
-
color: var(--colour-text-default);
|
|
135
|
+
color: var(--input-checkbox-button-label-color, var(--colour-text-default));
|
|
131
136
|
font-size: var(--input-font-size);
|
|
132
137
|
width: 100%;
|
|
133
138
|
min-height: var(--input-element-line-height);
|
|
134
139
|
align-items: center;
|
|
135
140
|
justify-content: center;
|
|
141
|
+
/* justify-content only centers the label as a single block — once text actually wraps
|
|
142
|
+
(e.g. "Half Head Highlights"), that block fills the full available width and each
|
|
143
|
+
individual line falls back to its default left-alignment inside it. text-align is what
|
|
144
|
+
centers wrapped lines relative to each other. */
|
|
145
|
+
text-align: center;
|
|
136
146
|
padding-block: 0.8rem;
|
|
137
147
|
padding-inline: 0.8rem;
|
|
138
148
|
white-space: var(--_white-space);
|
|
@@ -146,7 +156,7 @@ const flexDirection = ref(props.direction);
|
|
|
146
156
|
display: flex;
|
|
147
157
|
align-items: center;
|
|
148
158
|
justify-content: center;
|
|
149
|
-
color: var(--theme-text);
|
|
159
|
+
color: var(--input-checkbox-button-icon-color, var(--theme-text));
|
|
150
160
|
|
|
151
161
|
.icon {
|
|
152
162
|
height: var(--input-checkbox-decorator-icon-size);
|
|
@@ -112,8 +112,11 @@ const inputClasses = computed(() => [
|
|
|
112
112
|
grid-template-areas: "element-stack";
|
|
113
113
|
place-content: center;
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
/* Public --input-checkbox-* tokens, inline-fallback to the shared theme tokens (see
|
|
116
|
+
theming-component-token-pattern.md) — overriding one here doesn't touch every other
|
|
117
|
+
themed input/control that also reads --theme-checkbox-symbol-surface/--theme-border. */
|
|
118
|
+
background-color: var(--input-checkbox-surface, var(--theme-checkbox-symbol-surface));
|
|
119
|
+
border: 0.1rem solid var(--input-checkbox-border, var(--theme-border));
|
|
117
120
|
outline: var(--form-element-outline-width) solid transparent;
|
|
118
121
|
|
|
119
122
|
height: var(--input-checked-element-size);
|
|
@@ -139,7 +142,7 @@ const inputClasses = computed(() => [
|
|
|
139
142
|
|
|
140
143
|
&:not(:is(.button)) {
|
|
141
144
|
&:has(input:focus-visible) {
|
|
142
|
-
outline: var(--form-element-outline-width-focus) solid var(--theme-border-focus);
|
|
145
|
+
outline: var(--form-element-outline-width-focus) solid var(--input-checkbox-border-focus, var(--theme-border-focus));
|
|
143
146
|
outline-offset: var(--form-element-outline-offset-focus);
|
|
144
147
|
}
|
|
145
148
|
}
|
|
@@ -150,7 +153,7 @@ const inputClasses = computed(() => [
|
|
|
150
153
|
|
|
151
154
|
.input-checked-icon-checked,
|
|
152
155
|
.icon {
|
|
153
|
-
color: var(--theme-text);
|
|
156
|
+
color: var(--input-checkbox-icon-color, var(--theme-text));
|
|
154
157
|
}
|
|
155
158
|
}
|
|
156
159
|
}
|
|
@@ -164,7 +167,7 @@ const inputClasses = computed(() => [
|
|
|
164
167
|
|
|
165
168
|
.input-checked-icon-checked,
|
|
166
169
|
.icon {
|
|
167
|
-
color: var(--theme-text);
|
|
170
|
+
color: var(--input-checkbox-icon-color, var(--theme-text));
|
|
168
171
|
height: var(--input-checked-icon-size);
|
|
169
172
|
width: var(--input-checked-icon-size);
|
|
170
173
|
box-shadow: var(--_box-shadow);
|
package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadioButton.stories.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { Meta, StoryFn } from "@nuxtjs/storybook";
|
|
2
|
+
import { ref } from "vue";
|
|
3
|
+
import StorybookComponent from "../InputCheckboxRadioButton.vue";
|
|
4
|
+
|
|
5
|
+
interface InputCheckboxRadioButtonStoryArgs {
|
|
6
|
+
type: "checkbox" | "radio";
|
|
7
|
+
label: string;
|
|
8
|
+
isPill: boolean;
|
|
9
|
+
fieldHasError: boolean;
|
|
10
|
+
displayAsDisc: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
title: "Components/Forms/Input Checkbox Radio/InputCheckboxRadioButton",
|
|
15
|
+
component: StorybookComponent,
|
|
16
|
+
argTypes: {
|
|
17
|
+
type: {
|
|
18
|
+
control: { type: "select" },
|
|
19
|
+
options: ["checkbox", "radio"],
|
|
20
|
+
table: { category: "Basic" },
|
|
21
|
+
},
|
|
22
|
+
label: { control: "text", table: { category: "Basic" } },
|
|
23
|
+
isPill: { control: "boolean", table: { category: "Styling" } },
|
|
24
|
+
fieldHasError: { control: "boolean", table: { category: "States" } },
|
|
25
|
+
displayAsDisc: { control: "boolean", table: { category: "Styling" } },
|
|
26
|
+
},
|
|
27
|
+
args: {
|
|
28
|
+
type: "checkbox",
|
|
29
|
+
label: "Balayage",
|
|
30
|
+
isPill: false,
|
|
31
|
+
fieldHasError: false,
|
|
32
|
+
displayAsDisc: false,
|
|
33
|
+
},
|
|
34
|
+
} as Meta<InputCheckboxRadioButtonStoryArgs>;
|
|
35
|
+
|
|
36
|
+
const Template: StoryFn<InputCheckboxRadioButtonStoryArgs> = (args) => ({
|
|
37
|
+
components: { StorybookComponent },
|
|
38
|
+
setup() {
|
|
39
|
+
const modelValue = ref(false);
|
|
40
|
+
return { args, modelValue };
|
|
41
|
+
},
|
|
42
|
+
template: `
|
|
43
|
+
<div style="margin: 36px; max-width: 260px;">
|
|
44
|
+
<StorybookComponent
|
|
45
|
+
id="single-option"
|
|
46
|
+
name="single"
|
|
47
|
+
:type="args.type"
|
|
48
|
+
:label="args.label"
|
|
49
|
+
:is-pill="args.isPill"
|
|
50
|
+
:field-has-error="args.fieldHasError"
|
|
51
|
+
:display-as-disc="args.displayAsDisc"
|
|
52
|
+
v-model="modelValue"
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
55
|
+
`,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export const Default = Template.bind({});
|
|
59
|
+
|
|
60
|
+
// "Services of interest" pattern — the actual multi-option checkbox-pill layout this component
|
|
61
|
+
// is normally used in (as opposed to Default's single isolated control).
|
|
62
|
+
const OptionGroupTemplate: StoryFn = () => ({
|
|
63
|
+
components: { StorybookComponent },
|
|
64
|
+
setup() {
|
|
65
|
+
const services = [
|
|
66
|
+
"Balayage",
|
|
67
|
+
"Highlights",
|
|
68
|
+
"Half Head Highlights",
|
|
69
|
+
"Full Colour",
|
|
70
|
+
"Lowlights",
|
|
71
|
+
"Toner & Gloss",
|
|
72
|
+
];
|
|
73
|
+
const selected = ref<string[]>([]);
|
|
74
|
+
return { services, selected };
|
|
75
|
+
},
|
|
76
|
+
template: `
|
|
77
|
+
<div style="margin: 36px; max-width: 480px; display: grid; grid-template-columns: 1fr 1fr; gap: 0.8rem;">
|
|
78
|
+
<StorybookComponent
|
|
79
|
+
v-for="service in services"
|
|
80
|
+
:key="service"
|
|
81
|
+
:id="'service-' + service"
|
|
82
|
+
name="services"
|
|
83
|
+
type="checkbox"
|
|
84
|
+
:label="service"
|
|
85
|
+
:true-value="service"
|
|
86
|
+
:multiple-options="true"
|
|
87
|
+
v-model="selected"
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
`,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
export const OptionGroup = OptionGroupTemplate.bind({});
|
package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadioCore.stories.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Meta, StoryFn } from "@nuxtjs/storybook";
|
|
2
|
+
import { ref } from "vue";
|
|
3
|
+
import StorybookComponent from "../InputCheckboxRadioCore.vue";
|
|
4
|
+
import type { FormUiTheme } from "~/types/forms/types.forms.d";
|
|
5
|
+
|
|
6
|
+
interface InputCheckboxRadioCoreStoryArgs {
|
|
7
|
+
type: "checkbox" | "radio";
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
required: boolean;
|
|
11
|
+
theme: FormUiTheme;
|
|
12
|
+
fieldHasError: boolean;
|
|
13
|
+
displayAsDisc: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
title: "Components/Forms/Input Checkbox Radio/InputCheckboxRadioCore",
|
|
18
|
+
component: StorybookComponent,
|
|
19
|
+
argTypes: {
|
|
20
|
+
type: {
|
|
21
|
+
control: { type: "select" },
|
|
22
|
+
options: ["checkbox", "radio"],
|
|
23
|
+
description: "Control type",
|
|
24
|
+
table: { category: "Basic" },
|
|
25
|
+
},
|
|
26
|
+
id: { control: "text", table: { category: "Basic" } },
|
|
27
|
+
name: { control: "text", table: { category: "Basic" } },
|
|
28
|
+
required: { control: "boolean", table: { category: "Basic" } },
|
|
29
|
+
theme: {
|
|
30
|
+
control: { type: "select" },
|
|
31
|
+
options: ["default", "success", "error", "warning"],
|
|
32
|
+
table: { category: "Styling" },
|
|
33
|
+
},
|
|
34
|
+
fieldHasError: { control: "boolean", table: { category: "States" } },
|
|
35
|
+
displayAsDisc: {
|
|
36
|
+
control: "boolean",
|
|
37
|
+
description: "Checkbox only — render as a filled disc instead of a check icon",
|
|
38
|
+
table: { category: "Styling" },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
args: {
|
|
42
|
+
type: "checkbox",
|
|
43
|
+
id: "core-checkbox",
|
|
44
|
+
name: "coreCheckbox",
|
|
45
|
+
required: false,
|
|
46
|
+
theme: "default",
|
|
47
|
+
fieldHasError: false,
|
|
48
|
+
displayAsDisc: false,
|
|
49
|
+
},
|
|
50
|
+
} as Meta<InputCheckboxRadioCoreStoryArgs>;
|
|
51
|
+
|
|
52
|
+
const Template: StoryFn<InputCheckboxRadioCoreStoryArgs> = (args) => ({
|
|
53
|
+
components: { StorybookComponent },
|
|
54
|
+
setup() {
|
|
55
|
+
const modelValue = ref(false);
|
|
56
|
+
return { args, modelValue };
|
|
57
|
+
},
|
|
58
|
+
template: `
|
|
59
|
+
<div style="margin: 36px;">
|
|
60
|
+
<StorybookComponent v-bind="args" v-model="modelValue" />
|
|
61
|
+
</div>
|
|
62
|
+
`,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const Checkbox = Template.bind({});
|
|
66
|
+
Checkbox.args = { type: "checkbox" };
|
|
67
|
+
|
|
68
|
+
export const Radio = Template.bind({});
|
|
69
|
+
Radio.args = { type: "radio" };
|
|
70
|
+
|
|
71
|
+
export const CheckboxAsDisc = Template.bind({});
|
|
72
|
+
CheckboxAsDisc.args = { type: "checkbox", displayAsDisc: true };
|
|
73
|
+
|
|
74
|
+
export const ErrorState = Template.bind({});
|
|
75
|
+
ErrorState.args = { type: "checkbox", fieldHasError: true, theme: "error" };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# InputNumberCore — Consumer Styling Guide
|
|
2
|
+
|
|
3
|
+
## Public token API
|
|
4
|
+
|
|
5
|
+
Every colour token below is `var(--input-number-*, {default})` — set the `--input-number-*` name
|
|
6
|
+
to override just this component; leave it unset and it inherits the shared `--theme-input-*`/
|
|
7
|
+
`--theme-*` token every other themed component also falls back to. See
|
|
8
|
+
`theming-component-token-pattern.md` for why this two-tier shape exists.
|
|
9
|
+
|
|
10
|
+
| Token | Falls back to | Controls |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| `--input-number-surface` | `var(--theme-input-surface)` | Wrapper and input background |
|
|
13
|
+
| `--input-number-border` | `var(--theme-border)` | Border colour |
|
|
14
|
+
| `--input-number-text-color` | `var(--theme-input-text-color-normal)` | Input text colour |
|
|
15
|
+
| `--input-number-divider-color` | `var(--theme-input-surface-hover)` | Divider between the input and an embedded left/right `InputButtonCore` |
|
|
16
|
+
|
|
17
|
+
Geometry (`--form-textarea-padding-*`, `--input-font-size`, `--input-element-line-height`, etc.)
|
|
18
|
+
is shared across every `05.forms` component and untouched by this — see
|
|
19
|
+
`theming-form-geometry-tokens.md`.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Global theming — app-level CSS file
|
|
24
|
+
|
|
25
|
+
```css
|
|
26
|
+
:where(html) {
|
|
27
|
+
--input-number-surface: var(--rose-09);
|
|
28
|
+
--input-number-border: var(--rose-05);
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Only declare the tokens you want to change — everything else keeps inheriting the shared
|
|
33
|
+
`--theme-input-*`/`--theme-*` tokens.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Per-instance overrides
|
|
38
|
+
|
|
39
|
+
```vue
|
|
40
|
+
<InputNumberCore id="quantity" name="quantity" :min="1" :max="10" style="--input-number-border: var(--gold-04);" />
|
|
41
|
+
```
|
|
@@ -66,14 +66,20 @@ const minLength = computed(() => `${props.max.toString().length + 1}em`);
|
|
|
66
66
|
--_focus-box-shadow: var(--box-shadow-off);
|
|
67
67
|
--_min-width: v-bind(minLength);
|
|
68
68
|
|
|
69
|
+
/* Public --input-number-* tokens, inline-fallback to the shared --theme-* tokens (see
|
|
70
|
+
theming-component-token-pattern.md) — overriding one here doesn't touch every other themed
|
|
71
|
+
input/control that also reads --theme-input-surface/--theme-border. */
|
|
72
|
+
--_surface: var(--input-number-surface, var(--theme-input-surface));
|
|
73
|
+
--_border: var(--input-number-border, var(--theme-border));
|
|
74
|
+
|
|
69
75
|
display: flex;
|
|
70
76
|
align-items: center;
|
|
71
77
|
|
|
72
78
|
width: fit-content;
|
|
73
79
|
|
|
74
|
-
background-color: var(--
|
|
80
|
+
background-color: var(--_surface);
|
|
75
81
|
border-radius: var(--form-element-border-width);
|
|
76
|
-
border: var(--form-element-border-width) solid var(--
|
|
82
|
+
border: var(--form-element-border-width) solid var(--_border);
|
|
77
83
|
outline: var(--form-element-outline-width) solid transparent;
|
|
78
84
|
box-shadow: var(--_focus-box-shadow);
|
|
79
85
|
|
|
@@ -106,8 +112,8 @@ const minLength = computed(() => `${props.max.toString().length + 1}em`);
|
|
|
106
112
|
outline: none;
|
|
107
113
|
box-shadow: none;
|
|
108
114
|
|
|
109
|
-
background-color: var(--
|
|
110
|
-
color: var(--theme-input-text-color-normal);
|
|
115
|
+
background-color: var(--_surface);
|
|
116
|
+
color: var(--input-number-text-color, var(--theme-input-text-color-normal));
|
|
111
117
|
font-family: var(--font-family);
|
|
112
118
|
font-size: var(--input-font-size);
|
|
113
119
|
line-height: var(--input-element-line-height);
|
|
@@ -144,12 +150,12 @@ const minLength = computed(() => `${props.max.toString().length + 1}em`);
|
|
|
144
150
|
|
|
145
151
|
.left-slot {
|
|
146
152
|
margin-inline-end: 0;
|
|
147
|
-
border-right: 2px solid var(--theme-input-surface-hover);
|
|
153
|
+
border-right: 2px solid var(--input-number-divider-color, var(--theme-input-surface-hover));
|
|
148
154
|
}
|
|
149
155
|
|
|
150
156
|
.right-slot {
|
|
151
157
|
margin-inline-end: 0;
|
|
152
|
-
border-left: 2px solid var(--theme-input-surface-hover);
|
|
158
|
+
border-left: 2px solid var(--input-number-divider-color, var(--theme-input-surface-hover));
|
|
153
159
|
}
|
|
154
160
|
}
|
|
155
161
|
}
|
|
@@ -1,42 +1,48 @@
|
|
|
1
1
|
# InputSelectCore — Consumer Styling Guide
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Public token API
|
|
4
|
+
|
|
5
|
+
Every colour token below is `var(--input-select-*, {default})` — set the `--input-select-*` name
|
|
6
|
+
to override just this component; leave it unset and it inherits the shared `--theme-input-*`/
|
|
7
|
+
`--theme-*` token every other themed component also falls back to. See
|
|
8
|
+
`theming-component-token-pattern.md` for why this two-tier shape exists.
|
|
9
|
+
|
|
10
|
+
(Before 2026-08-25 this component used a private `--_input-select-*` naming scheme presented as a
|
|
11
|
+
"local override path" — that's gone. A private, underscore-prefixed custom property was never a
|
|
12
|
+
real override point; renaming it public with a real fallback is the actual fix, not a second
|
|
13
|
+
documented path to reach for.)
|
|
14
|
+
|
|
15
|
+
| Token | Falls back to | Controls |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `--input-select-surface` | `var(--theme-input-surface)` | Wrapper/select background |
|
|
18
|
+
| `--input-select-surface-hover` | `var(--theme-input-surface-hover)` | Option row background on hover |
|
|
19
|
+
| `--input-select-border` | `var(--theme-border)` | Border colour (`.normal`/`.underlined`/open picker, resting) |
|
|
20
|
+
| `--input-select-border-hover` | `var(--theme-border-focus)` | Outline colour on hover (mouse) |
|
|
21
|
+
| `--input-select-border-focus` | `var(--theme-border-focus)` | Outline colour on `:focus-visible` (keyboard/assistive) |
|
|
22
|
+
|
|
23
|
+
Geometry (`--input-padding-*`, `--input-min-height`, `--input-select-option-min-height`,
|
|
24
|
+
`--form-element-*`, etc.) is shared across every `05.forms` component and untouched by this — see
|
|
25
|
+
`theming-form-geometry-tokens.md`.
|
|
4
26
|
|
|
5
|
-
|
|
6
|
-
full rationale) — `.input-select-wrapper` now exposes local tokens, one indirection step below the
|
|
7
|
-
global `--theme-*` ones they default from:
|
|
8
|
-
|
|
9
|
-
```css
|
|
10
|
-
--_input-select-surface: var(--theme-input-surface);
|
|
11
|
-
--_input-select-surface-hover: var(--theme-input-surface-hover);
|
|
12
|
-
--_input-select-border: var(--theme-border);
|
|
13
|
-
--_input-select-border-focus: var(--theme-border-focus);
|
|
14
|
-
--_input-select-outline-color: var(--_input-select-border-focus);
|
|
15
|
-
```
|
|
27
|
+
---
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
/ `--theme-input-surface-hover` wherever you'd normally set theme tokens.
|
|
19
|
-
- **Local override** (this component only, guaranteed to win):
|
|
29
|
+
## Global theming — app-level CSS file
|
|
20
30
|
|
|
21
31
|
```css
|
|
22
|
-
|
|
23
|
-
--
|
|
24
|
-
--
|
|
25
|
-
--
|
|
32
|
+
:where(html) {
|
|
33
|
+
--input-select-surface: var(--rose-09);
|
|
34
|
+
--input-select-border: var(--rose-05);
|
|
35
|
+
--input-select-border-focus: var(--rose-03);
|
|
26
36
|
}
|
|
27
37
|
```
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Before 2026-08-22 this was referenced (on the open-picker outline) but never declared anywhere —
|
|
32
|
-
a dangling private token that silently resolved to the property's initial value (same failure mode
|
|
33
|
-
as the `--theme-button-surface` typo documented in the layer's `CLAUDE.md`, pitfall list). It's now
|
|
34
|
-
properly defined, defaulting to `--_input-select-border-focus`.
|
|
39
|
+
Only declare the tokens you want to change — everything else keeps inheriting the shared
|
|
40
|
+
`--theme-input-*`/`--theme-*` tokens.
|
|
35
41
|
|
|
36
42
|
---
|
|
37
43
|
|
|
38
|
-
##
|
|
44
|
+
## Per-instance overrides
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
```vue
|
|
47
|
+
<InputSelectCore id="country" name="country" style="--input-select-border-focus: var(--gold-04);" />
|
|
48
|
+
```
|