srcdev-nuxt-components 9.6.7 → 9.6.9
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/rules/consumer/ids-from-useid.md +14 -0
- package/.claude/skills/component-compliance-checklist.md +18 -1
- package/.claude/skills/component-inline-action-button.md +1 -1
- package/.claude/skills/component-local-style-override.md +1 -1
- package/.claude/skills/component-naming.md +13 -13
- package/.claude/skills/components/container-glow.md +2 -2
- package/.claude/skills/components/form-field.md +2 -2
- package/.claude/skills/components/form-wrapper.md +1 -1
- package/.claude/skills/components/glowing-border.md +1 -1
- package/.claude/skills/components/input-checkbox-radio.md +19 -3
- package/.claude/skills/components/input-checkbox.md +14 -3
- package/.claude/skills/components/input-description.md +7 -7
- package/.claude/skills/components/input-error.md +3 -3
- package/.claude/skills/components/input-label.md +7 -8
- package/.claude/skills/components/input-number.md +60 -29
- package/.claude/skills/components/input-radio.md +15 -1
- package/.claude/skills/components/{input-range-core.md → input-range.md} +53 -18
- package/.claude/skills/components/input-select.md +153 -0
- package/.claude/skills/components/input-text.md +220 -0
- package/.claude/skills/components/input-textarea.md +147 -0
- package/.claude/skills/components/select-menu.md +4 -4
- package/.claude/skills/components/toggle-switch.md +143 -0
- package/.claude/skills/composable-zod-validation.md +1 -1
- package/.claude/skills/icon-sets.md +1 -1
- package/.claude/skills/icon-sizing.md +1 -1
- package/.claude/skills/index.md +10 -10
- package/.claude/skills/storybook-add-story.md +1 -1
- package/.claude/skills/testing-add-unit-test.md +17 -0
- package/.claude/skills/theming-component-token-pattern.md +8 -8
- package/.claude/skills/theming-form-geometry-tokens.md +1 -1
- package/.vscode/srcdev-component-input-checkbox-radio.code-snippets +0 -1
- package/.vscode/srcdev-component-input-range.code-snippets +38 -18
- package/.vscode/srcdev-component-input-select.code-snippets +20 -14
- package/.vscode/srcdev-component-input-text.code-snippets +46 -40
- package/.vscode/srcdev-component-input-textarea.code-snippets +19 -14
- package/.vscode/srcdev-component-toggle-switch.code-snippets +19 -29
- package/app/assets/styles/setup/04.elements/forms/00.element-defaults.css +2 -2
- package/app/assets/styles/setup/04.elements/forms/06.button-geometry.css +1 -1
- package/app/components/01.atoms/animations/glowing-border/CONSUMER-STYLING.md +2 -2
- package/app/components/01.atoms/animations/glowing-border/stories/GlowingBorder.stories.ts +4 -4
- package/app/components/02.molecules/progress-tracker/stories/ProgressTracker.stories.ts +4 -4
- package/app/components/05.forms/form-errors/CONSUMER-STYLING.md +1 -1
- package/app/components/05.forms/form-field/CONSUMER-STYLING.md +2 -2
- package/app/components/05.forms/input-button/stories/InputButton.stories.ts +3 -3
- package/app/components/05.forms/input-checkbox/CONSUMER-STYLING.md +10 -0
- package/app/components/05.forms/input-checkbox/MultipleCheckboxes.vue +18 -4
- package/app/components/05.forms/input-checkbox/SingleCheckbox.vue +14 -2
- package/app/components/05.forms/input-checkbox/stories/MultipleCheckboxes.stories.ts +146 -37
- package/app/components/05.forms/input-checkbox/stories/SingleCheckbox.stories.ts +107 -30
- package/app/components/05.forms/input-checkbox/tests/MultipleCheckboxes.spec.ts +38 -4
- package/app/components/05.forms/input-checkbox/tests/SingleCheckbox.spec.ts +5 -0
- package/app/components/05.forms/input-checkbox-radio/CONSUMER-STYLING.md +10 -3
- package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadio.vue +1 -0
- package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioButton.vue +11 -10
- package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioField.vue +3 -0
- package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadio.stories.ts +44 -17
- package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadioButton.stories.ts +164 -58
- package/app/components/05.forms/input-checkbox-radio/stories/InputCheckboxRadioField.stories.ts +94 -21
- package/app/components/05.forms/input-checkbox-radio/tests/InputCheckboxRadio.spec.ts +14 -3
- package/app/components/05.forms/input-checkbox-radio/tests/InputCheckboxRadioButton.spec.ts +38 -3
- package/app/components/05.forms/input-checkbox-radio/tests/InputCheckboxRadioField.spec.ts +14 -3
- package/app/components/05.forms/input-description/CONSUMER-STYLING.md +7 -7
- package/app/components/05.forms/input-label/CONSUMER-STYLING.md +6 -6
- package/app/components/05.forms/input-number/CONSUMER-STYLING.md +50 -6
- package/app/components/05.forms/input-number/InputNumber.vue +39 -27
- package/app/components/05.forms/input-number/InputNumberField.vue +29 -16
- package/app/components/05.forms/input-number/stories/InputNumber.stories.ts +76 -25
- package/app/components/05.forms/input-number/stories/InputNumberField.stories.ts +116 -50
- package/app/components/05.forms/input-number/tests/InputNumber.spec.ts +49 -13
- package/app/components/05.forms/input-number/tests/InputNumberField.spec.ts +59 -5
- package/app/components/05.forms/input-radio/CONSUMER-STYLING.md +11 -0
- package/app/components/05.forms/input-radio/MultipleRadiobuttons.vue +14 -3
- package/app/components/05.forms/input-radio/stories/MultipleRadiobuttons.stories.ts +138 -38
- package/app/components/05.forms/input-radio/tests/MultipleRadioButtons.spec.ts +50 -3
- package/app/components/05.forms/input-range/CONSUMER-STYLING.md +68 -26
- package/app/components/05.forms/input-range/InputRange.vue +247 -0
- package/app/components/05.forms/input-range/{InputRangeDefault.vue → InputRangeField.vue} +41 -18
- package/app/components/05.forms/input-range/stories/InputRange.stories.ts +187 -0
- package/app/components/05.forms/input-range/stories/InputRangeField.stories.ts +196 -0
- package/app/components/05.forms/input-range/tests/{InputRangeCore.spec.ts → InputRange.spec.ts} +39 -15
- package/app/components/05.forms/input-range/tests/{InputRangeDefault.spec.ts → InputRangeField.spec.ts} +71 -7
- package/app/components/05.forms/input-select/CONSUMER-STYLING.md +80 -10
- package/app/components/05.forms/input-select/InputSelect.vue +198 -0
- package/app/components/05.forms/input-select/InputSelectField.vue +130 -0
- package/app/components/05.forms/input-select/stories/InputSelect.stories.ts +184 -0
- package/app/components/05.forms/input-select/stories/InputSelectField.stories.ts +187 -0
- package/app/components/05.forms/input-select/tests/{InputSelectCore.spec.ts → InputSelect.spec.ts} +75 -12
- package/app/components/05.forms/input-select/tests/InputSelectField.spec.ts +121 -0
- package/app/components/05.forms/input-text/CONSUMER-STYLING.md +85 -15
- package/app/components/05.forms/input-text/{InputPasswordWithLabel.vue → InputPasswordField.vue} +16 -19
- package/app/components/05.forms/input-text/{InputTextCore.vue → InputText.vue} +42 -78
- package/app/components/05.forms/input-text/{InputTextAsNumberWithLabel.vue → InputTextAsNumberField.vue} +66 -62
- package/app/components/05.forms/input-text/InputTextField.vue +168 -0
- package/app/components/05.forms/input-text/stories/InputPasswordField.stories.ts +163 -0
- package/app/components/05.forms/input-text/stories/InputText.stories.ts +194 -0
- package/app/components/05.forms/input-text/stories/InputTextAsNumberField.stories.ts +182 -0
- package/app/components/05.forms/input-text/stories/InputTextField.stories.ts +206 -0
- package/app/components/05.forms/input-text/tests/{InputPasswordWithLabel.spec.ts → InputPasswordField.spec.ts} +42 -3
- package/app/components/05.forms/input-text/tests/InputText.spec.ts +166 -0
- package/app/components/05.forms/input-text/tests/InputTextAsNumberField.spec.ts +135 -0
- package/app/components/05.forms/input-text/tests/InputTextField.spec.ts +130 -0
- package/app/components/05.forms/input-textarea/CONSUMER-STYLING.md +94 -12
- package/app/components/05.forms/input-textarea/InputTextarea.vue +165 -0
- package/app/components/05.forms/input-textarea/InputTextareaField.vue +159 -0
- package/app/components/05.forms/input-textarea/stories/InputTextarea.stories.ts +198 -0
- package/app/components/05.forms/input-textarea/stories/InputTextareaField.stories.ts +161 -0
- package/app/components/05.forms/input-textarea/tests/InputTextarea.spec.ts +149 -0
- package/app/components/05.forms/input-textarea/tests/InputTextareaField.spec.ts +122 -0
- package/app/components/05.forms/patterns/stories/ContactForm.stories.ts +13 -13
- package/app/components/05.forms/patterns/stories/LoginForm.stories.ts +6 -6
- package/app/components/05.forms/patterns/stories/MigratedFieldsForm.stories.ts +34 -36
- package/app/components/05.forms/toggle-switch/CONSUMER-STYLING.md +71 -50
- package/app/components/05.forms/toggle-switch/{ToggleSwitchCore.vue → ToggleSwitch.vue} +58 -73
- package/app/components/05.forms/toggle-switch/{ToggleSwitchWithLabel.vue → ToggleSwitchField.vue} +65 -19
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitch.stories.ts +148 -0
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchField.stories.ts +176 -0
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitch.spec.ts +145 -0
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchField.spec.ts +155 -0
- package/package.json +1 -1
- package/.claude/skills/components/input-select-core.md +0 -151
- package/.claude/skills/components/input-text-core.md +0 -204
- package/.claude/skills/components/input-textarea-core.md +0 -154
- package/.claude/skills/components/toggle-switch-core.md +0 -169
- package/app/components/05.forms/input-range/InputRangeCore.vue +0 -169
- package/app/components/05.forms/input-range/stories/InputRangeCore.stories.ts +0 -126
- package/app/components/05.forms/input-range/stories/InputRangeDefault.stories.ts +0 -108
- package/app/components/05.forms/input-select/InputSelectCore.vue +0 -188
- package/app/components/05.forms/input-select/InputSelectWithLabel.vue +0 -118
- package/app/components/05.forms/input-select/stories/InputSelectCore.stories.ts +0 -69
- package/app/components/05.forms/input-select/stories/InputSelectWithLabel.stories.ts +0 -115
- package/app/components/05.forms/input-select/tests/InputSelectWithLabel.spec.ts +0 -87
- package/app/components/05.forms/input-text/InputTextWithLabel.vue +0 -154
- package/app/components/05.forms/input-text/stories/InputPasswordWithLabel.stories.ts +0 -368
- package/app/components/05.forms/input-text/stories/InputTextAsNumberWithLabel.stories.ts +0 -480
- package/app/components/05.forms/input-text/stories/InputTextCore.stories.ts +0 -267
- package/app/components/05.forms/input-text/stories/InputTextWithLabel.stories.ts +0 -319
- package/app/components/05.forms/input-text/tests/InputTextAsNumberWithLabel.spec.ts +0 -75
- package/app/components/05.forms/input-text/tests/InputTextCore.spec.ts +0 -60
- package/app/components/05.forms/input-text/tests/InputTextWithLabel.spec.ts +0 -71
- package/app/components/05.forms/input-textarea/InputTextareaCore.vue +0 -159
- package/app/components/05.forms/input-textarea/InputTextareaWithLabel.vue +0 -136
- package/app/components/05.forms/input-textarea/stories/InputTextareaCore.stories.ts +0 -247
- package/app/components/05.forms/input-textarea/stories/InputTextareaWithLabel.stories.ts +0 -458
- package/app/components/05.forms/input-textarea/tests/InputTextareaCore.spec.ts +0 -119
- package/app/components/05.forms/input-textarea/tests/InputTextareaWithLabel.spec.ts +0 -93
- package/app/components/05.forms/toggle-switch/ToggleSwitchWithLabelInline.vue +0 -81
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchCore.stories.ts +0 -251
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabel.stories.ts +0 -251
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabelInline.stories.ts +0 -283
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchCore.spec.ts +0 -101
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchWithLabel.spec.ts +0 -133
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchWithLabelInline.spec.ts +0 -66
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
# InputTextCore Component
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
`InputTextCore` is the low-level native `<input>` primitive for the `05.forms` text-input family.
|
|
6
|
-
It renders a single styled text-like input with left/right icon slots, focus/dirty state models,
|
|
7
|
-
and numeric-pattern validation for `inputmode="numeric"`. It has no label, description, or error
|
|
8
|
-
message rendering of its own — those are composed by the wrapper variants below.
|
|
9
|
-
|
|
10
|
-
Most consumers should reach for one of the **Variants** rather than `InputTextCore` 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: `:input-variant`, `:aria-describedby`.
|
|
17
|
-
|
|
18
|
-
| Prop (template form) | Type | Default | Notes |
|
|
19
|
-
| --------------------------- | ------------------------------------------------------------ | ----------- | ---------------------------------------------------------------------- |
|
|
20
|
-
| `:type` | `"text" \| "email" \| "password" \| "number" \| "tel" \| "url" \| "date"` | `"text"` | Native input type. |
|
|
21
|
-
| `:inputmode` | `"text" \| "email" \| "tel" \| "url" \| "search" \| "numeric" \| "none" \| "decimal"` | `"text"` | Sets virtual keyboard hint; `"numeric"` also enables pattern validation. |
|
|
22
|
-
| `:maxlength` | `number` | `255` | Native `maxlength`. |
|
|
23
|
-
| `:min` | `string \| number` | (optional) | Passed straight through to the native input — e.g. earliest allowed date for `type="date"`, or lowest number for `type="number"`. Use an ISO date string (`"2026-01-01"`) for `type="date"`. |
|
|
24
|
-
| `:max` | `string \| number` | (optional) | Same as `min`, for the upper bound. |
|
|
25
|
-
| `:id` | `string` | (required) | Applied to the native input. |
|
|
26
|
-
| `:name` | `string` | (required) | Applied to the native input. |
|
|
27
|
-
| `:required` | `boolean` | `false` | Native `required`. |
|
|
28
|
-
| `:placeholder` | `string` | `""` | Native placeholder. |
|
|
29
|
-
| `:field-has-error` | `boolean` | `false` | Drives `.error` class, `aria-invalid`, and forces `theme` to `"error"`. |
|
|
30
|
-
| `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes applied to the wrapper. |
|
|
31
|
-
| `:theme` | `FormUiTheme` | `"default"` | Overridden to `"error"` internally when `fieldHasError` is true. |
|
|
32
|
-
| `:aria-describedby` | `string` | `""` | Forwarded to the native input. |
|
|
33
|
-
| `:input-variant` | `"normal" \| "outlined" \| "underlined"` | `"normal"` | Visual style — border box vs. underline. |
|
|
34
|
-
|
|
35
|
-
### v-models
|
|
36
|
-
|
|
37
|
-
| Model | Type | Notes |
|
|
38
|
-
| ----------------- | --------- | ---------------------------------------------------------- |
|
|
39
|
-
| `v-model` | `string` | The input value. **Required** (`defineModel({ required: true })`, 2026-09-25), same for `InputTextWithLabel` and `InputPasswordWithLabel`. |
|
|
40
|
-
| `v-model:is-dirty` | `boolean` | Set to `true` once the wrapper variant detects a non-empty value at mount or on change. |
|
|
41
|
-
| `v-model:is-active`| `boolean` | Tracks focus state (`focusin`/`focusout`). |
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## Slots
|
|
46
|
-
|
|
47
|
-
| Slot | Notes |
|
|
48
|
-
| ------- | ---------------------------------------------------------------------- |
|
|
49
|
-
| `left` | Rendered before the input, inside the bordered wrapper (e.g. an icon or `InputButton variant="inline"`). |
|
|
50
|
-
| `right` | Rendered after the input, inside the bordered wrapper. |
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## CSS Token Customization
|
|
55
|
-
|
|
56
|
-
All `--input-text-*` tokens can be overridden at global, page, or instance scope, each falling back
|
|
57
|
-
to the shared `--theme-*`/`--theme-input-*` tokens every other themed form component also uses. See
|
|
58
|
-
`CONSUMER-STYLING.md` in the component directory for the full token table.
|
|
59
|
-
|
|
60
|
-
**Common tokens:**
|
|
61
|
-
- `--input-text-surface` — wrapper background
|
|
62
|
-
- `--input-text-border` / `--input-text-border-hover` / `--input-text-border-focus` — border/outline colour
|
|
63
|
-
- `--input-text-color` — input text colour
|
|
64
|
-
- `--input-text-placeholder-color` — placeholder colour
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## Using `min`/`max` for a native date picker
|
|
69
|
-
|
|
70
|
-
`type="date"` plus `min`/`max` is the standard way to stop the browser's native date picker from
|
|
71
|
-
offering dates outside an allowed range — e.g. preventing a booking date in the past:
|
|
72
|
-
|
|
73
|
-
```vue
|
|
74
|
-
<InputTextCore
|
|
75
|
-
id="appointment-date"
|
|
76
|
-
name="appointmentDate"
|
|
77
|
-
type="date"
|
|
78
|
-
v-model="appointmentDate"
|
|
79
|
-
:min="new Date().toISOString().split('T')[0]"
|
|
80
|
-
max="2027-12-31"
|
|
81
|
-
/>
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
`min`/`max` are passed straight through with no validation of their own — the browser enforces the
|
|
85
|
-
range in its native picker/typed-entry UI. Pair with server-side validation; a native `min`/`max`
|
|
86
|
-
constraint is a UX affordance, not a substitute for validating the submitted value.
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## Variants
|
|
91
|
-
|
|
92
|
-
Three sibling wrappers compose `InputTextCore` with `InputLabel`, `InputDescription`, and
|
|
93
|
-
`InputError` for the common "labelled field" case. Each forwards `min`/`max` straight through if
|
|
94
|
-
you pass them.
|
|
95
|
-
|
|
96
|
-
### InputTextWithLabel
|
|
97
|
-
|
|
98
|
-
The general-purpose labelled text field — supports every `InputTypesText` value including
|
|
99
|
-
`"date"`.
|
|
100
|
-
|
|
101
|
-
```vue
|
|
102
|
-
<InputTextWithLabel
|
|
103
|
-
v-model="appointmentDate"
|
|
104
|
-
id="appointment-date"
|
|
105
|
-
name="appointmentDate"
|
|
106
|
-
type="date"
|
|
107
|
-
label="Appointment date"
|
|
108
|
-
:min="new Date().toISOString().split('T')[0]"
|
|
109
|
-
max="2027-12-31"
|
|
110
|
-
:error-message="errors.appointmentDate"
|
|
111
|
-
:field-has-error="!!errors.appointmentDate"
|
|
112
|
-
/>
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
Props: same `type`/`inputmode`/`maxlength`/`min`/`max`/`name`/`placeholder`/`label`/`errorMessage`/
|
|
116
|
-
`fieldHasError`/`required`/`styleClassPassthrough`/`theme`/`inputVariant` as `InputTextCore`, plus
|
|
117
|
-
`label: string` (required) and `errorMessage: object | string` (required — pass `""` if unused).
|
|
118
|
-
Slots: `left`, `right`, `descriptionHtml`, `descriptionText`.
|
|
119
|
-
|
|
120
|
-
### InputPasswordWithLabel
|
|
121
|
-
|
|
122
|
-
Adds a show/hide toggle button (`InputButton variant="inline"` in the `right` slot) that swaps
|
|
123
|
-
`type` between `"password"` and `"text"`.
|
|
124
|
-
|
|
125
|
-
```vue
|
|
126
|
-
<InputPasswordWithLabel
|
|
127
|
-
v-model="password"
|
|
128
|
-
name="password"
|
|
129
|
-
label="Password"
|
|
130
|
-
:error-message="errors.password"
|
|
131
|
-
:field-has-error="!!errors.password"
|
|
132
|
-
show-password-text="Show password"
|
|
133
|
-
hide-password-text="Hide password"
|
|
134
|
-
/>
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
Extra props: `showPasswordText` (default `"Show password"`) and `hidePasswordText` (default
|
|
138
|
-
`"Hide password"`) — the toggle button's accessible name in each state. Pass translated strings
|
|
139
|
-
here for a non-English consumer app; there is no built-in i18n framework dependency.
|
|
140
|
-
|
|
141
|
-
### InputTextAsNumberWithLabel
|
|
142
|
-
|
|
143
|
-
A numeric stepper: left/right `InputButton variant="inline"` buttons decrement/increment the
|
|
144
|
-
value by `step`, disabled once `min`/`max` is reached.
|
|
145
|
-
|
|
146
|
-
```vue
|
|
147
|
-
<InputTextAsNumberWithLabel
|
|
148
|
-
v-model="quantity"
|
|
149
|
-
name="quantity"
|
|
150
|
-
label="Quantity"
|
|
151
|
-
:min="1"
|
|
152
|
-
:max="99"
|
|
153
|
-
:step="1"
|
|
154
|
-
step-down-text="Decrease quantity"
|
|
155
|
-
step-up-text="Increase quantity"
|
|
156
|
-
>
|
|
157
|
-
<template #left>−</template>
|
|
158
|
-
<template #right>+</template>
|
|
159
|
-
</InputTextAsNumberWithLabel>
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
`v-model` is `number | undefined` (`defineModel({ default: undefined })`): clearing the field sets it to `undefined`, so bind a `ref<number | undefined>` rather than assuming a number.
|
|
163
|
-
|
|
164
|
-
Extra props: `min: number` (required), `max: number` (required), `step?: number` (default `1`),
|
|
165
|
-
`stepDownText`/`stepUpText` (default `"Step down"`/`"Step up"`) — the decrement/increment buttons'
|
|
166
|
-
accessible names. Requires `left`/`right` slot content for the step icons (no default icon).
|
|
167
|
-
|
|
168
|
-
**Fixed 2026-09-22**: `updateElementClasses(["input-text-as-number", "has-left-button",
|
|
169
|
-
"has-right-button"])` computes marker classes onto this component's own `elementClasses`, which is
|
|
170
|
-
bound to the outer `.input-text-with-label` wrapper — but the nested `<InputTextCore>` was passed
|
|
171
|
-
`:style-class-passthrough` as a bare shorthand, which resolves to the raw incoming
|
|
172
|
-
`styleClassPassthrough` **prop**, not the local `elementClasses`. So the marker classes never
|
|
173
|
-
reached the actual `<input>`, the scoped `.input-text-core.input-text-as-number { width:
|
|
174
|
-
fit-content; text-align: center; ... }` CSS never matched anything, and the input silently fell
|
|
175
|
-
back to `InputTextCore`'s default full-width, left-aligned styling instead of the intended
|
|
176
|
-
compact, centered numeric stepper box. Fixed by changing that binding to
|
|
177
|
-
`:style-class-passthrough="elementClasses"` — `elementClasses` still carries any consumer-supplied
|
|
178
|
-
`styleClassPassthrough` too, since the composable seeds it from the prop before the marker classes
|
|
179
|
-
are toggled on top. When a component computes its own marker/state classes via
|
|
180
|
-
`updateElementClasses()` and needs them on a *child* component rather than its own root element,
|
|
181
|
-
always pass the computed `elementClasses` explicitly — the `:x` shorthand only ever resolves to
|
|
182
|
-
the prop of that exact name, never a same-named local composable output.
|
|
183
|
-
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
## Accessibility
|
|
187
|
-
|
|
188
|
-
- `field-has-error` sets `aria-invalid="true"` and forces the error theme, independent of what
|
|
189
|
-
`theme` was passed.
|
|
190
|
-
- `inputmode="numeric"` adds a `pattern="[0-9]+"` and blocks non-numeric `beforeinput` edits that
|
|
191
|
-
would fail it.
|
|
192
|
-
- Focus is tracked via native `focusin`/`focusout`, not a custom keyboard handler — `:focus-visible`
|
|
193
|
-
styling is preserved.
|
|
194
|
-
- The password/number variants give their icon-only toggle/step buttons a real accessible name via
|
|
195
|
-
`InputButton`'s `buttonText` (visually hidden via `.sr-only` when an icon-only slot is used).
|
|
196
|
-
|
|
197
|
-
---
|
|
198
|
-
|
|
199
|
-
## Notes
|
|
200
|
-
|
|
201
|
-
- **No built-in label/error rendering**: `InputTextCore` alone renders no `<label>` or error text —
|
|
202
|
-
use a variant, or compose your own wrapper the same way they do.
|
|
203
|
-
- **`min`/`max` need matching `type`**: they're inert on `type="text"`/`"email"`/etc. — only
|
|
204
|
-
`"date"` and `"number"` (native range-constrained types) act on them.
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# InputTextareaCore Component
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
`InputTextareaCore` is the low-level native `<textarea>` primitive for the `05.forms` multi-line
|
|
6
|
-
text family. It renders a single native textarea with left/right decorative slots. It has no
|
|
7
|
-
label, description, or error-message rendering of its own — that's composed by the
|
|
8
|
-
`InputTextareaWithLabel` variant below.
|
|
9
|
-
|
|
10
|
-
Most consumers should reach for **InputTextareaWithLabel** rather than `InputTextareaCore`
|
|
11
|
-
directly.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Props reference
|
|
16
|
-
|
|
17
|
-
> **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`.
|
|
18
|
-
|
|
19
|
-
| Prop (template form) | Type | Default | Notes |
|
|
20
|
-
|------|------|---------|-------|
|
|
21
|
-
| `:id` | `string` | (required) | Applied to the native textarea. |
|
|
22
|
-
| `:name` | `string` | (required) | Applied to the native textarea. |
|
|
23
|
-
| `:maxlength` | `number` | `255` | Native `maxlength`. |
|
|
24
|
-
| `:placeholder` | `string` | `""` | Native `placeholder`. |
|
|
25
|
-
| `:required` | `boolean` | `false` | Native `required`. |
|
|
26
|
-
| `:theme` | `FormUiTheme` | `"default"` | Sets `data-theme` on the wrapper. |
|
|
27
|
-
| `: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`. |
|
|
28
|
-
| `:field-has-error` | `boolean` | `false` | Drives `data-invalid` on the wrapper and `aria-invalid` on the textarea. |
|
|
29
|
-
| `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes applied to the textarea element. |
|
|
30
|
-
| `:aria-describedby` | `string` | `""` | Forwarded to the native textarea. |
|
|
31
|
-
|
|
32
|
-
### v-models
|
|
33
|
-
|
|
34
|
-
- `v-model` — `string | number | readonly string[] | null | undefined`, **required**
|
|
35
|
-
(`defineModel({ required: true })`, satisfying `vue/require-default-prop` the same way as
|
|
36
|
-
`InputRangeCore`/`InputNumber` — see `project_ledger_eslint_issues_column` in memory).
|
|
37
|
-
"Required" here means a consumer must bind `v-model`, not that the value can't be an empty
|
|
38
|
-
string — `""` is a perfectly valid textarea value.
|
|
39
|
-
- `v-model:is-dirty` — `boolean`. Set once by `InputTextareaWithLabel` the first time the value
|
|
40
|
-
becomes non-empty (never reset back to `false` by this component).
|
|
41
|
-
- `v-model:is-active` — `boolean`. Tracks native focus/blur (`focusin`/`focusout`), toggling
|
|
42
|
-
`.active` on the wrapper and textarea.
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## Slots
|
|
47
|
-
|
|
48
|
-
| Slot | Notes |
|
|
49
|
-
|------|-------|
|
|
50
|
-
| `left` | Rendered before the textarea (decorative — e.g. an icon). Adds `has-left-slot` to the wrapper. |
|
|
51
|
-
| `right` | Rendered after the textarea (decorative). Adds `has-right-slot` to the wrapper. |
|
|
52
|
-
|
|
53
|
-
Unlike `InputNumber`/`InputRangeCore`, these slots have no built-in `InputButton`
|
|
54
|
-
divider/embedded-button styling — they're plain decorative content (see the Storybook stories,
|
|
55
|
-
which use emoji), not a stepper control.
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## CSS token API
|
|
60
|
-
|
|
61
|
-
See [CONSUMER-STYLING.md](../../app/components/05.forms/input-textarea/CONSUMER-STYLING.md) for
|
|
62
|
-
the full table with defaults.
|
|
63
|
-
|
|
64
|
-
**Common tokens:**
|
|
65
|
-
- `--input-textarea-surface` — wrapper background, falls back to `--theme-input-surface`
|
|
66
|
-
- `--input-textarea-border` / `-border-hover` / `-border-focus` — border colour resting, mouse-hover outline, and `:focus-visible` outline, fall back to `--theme-border`/`--theme-border-focus`
|
|
67
|
-
- `--input-textarea-text-color` — textarea text colour, falls back to `--theme-input-text-color-normal`
|
|
68
|
-
- `--input-textarea-placeholder-color` — placeholder text colour, falls back to `--theme-input-placeholder`
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## Fixed 2026-09-22: undefined tokens and dead code
|
|
73
|
-
|
|
74
|
-
`InputTextareaCore` migrated to full compliance from a `variants:true`, `eslint_issues:true`,
|
|
75
|
-
score-1/5 state (no public tokens, no CONSUMER-STYLING.md, no tests, no skill doc, no snippet).
|
|
76
|
-
|
|
77
|
-
- **Silently broken styling from undefined CSS custom properties** (same bug class as pitfall #21
|
|
78
|
-
and `InputRangeCore`'s dead `--theme-form-range-accent-color`): the wrapper's `padding-inline`
|
|
79
|
-
read `--element-decorator-padding-inline`, and the placeholder's `font-size`/`font-style`/
|
|
80
|
-
`line-height`/`font-weight` read `--input-placeholder-font-size`/`-font-style`/`-line-height`/
|
|
81
|
-
`-font-weight` — **none of these five custom properties were declared anywhere in the
|
|
82
|
-
codebase**. Per the CSS spec, an undefined `var()` with no fallback makes the whole declaration
|
|
83
|
-
invalid, so all five silently fell back to the browser/inherited default instead of the intended
|
|
84
|
-
design. Fixed by switching to the tokens `InputTextCore` actually uses for the same purpose:
|
|
85
|
-
`--input-padding-inline` for the wrapper padding, and `--theme-input-placeholder-font-size` +
|
|
86
|
-
hardcoded `italic`/`1`/`normal` for the placeholder (matching `InputTextCore`'s placeholder rule
|
|
87
|
-
exactly — those three placeholder sub-properties aren't tokenized there either).
|
|
88
|
-
- **Zero public `--input-textarea-*` tokens existed at all** — every colour value read a
|
|
89
|
-
`--theme-*` global directly, with no component-level override point (the exact gap pitfall #14
|
|
90
|
-
describes for early `05.forms` components). Added the full set matching `InputTextCore`'s shape:
|
|
91
|
-
`--input-textarea-surface`, `-border`, `-border-hover`, `-border-focus`, `-text-color`,
|
|
92
|
-
`-placeholder-color`.
|
|
93
|
-
- **Dead code**: both `InputTextareaCore` and `InputTextareaWithLabel` declared
|
|
94
|
-
`const FormUiTheme = computed(() => props.fieldHasError ? "error" : props.theme);` — a value
|
|
95
|
-
binding that shadows the imported `FormUiTheme` *type* import of the same name, and is never
|
|
96
|
-
referenced anywhere in either file (the template's `:theme` shorthand resolves straight from
|
|
97
|
-
`defineProps`). Removed both. `InputTextareaWithLabel` also had ~5 lines of commented-out dead
|
|
98
|
-
code (an old, hand-rolled `id`/`errorId`/`ariaDescribedby` implementation, superseded by
|
|
99
|
-
`useAriaDescribedById` right below it) — removed. (The identical dead `FormUiTheme` computed
|
|
100
|
-
still exists in `InputTextCore`/`InputTextWithLabel` themselves — out of scope for this pass
|
|
101
|
-
since those weren't being touched, but worth cleaning up next time either is migrated.)
|
|
102
|
-
- **`InputTextareaWithLabel` passed `:label` down to `InputTextareaCore`**, which has no `label`
|
|
103
|
-
prop (matching `InputTextCore`, which doesn't have one either) — it fell through as a
|
|
104
|
-
meaningless raw `label="..."` HTML attribute on the wrapper `<div>`. Removed the pass-through;
|
|
105
|
-
`InputLabel` (which does have a real `label` concept) already renders it correctly. Covered by
|
|
106
|
-
a regression test.
|
|
107
|
-
- **`defineModel()` had no default**, tripping `vue/require-default-prop` in both files — fixed
|
|
108
|
-
with `{ required: true }`, same pattern as `InputRangeCore`/`InputNumber` (see the v-model
|
|
109
|
-
note above for why this is still correct even though an empty string is a valid value here).
|
|
110
|
-
- **`InputTextareaWithLabel.stories.ts` used `reactive()` in two render functions
|
|
111
|
-
(`AllVariants`/`FormExamples`) without importing it** — only `computed` was imported from
|
|
112
|
-
`"vue"`. This library's `.ts` Storybook files need Vue reactivity APIs imported explicitly (no
|
|
113
|
-
Nuxt auto-import outside `.vue` files) — a real runtime `ReferenceError` waiting to happen the
|
|
114
|
-
first time either story rendered. Fixed by adding `reactive` to the existing `computed` import.
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
## Variants
|
|
119
|
-
|
|
120
|
-
### InputTextareaWithLabel
|
|
121
|
-
|
|
122
|
-
`InputTextareaWithLabel` (`InputTextareaWithLabel.vue`) composes `InputTextareaCore` with
|
|
123
|
-
`InputLabel`, `InputDescription` (positioned before or after the field depending on
|
|
124
|
-
`inputVariant === "outlined"`, matching `InputTextWithLabel`'s exact placement rule), and
|
|
125
|
-
`InputError`.
|
|
126
|
-
|
|
127
|
-
**Additional props over InputTextareaCore:**
|
|
128
|
-
|
|
129
|
-
| Prop (template form) | Type | Default | Notes |
|
|
130
|
-
|------|------|---------|-------|
|
|
131
|
-
| `:label` | `string` | (required) | Rendered via `InputLabel`. |
|
|
132
|
-
| `:error-message` | `object \| string` | (required) | Rendered via `InputError` when `field-has-error` is true. |
|
|
133
|
-
|
|
134
|
-
**Slots**: `descriptionHtml`, `descriptionText` (both forwarded to `InputDescription`), plus
|
|
135
|
-
`left`/`right` (forwarded straight through to the underlying `InputTextareaCore`).
|
|
136
|
-
|
|
137
|
-
```vue
|
|
138
|
-
<InputTextareaWithLabel
|
|
139
|
-
name="message"
|
|
140
|
-
label="Message"
|
|
141
|
-
v-model="message"
|
|
142
|
-
placeholder="How can we help you?"
|
|
143
|
-
:maxlength="500"
|
|
144
|
-
:error-message="errors.message"
|
|
145
|
-
:field-has-error="!!errors.message"
|
|
146
|
-
>
|
|
147
|
-
<template #descriptionText>Please describe your inquiry in detail</template>
|
|
148
|
-
</InputTextareaWithLabel>
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
`v-model` is `string | number | readonly string[] | null | undefined`, **required** — same as
|
|
152
|
-
`InputTextareaCore` above. `isDirty`/`isActive` are managed internally (not exposed as props) —
|
|
153
|
-
`isDirty` flips to `true` the first time the value becomes a non-empty string (checked on mount
|
|
154
|
-
and on every change), and never resets.
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
# ToggleSwitchCore Component
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
`ToggleSwitchCore` is the low-level pill-shaped (or square, via `round`) toggle switch primitive
|
|
6
|
-
for the `05.forms` toggle family. It renders a visually-hidden native `<input type="checkbox">`
|
|
7
|
-
(kept in the DOM for real keyboard/checkbox semantics) plus a styled track/thumb visual, with
|
|
8
|
-
optional icon slots shown inside the thumb for the on/off states. It has no label, description, or
|
|
9
|
-
error-message rendering of its own — that's composed by the `ToggleSwitchWithLabel` and
|
|
10
|
-
`ToggleSwitchWithLabelInline` variants below.
|
|
11
|
-
|
|
12
|
-
Most consumers should reach for **ToggleSwitchWithLabel** or **ToggleSwitchWithLabelInline** rather
|
|
13
|
-
than `ToggleSwitchCore` directly.
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## Props reference
|
|
18
|
-
|
|
19
|
-
> **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`, `:true-value`, `:false-value`.
|
|
20
|
-
|
|
21
|
-
| Prop (template form) | Type | Default | Notes |
|
|
22
|
-
|------|------|---------|-------|
|
|
23
|
-
| `:id` | `string` | (required) | Combined with a fixed prefix to build the native input's `id` (`toggle-switch-${id}`). |
|
|
24
|
-
| `:name` | `string` | (required) | Applied to the native checkbox. |
|
|
25
|
-
| `:required` | `boolean` | `false` | Native `required`. |
|
|
26
|
-
| `:field-has-error` | `boolean` | `false` | Drives `aria-invalid` on the checkbox, and switches `data-theme` to `"error"` regardless of the `theme` prop's own value. |
|
|
27
|
-
| `:true-value` / `:false-value` | `string \| number \| boolean` | `true` / `false` | The two values `v-model` toggles between. |
|
|
28
|
-
| `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes applied to the root element. |
|
|
29
|
-
| `:theme` | `FormUiTheme` | `"default"` | Sets `data-theme` on the root (superseded by `"error"` when `field-has-error` is true — see above). |
|
|
30
|
-
| `:round` | `boolean` | `true` | `true` renders a pill-shaped track/thumb (`border-radius: 100vw`); `false` renders a rounded-square shape (`border-radius: 0.4rem`). See the `--toggle-switch-border-radius` token in CONSUMER-STYLING.md to override the exact value either state produces. |
|
|
31
|
-
| `:aria-describedby` | `string` | `""` | Forwarded to the native checkbox (omitted when empty). Changed 2026-09-27: it was ignored before, and the checkbox always pointed at `<id>-description` whether or not that element existed, so `ToggleSwitchWithLabel`'s error-message id never reached it. |
|
|
32
|
-
|
|
33
|
-
### v-model
|
|
34
|
-
|
|
35
|
-
- `v-model` — `string | number | boolean`, **required** (`defineModel({ required: true })`, satisfying `vue/require-default-prop` — see `project_ledger_eslint_issues_column` in memory).
|
|
36
|
-
|
|
37
|
-
### Slots
|
|
38
|
-
|
|
39
|
-
- `iconOn` / `iconOff` — replace the default `material-symbols:circle-outline` icon shown inside the thumb for each state. Providing either slot switches off the `use-default-icons` wrapper class (checked via `useSlots()`, so this only reacts to slot *presence*, not conditional slot content).
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## CSS token API
|
|
44
|
-
|
|
45
|
-
See [CONSUMER-STYLING.md](../../app/components/05.forms/toggle-switch/CONSUMER-STYLING.md) for the
|
|
46
|
-
full table with defaults.
|
|
47
|
-
|
|
48
|
-
**Common tokens:**
|
|
49
|
-
- `--toggle-switch-surface` / `-surface-hover` — track background resting/hover, fall back to `--theme-checkbox-symbol-surface`/`--theme-surface-subtle`
|
|
50
|
-
- `--toggle-switch-border` / `-border-focus` — track border colour, and `:focus-visible` outline colour, fall back to `--theme-border`/`--theme-border-focus`
|
|
51
|
-
- `--toggle-switch-symbol-surface-off` / `-symbol-surface-on` — thumb background unchecked/checked
|
|
52
|
-
- `--toggle-switch-border-radius` — track and thumb shape; see the `round` prop above
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
|
|
56
|
-
## Fixed 2026-09-23: dead functionality and story bugs
|
|
57
|
-
|
|
58
|
-
`ToggleSwitchCore` (and its two variants) migrated to full compliance from a `variants:true`,
|
|
59
|
-
`eslint_issues:true`, score-3/5 state (no tests, no skill doc).
|
|
60
|
-
|
|
61
|
-
- **`round` prop was completely dead** — both variants declared and forwarded it to
|
|
62
|
-
`ToggleSwitchCore`, and Storybook's `SquareShape` story existed specifically to demonstrate
|
|
63
|
-
`round: false`, but `ToggleSwitchCore` never declared the prop at all, so it silently fell
|
|
64
|
-
through as an inert `round` DOM attribute on the root `<div>` and the track/thumb stayed pill-shaped
|
|
65
|
-
regardless. Fixed by adding `round` to `ToggleSwitchCore` and wiring it to a new
|
|
66
|
-
`--toggle-switch-border-radius` token (100vw when round, 0.4rem when square).
|
|
67
|
-
- **`fieldHasError` didn't switch the component's theme** — `ToggleSwitchCore` and
|
|
68
|
-
`ToggleSwitchWithLabel` both computed an error-aware theme value but bound `data-theme` to the
|
|
69
|
-
raw `theme` prop instead of that computed, so this library's `[data-theme="error"]` global
|
|
70
|
-
token overrides never applied when a consumer set `fieldHasError`. Fixed by binding `data-theme`
|
|
71
|
-
to the computed value in both files.
|
|
72
|
-
- **An invalid `for` attribute on a `<div>`** (`.toggle-switch-wrapper`) was removed — `for` is
|
|
73
|
-
only a valid HTML attribute on `<label>`/`<output>`, so it was inert dead markup; the real
|
|
74
|
-
label association is via `InputLabel`'s own `for`/`id` wiring in the variants.
|
|
75
|
-
- **`defineModel` had no default**, tripping `vue/require-default-prop` (a known false-positive
|
|
76
|
-
for `defineModel` — see `project_ledger_eslint_issues_column` in memory) — fixed with
|
|
77
|
-
`{ required: true }` across `ToggleSwitchCore` and both variants, since a checkbox genuinely
|
|
78
|
-
can't be meaningfully empty.
|
|
79
|
-
- **Two dead, unread `--_transition-duration` private tokens** (one per variant) were removed —
|
|
80
|
-
nothing in either variant's own CSS or `ToggleSwitchCore`'s ever read them; the actual
|
|
81
|
-
transition durations are hardcoded `0.4s` literals inside `ToggleSwitchCore`.
|
|
82
|
-
- **Custom `iconOn`/`iconOff` slot content could render as a non-square box, throwing off
|
|
83
|
-
centering** — `.symbol-icon` is `display: grid; place-content: center;`, and its single
|
|
84
|
-
auto-sized track shrink-wraps to the slotted content's own box. A plain inline element (e.g. a
|
|
85
|
-
`<span>` wrapping a 16×16 SVG, as in the `WithCustomIcons` stories) sizes to its line box's
|
|
86
|
-
leading rather than its intrinsic height, rendering as ~16×20 instead of 16×16. Fixed by adding
|
|
87
|
-
`line-height: 0` to `.symbol-icon`, which collapses that leading for whatever markup a consumer
|
|
88
|
-
slots in. The default icon (`@nuxt/icon`'s `Icon` component) wasn't affected, since iconify sets
|
|
89
|
-
its own sizing/`vertical-align` internally.
|
|
90
|
-
- **All three story files' `theme` control listed the wrong option set**
|
|
91
|
-
(`["primary", "secondary", "tertiary", "ghost", "error", "success", "warning"]`, none of which
|
|
92
|
-
match `FormUiTheme`) — fixed to the real `["default", "success", "error", "warning"]`.
|
|
93
|
-
`ToggleSwitchWithLabelInline.stories.ts`'s `InlineFormExample` story also passed the now-invalid
|
|
94
|
-
`theme="secondary"` directly — fixed to `"default"`.
|
|
95
|
-
|
|
96
|
-
---
|
|
97
|
-
|
|
98
|
-
## Variants
|
|
99
|
-
|
|
100
|
-
### ToggleSwitchWithLabel
|
|
101
|
-
|
|
102
|
-
`ToggleSwitchWithLabel` (`ToggleSwitchWithLabel.vue`) composes `ToggleSwitchCore` with
|
|
103
|
-
`InputLabel`, an optional description (rendered by `InputDescription`, the same as every other labelled form field), and `InputError`.
|
|
104
|
-
|
|
105
|
-
**Additional props over ToggleSwitchCore:**
|
|
106
|
-
|
|
107
|
-
| Prop (template form) | Type | Default | Notes |
|
|
108
|
-
|------|------|---------|-------|
|
|
109
|
-
| `:label` | `string` | (required) | Rendered via `InputLabel`. |
|
|
110
|
-
| `:error-message` | `object \| string` | `""` | Rendered via `InputError` when `field-has-error` is true. |
|
|
111
|
-
|
|
112
|
-
Note this wrapper passes `id`/`name`/`required`/`field-has-error`/`true-value`/`false-value`/
|
|
113
|
-
`theme`/`round`/`aria-describedby` straight through to `ToggleSwitchCore`; `id` is a self-generated
|
|
114
|
-
`useId()` value, not a prop — consumers don't set it directly.
|
|
115
|
-
|
|
116
|
-
**Slots**: `descriptionText` / `descriptionHtml` (help text between the label and the switch, via `InputDescription`), `iconOn`, `iconOff` (the latter two forwarded to `ToggleSwitchCore`).
|
|
117
|
-
|
|
118
|
-
The switch's `aria-describedby` lists the description id when a description slot is present and the error id when `field-has-error` is true, both together when both apply.
|
|
119
|
-
|
|
120
|
-
```vue
|
|
121
|
-
<ToggleSwitchWithLabel
|
|
122
|
-
name="notifications"
|
|
123
|
-
label="Enable notifications"
|
|
124
|
-
v-model="notificationsEnabled"
|
|
125
|
-
:error-message="errors.notifications"
|
|
126
|
-
:field-has-error="!!errors.notifications"
|
|
127
|
-
>
|
|
128
|
-
<template #descriptionText>Turn this on to receive notifications</template>
|
|
129
|
-
</ToggleSwitchWithLabel>
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
#### Breaking change 2026-09-29: description slot
|
|
133
|
-
|
|
134
|
-
The `description` slot is removed. Use `descriptionText` (plain text, wrapped in a `<p>`) or
|
|
135
|
-
`descriptionHtml` (rich markup) instead. The old slot rendered into a bare `<div>` with no class,
|
|
136
|
-
spacing or tokens, so its content sat flush against the switch; it now goes through
|
|
137
|
-
`InputDescription` like every other labelled form field, styled by the `--input-description-*`
|
|
138
|
-
tokens (see `input-description` CONSUMER-STYLING.md) and targetable as
|
|
139
|
-
`.toggle-switch-description`. `aria-describedby` also used to drop the description id whenever
|
|
140
|
-
the field had an error; it now lists both.
|
|
141
|
-
|
|
142
|
-
```vue
|
|
143
|
-
<!-- before -->
|
|
144
|
-
<template #description>Sends at most one email a week.</template>
|
|
145
|
-
<!-- after -->
|
|
146
|
-
<template #descriptionText>Sends at most one email a week.</template>
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### ToggleSwitchWithLabelInline
|
|
150
|
-
|
|
151
|
-
`ToggleSwitchWithLabelInline` (`ToggleSwitchWithLabelInline.vue`) composes
|
|
152
|
-
`ToggleSwitchCore` with `InputLabel` laid out inline (label and switch side by side, no
|
|
153
|
-
description/error-message support — this variant is for compact settings-row usage).
|
|
154
|
-
|
|
155
|
-
**Additional props over ToggleSwitchCore:**
|
|
156
|
-
|
|
157
|
-
| Prop (template form) | Type | Default | Notes |
|
|
158
|
-
|------|------|---------|-------|
|
|
159
|
-
| `:label` | `string` | (required) | Rendered via `InputLabel`. |
|
|
160
|
-
| `:label-weight` | `LabelWeight` (`"normal" \| "semi-bold" \| "bold"`) | `"normal"` | Maps to a `body-normal`/`body-normal-semibold`/`body-normal-bold` class on the label. |
|
|
161
|
-
|
|
162
|
-
This variant has no `required`/`field-has-error`/`error-message` props — it never claimed error-state
|
|
163
|
-
support, unlike `ToggleSwitchWithLabel`.
|
|
164
|
-
|
|
165
|
-
**Slots**: `iconOn`, `iconOff` (forwarded to `ToggleSwitchCore`).
|
|
166
|
-
|
|
167
|
-
```vue
|
|
168
|
-
<ToggleSwitchWithLabelInline name="darkMode" label="Dark mode" v-model="darkModeEnabled" />
|
|
169
|
-
```
|