vunor 0.1.2 → 0.1.4
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/README.md +6 -11
- package/dist/AppLayout.mjs +0 -0
- package/dist/AppToasts.mjs +4 -8
- package/dist/Button.mjs +5 -10
- package/dist/ButtonBase.mjs +4 -8
- package/dist/Calendar.mjs +10 -14
- package/dist/Card.mjs +4 -9
- package/dist/CardHeader.mjs +5 -10
- package/dist/CardInner.mjs +4 -9
- package/dist/Checkbox.mjs +4 -8
- package/dist/Combobox.d.mts +9 -9
- package/dist/Combobox.mjs +5 -9
- package/dist/DatePicker.d.mts +9 -9
- package/dist/DatePicker.mjs +12 -17
- package/dist/DatePickerBase.d.mts +9 -9
- package/dist/DatePickerBase.mjs +13 -17
- package/dist/DatePickerInner.mjs +5 -9
- package/dist/DatePickerPopup.mjs +4 -8
- package/dist/DelayedSwitch.mjs +4 -9
- package/dist/DevTools.mjs +583 -150
- package/dist/Dialog.d.mts +6 -6
- package/dist/Dialog.mjs +4 -8
- package/dist/Icon.mjs +4 -9
- package/dist/InnerLoading.mjs +4 -8
- package/dist/Input.d.mts +13 -13
- package/dist/Input.mjs +4 -8
- package/dist/InputBase.d.mts +9 -9
- package/dist/InputBase.mjs +4 -8
- package/dist/Label.mjs +1 -4
- package/dist/LoadingIndicator.mjs +4 -8
- package/dist/Menu.d.mts +2 -2
- package/dist/Menu.mjs +5 -14
- package/dist/MenuItem.mjs +4 -9
- package/dist/OverflowContainer.mjs +4 -8
- package/dist/Pagination.mjs +4 -9
- package/dist/Popover.mjs +4 -9
- package/dist/ProgressBar.mjs +4 -9
- package/dist/RadioGroup.mjs +4 -8
- package/dist/Select.d.mts +9 -9
- package/dist/Select.mjs +4 -9
- package/dist/SelectBase.mjs +4 -8
- package/dist/Slider.mjs +4 -8
- package/dist/Tabs.mjs +4 -9
- package/dist/nuxt.mjs +1 -3
- package/dist/theme.d.mts +35 -0
- package/dist/theme.mjs +313 -336
- package/dist/utils-6bTTIoaw.js +40 -0
- package/dist/utils.d.mts +2 -2
- package/dist/utils.mjs +1 -4
- package/dist/vite.mjs +1 -2
- package/dist/vunor.d.mts +13 -13
- package/dist/vunor.mjs +1 -6
- package/package.json +30 -37
- package/scripts/setup-skills.js +0 -78
- package/skills/vunor/SKILL.md +0 -115
- package/skills/vunor/components.md +0 -320
- package/skills/vunor/core.md +0 -173
- package/skills/vunor/forms.md +0 -348
- package/skills/vunor/palette.md +0 -223
- package/skills/vunor/rules.md +0 -263
- package/skills/vunor/shortcuts.md +0 -239
- package/skills/vunor/typography.md +0 -204
package/skills/vunor/forms.md
DELETED
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
# Form components — vunor
|
|
2
|
-
|
|
3
|
-
> Detailed usage for Input, Select, Combobox, Checkbox, RadioGroup, Slider, and DatePicker.
|
|
4
|
-
|
|
5
|
-
## Concepts
|
|
6
|
-
|
|
7
|
-
Form components use the `i8-*` shortcut system for visual styling and Reka UI primitives for accessibility. They support three design variants (`flat`, `filled`, `round`) and integrate with the color scope system.
|
|
8
|
-
|
|
9
|
-
All form components:
|
|
10
|
-
- Use `scope-{color}` for theming (default scope applies if none set)
|
|
11
|
-
- Support `disabled`, `error`, and `loading` states
|
|
12
|
-
- Emit standard Vue events (`update:modelValue`, `blur`, `focus`)
|
|
13
|
-
- Use `aria-*` and `data-*` attributes for state (not HTML attributes)
|
|
14
|
-
|
|
15
|
-
## API Reference
|
|
16
|
-
|
|
17
|
-
### `<VuInput>`
|
|
18
|
-
|
|
19
|
-
Text / textarea input with floating label.
|
|
20
|
-
|
|
21
|
-
| Prop | Type | Default | Description |
|
|
22
|
-
|------|------|---------|-------------|
|
|
23
|
-
| `modelValue` | `string` | — | v-model binding |
|
|
24
|
-
| `label` | `string` | — | Floating label text |
|
|
25
|
-
| `stackLabel` | `boolean` | `false` | Keep label stacked (never floats) |
|
|
26
|
-
| `placeholder` | `string` | — | Input placeholder |
|
|
27
|
-
| `design` | `'flat' \| 'filled' \| 'round'` | `'flat'` | Visual design variant |
|
|
28
|
-
| `type` | `'text' \| 'textarea'` | `'text'` | Input type |
|
|
29
|
-
| `readonly` | `boolean` | `false` | Read-only state |
|
|
30
|
-
| `disabled` | `boolean` | `false` | Disabled state |
|
|
31
|
-
| `required` | `boolean` | `false` | Required field |
|
|
32
|
-
| `maxlength` | `number` | — | Character limit |
|
|
33
|
-
| `rows` | `number` | — | Textarea rows |
|
|
34
|
-
| `autoGrow` | `boolean` | `false` | Auto-grow textarea |
|
|
35
|
-
| `loading` | `boolean` | `false` | Show loading state |
|
|
36
|
-
| `error` | `string \| boolean` | — | Error state / message |
|
|
37
|
-
| `hint` | `string` | — | Hint text below input |
|
|
38
|
-
| `iconBefore` | `string` | — | Icon before input (outside border) |
|
|
39
|
-
| `iconAfter` | `string` | — | Icon after input (outside border) |
|
|
40
|
-
| `iconPrepend` | `string` | — | Icon prepended inside border |
|
|
41
|
-
| `iconAppend` | `string` | — | Icon appended inside border |
|
|
42
|
-
|
|
43
|
-
**Emits**: `update:modelValue`, `blur`, `focus`, `click`, `beforeClick`, `afterClick`, `prependClick`, `appendClick`
|
|
44
|
-
|
|
45
|
-
**Slots**: `default`, `before`, `after`, `prepend`, `append`, `input`, `overlay`, `error`, `hint`, `counter`, `label`
|
|
46
|
-
|
|
47
|
-
**CSS classes**: `i8` (wrapper), `i8-input`, `i8-textarea`, `i8-label`, `i8-hint`, `i8-counter`, `i8-prepend`, `i8-append`, `i8-before`, `i8-after`, `i8-underline`
|
|
48
|
-
|
|
49
|
-
**Data attrs**: `data-has-value`, `data-active`, `data-error`, `data-has-label`, `data-has-prepend`, `data-has-append`
|
|
50
|
-
|
|
51
|
-
```html
|
|
52
|
-
<!-- Basic text input -->
|
|
53
|
-
<VuInput v-model="name" label="Full Name" />
|
|
54
|
-
|
|
55
|
-
<!-- With design variant -->
|
|
56
|
-
<VuInput v-model="email" label="Email" design="filled" />
|
|
57
|
-
|
|
58
|
-
<!-- With validation error -->
|
|
59
|
-
<VuInput v-model="password" label="Password" type="password" :error="passwordError" />
|
|
60
|
-
|
|
61
|
-
<!-- Textarea with auto-grow -->
|
|
62
|
-
<VuInput v-model="bio" label="Bio" type="textarea" auto-grow :rows="3" />
|
|
63
|
-
|
|
64
|
-
<!-- With icons -->
|
|
65
|
-
<VuInput v-model="search" label="Search" icon-prepend="i-mdi-magnify" icon-append="i-mdi-close" />
|
|
66
|
-
|
|
67
|
-
<!-- With character counter -->
|
|
68
|
-
<VuInput v-model="tweet" label="Tweet" :maxlength="280" />
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### `<VuSelect>`
|
|
72
|
-
|
|
73
|
-
Dropdown select built on Reka UI SelectRoot.
|
|
74
|
-
|
|
75
|
-
| Prop | Type | Default | Description |
|
|
76
|
-
|------|------|---------|-------------|
|
|
77
|
-
| `modelValue` | `string` | — | v-model binding |
|
|
78
|
-
| `items` | `Array<string \| T> \| Record<string, Array>` | — | Options (array or grouped record) |
|
|
79
|
-
| `defaultValue` | `string` | — | Initial value |
|
|
80
|
-
| `disabled` | `boolean` | `false` | Disabled state |
|
|
81
|
-
| `disabledValues` | `string[]` | — | Specific disabled options |
|
|
82
|
-
| `required` | `boolean` | `false` | Required field |
|
|
83
|
-
| `placeholder` | `string` | — | Placeholder text |
|
|
84
|
-
| `popupClass` | `string` | — | CSS class for popup |
|
|
85
|
-
| `popupRound` | `boolean` | — | Round popup corners |
|
|
86
|
-
| `popupPosition` | `'item-aligned' \| 'popper'` | — | Popup positioning mode |
|
|
87
|
-
|
|
88
|
-
**Slots**: `default` (custom item rendering), `selected-items`
|
|
89
|
-
**CSS classes**: `select-content` (popup), `select-item`, `select-grp-label`, `select-separator`
|
|
90
|
-
**Data attrs on items**: `data-highlighted`, `data-disabled`, `data-state="checked"`
|
|
91
|
-
|
|
92
|
-
```html
|
|
93
|
-
<!-- Simple string items -->
|
|
94
|
-
<VuInput label="Fruit" design="filled">
|
|
95
|
-
<VuSelect v-model="fruit" :items="['Apple', 'Banana', 'Cherry']" />
|
|
96
|
-
</VuInput>
|
|
97
|
-
|
|
98
|
-
<!-- Object items -->
|
|
99
|
-
<VuInput label="Country">
|
|
100
|
-
<VuSelect
|
|
101
|
-
v-model="country"
|
|
102
|
-
:items="[
|
|
103
|
-
{ value: 'us', label: 'United States' },
|
|
104
|
-
{ value: 'ca', label: 'Canada' },
|
|
105
|
-
{ value: 'mx', label: 'Mexico' },
|
|
106
|
-
]"
|
|
107
|
-
/>
|
|
108
|
-
</VuInput>
|
|
109
|
-
|
|
110
|
-
<!-- Grouped items -->
|
|
111
|
-
<VuInput label="City">
|
|
112
|
-
<VuSelect
|
|
113
|
-
v-model="city"
|
|
114
|
-
:items="{
|
|
115
|
-
'North America': ['New York', 'Toronto'],
|
|
116
|
-
'Europe': ['London', 'Paris'],
|
|
117
|
-
}"
|
|
118
|
-
/>
|
|
119
|
-
</VuInput>
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
**Important**: `VuSelect` is typically placed inside a `VuInput` wrapper to get the label, design variant, and error handling.
|
|
123
|
-
|
|
124
|
-
### `<VuCombobox>`
|
|
125
|
-
|
|
126
|
-
Searchable dropdown with filtering. Inherits input props for label, design, etc.
|
|
127
|
-
|
|
128
|
-
| Prop | Type | Default | Description |
|
|
129
|
-
|------|------|---------|-------------|
|
|
130
|
-
| `modelValue` | `string \| string[]` | — | v-model binding |
|
|
131
|
-
| `items` | `Array<T \| string> \| Record<string, Array>` | — | Options |
|
|
132
|
-
| `searchTerm` | `string` | — | v-model for search text |
|
|
133
|
-
| `multiple` | `boolean` | `false` | Multi-select mode |
|
|
134
|
-
| `checkboxItems` | `boolean` | `false` | Show checkboxes in multi-select |
|
|
135
|
-
| `modelOpen` | `boolean` | — | v-model for open state |
|
|
136
|
-
| `dropdownIcon` | `string` | `'i--chevron-down'` | Dropdown toggle icon |
|
|
137
|
-
| `resetSearchTermOnBlur` | `boolean` | — | Clear search on blur |
|
|
138
|
-
| `popupClass` | `string` | — | CSS class for popup |
|
|
139
|
-
| `popupRound` | `boolean` | — | Round popup corners |
|
|
140
|
-
| `align` | `string` | — | Popup alignment |
|
|
141
|
-
|
|
142
|
-
Also accepts all `VuInput` props: `label`, `design`, `disabled`, `error`, `hint`, etc.
|
|
143
|
-
|
|
144
|
-
**Slots**: `empty`, `group-label`, `item` (with `{ selected }` bind), `prepend`, `append`
|
|
145
|
-
|
|
146
|
-
```html
|
|
147
|
-
<!-- Basic searchable select -->
|
|
148
|
-
<VuCombobox v-model="fruit" label="Fruit" :items="fruits" />
|
|
149
|
-
|
|
150
|
-
<!-- Multi-select with checkboxes -->
|
|
151
|
-
<VuCombobox
|
|
152
|
-
v-model="selectedFruits"
|
|
153
|
-
label="Fruits"
|
|
154
|
-
:items="fruits"
|
|
155
|
-
multiple
|
|
156
|
-
checkbox-items
|
|
157
|
-
/>
|
|
158
|
-
|
|
159
|
-
<!-- Custom item rendering -->
|
|
160
|
-
<VuCombobox v-model="user" label="User" :items="users">
|
|
161
|
-
<template #item="{ item, selected }">
|
|
162
|
-
<div class="flex items-center gap-$xs">
|
|
163
|
-
<img :src="item.avatar" class="size-6 rounded-full" />
|
|
164
|
-
<span>{{ item.label }}</span>
|
|
165
|
-
</div>
|
|
166
|
-
</template>
|
|
167
|
-
</VuCombobox>
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### `<VuCheckbox>`
|
|
171
|
-
|
|
172
|
-
Checkbox with indeterminate state support.
|
|
173
|
-
|
|
174
|
-
| Prop | Type | Default | Description |
|
|
175
|
-
|------|------|---------|-------------|
|
|
176
|
-
| `modelValue` | `boolean \| undefined \| 'indeterminate'` | — | v-model binding |
|
|
177
|
-
| `label` | `string` | — | Checkbox label |
|
|
178
|
-
| `disabled` | `boolean` | `false` | Disabled state |
|
|
179
|
-
| `readonly` | `boolean` | `false` | Read-only state |
|
|
180
|
-
| `required` | `boolean` | `false` | Required field |
|
|
181
|
-
| `error` | `string \| boolean` | — | Error state / message |
|
|
182
|
-
| `verticalMiddle` | `boolean` | `false` | Vertically center checkbox with label |
|
|
183
|
-
| `reverse` | `boolean` | `false` | Label before checkbox |
|
|
184
|
-
|
|
185
|
-
**Slots**: `default`, `label`
|
|
186
|
-
**CSS classes**: `checkbox-root`, `checkbox`, `checkbox-indicator`, `checkbox-icon`, `checkbox-label`
|
|
187
|
-
**Data attrs**: `data-state="checked" \| "unchecked" \| "indeterminate"`, `data-error`, `aria-disabled`
|
|
188
|
-
|
|
189
|
-
```html
|
|
190
|
-
<VuCheckbox v-model="agreed" label="I agree to the terms" />
|
|
191
|
-
<VuCheckbox v-model="selectAll" label="Select all" />
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### `<VuRadioGroup>`
|
|
195
|
-
|
|
196
|
-
Radio button group.
|
|
197
|
-
|
|
198
|
-
| Prop | Type | Default | Description |
|
|
199
|
-
|------|------|---------|-------------|
|
|
200
|
-
| `modelValue` | `string` | — | v-model binding |
|
|
201
|
-
| `items` | `Array<string \| { value, label?, disabled? }>` | — | Radio options |
|
|
202
|
-
| `defaultValue` | `string` | — | Initial value |
|
|
203
|
-
| `label` | `string` | — | Group label |
|
|
204
|
-
| `labelVisible` | `boolean` | `true` | Show group label |
|
|
205
|
-
| `row` | `boolean` | `false` | Horizontal layout |
|
|
206
|
-
| `disabled` | `boolean` | `false` | Disable all items |
|
|
207
|
-
| `disabledValues` | `string[]` | — | Specific disabled items |
|
|
208
|
-
| `error` | `string \| boolean` | — | Error state / message |
|
|
209
|
-
| `verticalMiddle` | `boolean` | `false` | Vertically center radio with label |
|
|
210
|
-
| `reverse` | `boolean` | `false` | Label before radio |
|
|
211
|
-
|
|
212
|
-
**Slots**: `default` (custom item rendering with item bind)
|
|
213
|
-
**CSS classes**: `rb-container`, `rb-label`, `rb-root`, `rb-row`, `rb-item-wrapper`, `rb-item`, `rb-item-indicator`, `rb-item-label`
|
|
214
|
-
**Data attrs**: `data-state`, `data-disabled`
|
|
215
|
-
|
|
216
|
-
```html
|
|
217
|
-
<!-- String items -->
|
|
218
|
-
<VuRadioGroup v-model="size" label="Size" :items="['Small', 'Medium', 'Large']" />
|
|
219
|
-
|
|
220
|
-
<!-- Object items with disabled -->
|
|
221
|
-
<VuRadioGroup
|
|
222
|
-
v-model="plan"
|
|
223
|
-
label="Plan"
|
|
224
|
-
:items="[
|
|
225
|
-
{ value: 'free', label: 'Free' },
|
|
226
|
-
{ value: 'pro', label: 'Pro' },
|
|
227
|
-
{ value: 'enterprise', label: 'Enterprise', disabled: true },
|
|
228
|
-
]"
|
|
229
|
-
/>
|
|
230
|
-
|
|
231
|
-
<!-- Horizontal layout -->
|
|
232
|
-
<VuRadioGroup v-model="align" :items="['Left', 'Center', 'Right']" row />
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
### `<VuSlider>`
|
|
236
|
-
|
|
237
|
-
Range slider.
|
|
238
|
-
|
|
239
|
-
| Prop | Type | Default | Description |
|
|
240
|
-
|------|------|---------|-------------|
|
|
241
|
-
| `modelValue` | `number[]` | — | v-model binding (array for range) |
|
|
242
|
-
| `min` | `number` | `0` | Minimum value |
|
|
243
|
-
| `max` | `number` | `100` | Maximum value |
|
|
244
|
-
| `step` | `number` | `1` | Step increment |
|
|
245
|
-
| `disabled` | `boolean` | `false` | Disabled state |
|
|
246
|
-
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Slider direction |
|
|
247
|
-
| `thumbs` | `number` | `1` | Number of thumbs |
|
|
248
|
-
| `labels` | `string[]` | — | Labels for thumbs |
|
|
249
|
-
| `label` | `string` | — | Overall label |
|
|
250
|
-
| `displayValue` | `boolean` | `false` | Show current value |
|
|
251
|
-
| `hideRange` | `boolean` | `false` | Hide the colored range track |
|
|
252
|
-
|
|
253
|
-
**Slots**: `default` (custom thumb rendering)
|
|
254
|
-
**CSS classes**: `slider`, `slider-track`, `slider-range`, `slider-thumb`
|
|
255
|
-
|
|
256
|
-
```html
|
|
257
|
-
<!-- Basic slider -->
|
|
258
|
-
<VuSlider v-model="volume" :min="0" :max="100" label="Volume" />
|
|
259
|
-
|
|
260
|
-
<!-- Range slider (two thumbs) -->
|
|
261
|
-
<VuSlider v-model="priceRange" :min="0" :max="1000" :thumbs="2" label="Price Range" />
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
### `<VuDatePicker>`
|
|
265
|
-
|
|
266
|
-
Date picker with calendar popup.
|
|
267
|
-
|
|
268
|
-
Composed from: `DatePickerBase`, `DatePickerInner`, `DatePickerPopup`.
|
|
269
|
-
|
|
270
|
-
```html
|
|
271
|
-
<VuDatePicker v-model="date" label="Start Date" />
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
Uses `@internationalized/date` for date handling.
|
|
275
|
-
|
|
276
|
-
## Common patterns
|
|
277
|
-
|
|
278
|
-
### Pattern: Form with validation
|
|
279
|
-
|
|
280
|
-
```html
|
|
281
|
-
<form @submit.prevent="submit" class="flex flex-col gap-$m">
|
|
282
|
-
<VuInput v-model="name" label="Name" required :error="errors.name" />
|
|
283
|
-
<VuInput v-model="email" label="Email" required :error="errors.email" />
|
|
284
|
-
|
|
285
|
-
<VuInput label="Role">
|
|
286
|
-
<VuSelect v-model="role" :items="roles" />
|
|
287
|
-
</VuInput>
|
|
288
|
-
|
|
289
|
-
<VuCheckbox v-model="terms" label="I agree to the terms" :error="errors.terms" />
|
|
290
|
-
|
|
291
|
-
<VuButton label="Submit" class="scope-primary c8-filled" type="submit" />
|
|
292
|
-
</form>
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
### Pattern: Select inside Input wrapper
|
|
296
|
-
|
|
297
|
-
`VuSelect` should be wrapped in `VuInput` for labels and design consistency:
|
|
298
|
-
|
|
299
|
-
```html
|
|
300
|
-
<VuInput label="Country" design="filled" :error="countryError">
|
|
301
|
-
<VuSelect v-model="country" :items="countries" />
|
|
302
|
-
</VuInput>
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
### Pattern: Input with prefix/suffix slots
|
|
306
|
-
|
|
307
|
-
```html
|
|
308
|
-
<VuInput v-model="price" label="Price" design="filled">
|
|
309
|
-
<template #prepend>$</template>
|
|
310
|
-
<template #append>.00</template>
|
|
311
|
-
</VuInput>
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
### Pattern: Segmented input group
|
|
315
|
-
|
|
316
|
-
Use the `segmented` class to visually join adjacent inputs:
|
|
317
|
-
|
|
318
|
-
```html
|
|
319
|
-
<div class="segmented">
|
|
320
|
-
<VuInput v-model="firstName" label="First Name" />
|
|
321
|
-
<VuInput v-model="lastName" label="Last Name" />
|
|
322
|
-
</div>
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
## Integration
|
|
326
|
-
|
|
327
|
-
- Form components use `i8-*` shortcuts for styling. Override through `vunorShortcuts()` (see [shortcuts.md](shortcuts.md)).
|
|
328
|
-
- Error states set `data-error` attribute and apply error color scoping automatically.
|
|
329
|
-
- The `design` prop maps to `i8-flat`, `i8-filled`, or `i8-round` CSS classes.
|
|
330
|
-
- Combobox combines Input and Select functionality — it renders an `i8` wrapper with a dropdown popup.
|
|
331
|
-
|
|
332
|
-
## Best practices
|
|
333
|
-
|
|
334
|
-
- Wrap `VuSelect` inside `VuInput` for consistent label/error handling.
|
|
335
|
-
- Use `design` prop consistently across a form — mixing designs looks inconsistent.
|
|
336
|
-
- Pass error messages as strings (not just `true`) to display the error text below the input.
|
|
337
|
-
- Use `autoGrow` on textarea inputs for better UX — it expands as the user types.
|
|
338
|
-
- For multi-select Combobox, enable `checkboxItems` to make selection state obvious.
|
|
339
|
-
|
|
340
|
-
## Gotchas
|
|
341
|
-
|
|
342
|
-
- `VuCheckbox` uses `data-state="checked"` / `"unchecked"` / `"indeterminate"` — not the `:checked` CSS pseudo-class.
|
|
343
|
-
- `VuSelect` popup portals to `<body>`. Scope CSS selectors to `.select-content` to target it.
|
|
344
|
-
- Reka UI renders hidden native `<select>/<option>` for form submission alongside the custom UI. `[role="option"]` matches both — use `div[role="option"]` within `.select-content` for the visible options.
|
|
345
|
-
- `disabled` renders as `aria-disabled="true"`, not the HTML `disabled` attribute. Use `[aria-disabled="true"]` in CSS selectors.
|
|
346
|
-
- `required` renders as `aria-required="true"`, not the HTML `required` attribute.
|
|
347
|
-
- Input `type` prop accepts `'text'` or `'textarea'` — for HTML input types like `'password'` or `'email'`, pass them through the native attrs.
|
|
348
|
-
- The `modelValue` for `VuSlider` is always an array, even for a single thumb.
|
package/skills/vunor/palette.md
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
# Color palette — vunor
|
|
2
|
-
|
|
3
|
-
> Oklab perceptual color system, seed colors, layers, surfaces, scopes, and the current-color system.
|
|
4
|
-
|
|
5
|
-
## Concepts
|
|
6
|
-
|
|
7
|
-
Vunor generates its color system using `@prostojs/palitra`, built on the Oklab perceptual color model. Unlike RGB/HSL, Oklab grades colors by how people actually see them — `primary-500` and `error-500` sit at the same perceptual luminance regardless of hue.
|
|
8
|
-
|
|
9
|
-
For each seed color, palitra generates:
|
|
10
|
-
1. **Main palette** (10 steps: 50, 100, 200, ..., 900) — luminance-graded with perceptually uniform steps
|
|
11
|
-
2. **Layer palette** (5 steps: 0–4) — desaturated variants for backgrounds
|
|
12
|
-
|
|
13
|
-
All values are emitted as CSS custom properties.
|
|
14
|
-
|
|
15
|
-
### Color architecture
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
seed color → palitra (Oklab) → main palette (50–900)
|
|
19
|
-
→ light layers (0–4)
|
|
20
|
-
→ dark layers (0–4)
|
|
21
|
-
↓
|
|
22
|
-
CSS custom properties
|
|
23
|
-
↓
|
|
24
|
-
scope-{color} → --scope-color-{step}
|
|
25
|
-
current-bg-* → --current-bg
|
|
26
|
-
bg-current → applies --current-bg
|
|
27
|
-
layer-{0-4} → full bg+text+icon
|
|
28
|
-
surface-{step} → full bg+text+icon
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Configuration
|
|
32
|
-
|
|
33
|
-
### Seed colors
|
|
34
|
-
|
|
35
|
-
Pass to `presetVunor({ palette: { colors: { ... } } })`:
|
|
36
|
-
|
|
37
|
-
| Name | Default | Purpose |
|
|
38
|
-
|------|---------|---------|
|
|
39
|
-
| `primary` | `#004eaf` | Main brand color |
|
|
40
|
-
| `secondary` | `#edd812` | Accent / highlight |
|
|
41
|
-
| `good` | `#7bc76a` | Success / positive |
|
|
42
|
-
| `warn` | `#ef9421` | Warning |
|
|
43
|
-
| `error` | `#bf5a5f` | Error / negative |
|
|
44
|
-
| `grey` | `#858892` | Neutral greys |
|
|
45
|
-
| `neutral` | `#5da0c5` | Alternative neutral |
|
|
46
|
-
|
|
47
|
-
Each color can be a simple hex string or an object with fine-tuning:
|
|
48
|
-
|
|
49
|
-
```ts
|
|
50
|
-
palette: {
|
|
51
|
-
colors: {
|
|
52
|
-
primary: '#6B4EFF', // simple swap
|
|
53
|
-
error: {
|
|
54
|
-
color: '#DC2626',
|
|
55
|
-
vivid: { dark: 0.3, light: 0.3 }, // hue rotation at palette edges
|
|
56
|
-
saturate: { dark: -0.1, light: -0.1 }, // desaturate slightly
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Advanced palette controls
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
palette: {
|
|
66
|
-
lightest: 0.97, // max luminance for lightest step (0–1)
|
|
67
|
-
darkest: 0.24, // min luminance for darkest step (0–1)
|
|
68
|
-
layersDepth: 0.08, // brightness step between layer-0 and layer-4
|
|
69
|
-
|
|
70
|
-
mainPalette: {
|
|
71
|
-
luminance: { dark: 0.26, middle: 0.62, light: 0.97 },
|
|
72
|
-
saturate: { dark: -0.25, light: -0.25 },
|
|
73
|
-
vivid: { dark: 0.1, light: 0.2 },
|
|
74
|
-
},
|
|
75
|
-
layerPalette: {
|
|
76
|
-
desaturate: 0.2,
|
|
77
|
-
luminance: { dark: 0.24, light: 0.32 },
|
|
78
|
-
},
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## API Reference
|
|
83
|
-
|
|
84
|
-
### Color scope classes
|
|
85
|
-
|
|
86
|
-
#### `scope-{color}`
|
|
87
|
-
|
|
88
|
-
Sets `--scope-color-*` CSS custom properties for an entire subtree.
|
|
89
|
-
|
|
90
|
-
```html
|
|
91
|
-
<div class="scope-primary">
|
|
92
|
-
<!-- All children can use scope-aware utilities -->
|
|
93
|
-
<button class="c8-filled">Uses primary palette</button>
|
|
94
|
-
</div>
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
Valid color names: `primary`, `secondary`, `good`, `warn`, `error`, `grey`, `neutral`.
|
|
98
|
-
|
|
99
|
-
#### `current-{target}-{color}-{step}`
|
|
100
|
-
|
|
101
|
-
Sets a specific `--current-{target}` CSS variable to a specific palette step.
|
|
102
|
-
|
|
103
|
-
```html
|
|
104
|
-
<div class="current-bg-primary-500 current-text-primary-50">
|
|
105
|
-
<div class="bg-current text-current">Colored via CSS vars</div>
|
|
106
|
-
</div>
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Targets: `text`, `bg`, `icon`, `border`, `outline`, `caret`, `hl` (highlight).
|
|
110
|
-
|
|
111
|
-
#### `{target}-current` / `{target}-current/{opacity}`
|
|
112
|
-
|
|
113
|
-
Applies the current-scoped color.
|
|
114
|
-
|
|
115
|
-
```html
|
|
116
|
-
<div class="current-bg-error-500">
|
|
117
|
-
<div class="bg-current">Full opacity error bg</div>
|
|
118
|
-
<div class="bg-current/50">50% opacity error bg</div>
|
|
119
|
-
</div>
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### Layers
|
|
123
|
-
|
|
124
|
-
#### `layer-{0-4}`
|
|
125
|
-
|
|
126
|
-
Full background + text + icon styling that automatically adapts to light/dark mode.
|
|
127
|
-
|
|
128
|
-
```html
|
|
129
|
-
<div class="layer-0 scope-primary">
|
|
130
|
-
<!-- Light: lightest bg, dark text. Dark mode: darkest bg, light text -->
|
|
131
|
-
<div class="layer-1">
|
|
132
|
-
<!-- One step deeper -->
|
|
133
|
-
<div class="layer-2">
|
|
134
|
-
<!-- Even deeper -->
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
- `layer-0` is the outermost (lightest in light mode, darkest in dark)
|
|
141
|
-
- Each layer steps progressively toward the opposite extreme
|
|
142
|
-
- Apply `scope-{color}` to tint the layers with a palette
|
|
143
|
-
|
|
144
|
-
### Surfaces
|
|
145
|
-
|
|
146
|
-
#### `surface-{step}`
|
|
147
|
-
|
|
148
|
-
Applies a specific palette stop as background + text + icon.
|
|
149
|
-
|
|
150
|
-
```html
|
|
151
|
-
<div class="scope-primary surface-100">Lightly tinted</div>
|
|
152
|
-
<div class="scope-error surface-500">Bold error banner</div>
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
- `surface-0` through `surface-4` map to the layer scale
|
|
156
|
-
- `surface-50` through `surface-900` map to the main 10-step palette
|
|
157
|
-
- Custom surfaces can be defined in `palette.surfaces`
|
|
158
|
-
|
|
159
|
-
## Common patterns
|
|
160
|
-
|
|
161
|
-
### Pattern: Scoped button colors
|
|
162
|
-
|
|
163
|
-
Use `scope-{color}` to set the palette, then a `c8-*` class for the visual style.
|
|
164
|
-
|
|
165
|
-
```html
|
|
166
|
-
<button class="scope-primary c8-filled">Primary</button>
|
|
167
|
-
<button class="scope-error c8-outlined">Error</button>
|
|
168
|
-
<button class="scope-good c8-light">Success</button>
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### Pattern: Themed card sections
|
|
172
|
-
|
|
173
|
-
Nest layers to create visual depth.
|
|
174
|
-
|
|
175
|
-
```html
|
|
176
|
-
<div class="layer-0 scope-primary">
|
|
177
|
-
<VuCard>
|
|
178
|
-
<div class="layer-1">
|
|
179
|
-
<p>Slightly deeper background</p>
|
|
180
|
-
<div class="layer-2">
|
|
181
|
-
<p>Even deeper</p>
|
|
182
|
-
</div>
|
|
183
|
-
</div>
|
|
184
|
-
</VuCard>
|
|
185
|
-
</div>
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### Pattern: Custom current-color usage
|
|
189
|
-
|
|
190
|
-
Set colors independently for different targets.
|
|
191
|
-
|
|
192
|
-
```html
|
|
193
|
-
<div class="current-bg-primary-100 current-text-primary-900 current-border-primary-300">
|
|
194
|
-
<div class="bg-current text-current border-current border-1">
|
|
195
|
-
Custom color combination
|
|
196
|
-
</div>
|
|
197
|
-
</div>
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### Pattern: Dark mode awareness
|
|
201
|
-
|
|
202
|
-
Layers handle dark mode automatically. No need for explicit `dark:` prefixes on layer/surface classes.
|
|
203
|
-
|
|
204
|
-
```html
|
|
205
|
-
<!-- This automatically adapts to dark mode -->
|
|
206
|
-
<div class="layer-0 scope-primary">
|
|
207
|
-
<div class="layer-1">Content</div>
|
|
208
|
-
</div>
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
## Best practices
|
|
212
|
-
|
|
213
|
-
- Use `scope-{color}` at the highest reasonable ancestor — children inherit the scope.
|
|
214
|
-
- Prefer `layer-*` for page-level backgrounds and `surface-*` for specific component accents.
|
|
215
|
-
- Don't hard-code palette step numbers unless you need a specific color — let layers and surfaces handle light/dark adaptation.
|
|
216
|
-
- When customizing the palette, test both light and dark modes — Oklab ensures perceptual consistency, but extreme `vivid`/`saturate` overrides can break the balance.
|
|
217
|
-
|
|
218
|
-
## Gotchas
|
|
219
|
-
|
|
220
|
-
- `scope-{color}` only sets CSS variables — it doesn't apply any visible styles by itself. You need `layer-*`, `surface-*`, `bg-current`, etc. to consume the scope.
|
|
221
|
-
- Layer numbers don't correspond to z-index — they're visual depth levels only.
|
|
222
|
-
- Swapping the primary color changes every component that uses `scope-primary` — this is by design but can be surprising if components mix scopes unexpectedly.
|
|
223
|
-
- `current-bg-scope-color-500` (note: `scope-color`, not a specific palette name) references whatever the current scope is. This is used internally by `c8-filled` and similar shortcut patterns.
|