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.
Files changed (62) hide show
  1. package/README.md +6 -11
  2. package/dist/AppLayout.mjs +0 -0
  3. package/dist/AppToasts.mjs +4 -8
  4. package/dist/Button.mjs +5 -10
  5. package/dist/ButtonBase.mjs +4 -8
  6. package/dist/Calendar.mjs +10 -14
  7. package/dist/Card.mjs +4 -9
  8. package/dist/CardHeader.mjs +5 -10
  9. package/dist/CardInner.mjs +4 -9
  10. package/dist/Checkbox.mjs +4 -8
  11. package/dist/Combobox.d.mts +9 -9
  12. package/dist/Combobox.mjs +5 -9
  13. package/dist/DatePicker.d.mts +9 -9
  14. package/dist/DatePicker.mjs +12 -17
  15. package/dist/DatePickerBase.d.mts +9 -9
  16. package/dist/DatePickerBase.mjs +13 -17
  17. package/dist/DatePickerInner.mjs +5 -9
  18. package/dist/DatePickerPopup.mjs +4 -8
  19. package/dist/DelayedSwitch.mjs +4 -9
  20. package/dist/DevTools.mjs +583 -150
  21. package/dist/Dialog.d.mts +6 -6
  22. package/dist/Dialog.mjs +4 -8
  23. package/dist/Icon.mjs +4 -9
  24. package/dist/InnerLoading.mjs +4 -8
  25. package/dist/Input.d.mts +13 -13
  26. package/dist/Input.mjs +4 -8
  27. package/dist/InputBase.d.mts +9 -9
  28. package/dist/InputBase.mjs +4 -8
  29. package/dist/Label.mjs +1 -4
  30. package/dist/LoadingIndicator.mjs +4 -8
  31. package/dist/Menu.d.mts +2 -2
  32. package/dist/Menu.mjs +5 -14
  33. package/dist/MenuItem.mjs +4 -9
  34. package/dist/OverflowContainer.mjs +4 -8
  35. package/dist/Pagination.mjs +4 -9
  36. package/dist/Popover.mjs +4 -9
  37. package/dist/ProgressBar.mjs +4 -9
  38. package/dist/RadioGroup.mjs +4 -8
  39. package/dist/Select.d.mts +9 -9
  40. package/dist/Select.mjs +4 -9
  41. package/dist/SelectBase.mjs +4 -8
  42. package/dist/Slider.mjs +4 -8
  43. package/dist/Tabs.mjs +4 -9
  44. package/dist/nuxt.mjs +1 -3
  45. package/dist/theme.d.mts +35 -0
  46. package/dist/theme.mjs +313 -336
  47. package/dist/utils-6bTTIoaw.js +40 -0
  48. package/dist/utils.d.mts +2 -2
  49. package/dist/utils.mjs +1 -4
  50. package/dist/vite.mjs +1 -2
  51. package/dist/vunor.d.mts +13 -13
  52. package/dist/vunor.mjs +1 -6
  53. package/package.json +30 -37
  54. package/scripts/setup-skills.js +0 -78
  55. package/skills/vunor/SKILL.md +0 -115
  56. package/skills/vunor/components.md +0 -320
  57. package/skills/vunor/core.md +0 -173
  58. package/skills/vunor/forms.md +0 -348
  59. package/skills/vunor/palette.md +0 -223
  60. package/skills/vunor/rules.md +0 -263
  61. package/skills/vunor/shortcuts.md +0 -239
  62. package/skills/vunor/typography.md +0 -204
@@ -1,263 +0,0 @@
1
- # UnoCSS custom rules — vunor
2
-
3
- > All 15 custom UnoCSS rules provided by `presetVunor`: palette/scope/current-color rules, spacing/typography/card/fingertip rules, and i8 input styling rules.
4
-
5
- ## Concepts
6
-
7
- Vunor's `presetVunor()` registers custom UnoCSS rules that power the color scope system, typography-aware spacing, card sizing, touch targets, and input styling. These rules generate CSS custom properties that compose together — e.g., `scope-primary` sets palette variables, `current-bg-scope-color-500` reads them, and `bg-current` applies the result.
8
-
9
- Rules are organized in three groups:
10
- 1. **Palette rules** (8) — color scoping, current-color system, icon utilities
11
- 2. **Spacing rules** (4) — text margins, card spacing, fingertip sizing
12
- 3. **Input rules** (3) — i8 border/bg/outline configuration
13
-
14
- ## Palette rules
15
-
16
- ### `scope-{color}`
17
-
18
- Sets `--scope-color-*` CSS custom properties for the entire subtree.
19
-
20
- **Pattern:** `/^scope-(.*)$/`
21
-
22
- Generates variables for all 10 main shades (50–900), 5 light layers (0–4), 5 dark layers (0–4), and sets `--current-hl` to the 500 shade. Valid colors: `primary`, `secondary`, `good`, `warn`, `error`, `grey`, `neutral`.
23
-
24
- ```html
25
- <div class="scope-primary">
26
- <!-- --scope-color-50 through --scope-color-900 are now set -->
27
- <!-- --scope-light-0 through --scope-light-4 -->
28
- <!-- --scope-dark-0 through --scope-dark-4 -->
29
- <!-- --current-hl = primary-500 -->
30
- </div>
31
- ```
32
-
33
- ### `current-{target}-{source}`
34
-
35
- Sets a `--current-{target}` CSS variable to a specific color.
36
-
37
- **Pattern:** `/^current-(text|bg|icon|border|outline|caret|hl)-(.+)$/`
38
-
39
- | Source type | Example | Result |
40
- |-------------|---------|--------|
41
- | Scope reference | `current-bg-scope-color-500` | `--current-bg: var(--scope-color-500)` |
42
- | Highlight alias | `current-text-hl` | `--current-text: var(--current-hl)` |
43
- | Theme color | `current-bg-primary-500` | `--current-bg: <rgb values>` |
44
-
45
- ```html
46
- <div class="current-bg-primary-500 current-text-primary-50">
47
- <div class="bg-current text-current">Styled via CSS vars</div>
48
- </div>
49
- ```
50
-
51
- ### `{target}-current` / `{target}-current-{source}` / `{target}-current/{opacity}`
52
-
53
- Applies a `--current-*` color to a CSS property.
54
-
55
- **Pattern:** `/^(text|bg|icon|border|outline|caret|fill|shadow|ring)-current(-text|-bg|-icon|-border|-outline|-caret|-hl)?(\/\d{1,3})?$/`
56
-
57
- ```html
58
- <div class="text-current"> <!-- color: rgb(var(--current-text) / 1) -->
59
- <div class="bg-current/50"> <!-- background-color: rgb(var(--current-bg) / 0.5) -->
60
- <div class="border-current-hl"> <!-- border-color uses --current-hl -->
61
- <div class="icon-current"> <!-- sets --un-icon-opacity: 1 -->
62
- <div class="shadow-current-border"> <!-- --un-shadow-color from --current-border -->
63
- ```
64
-
65
- Special behavior for `icon` target: sets `--un-icon-opacity` but applies color through the `icon-color` rule, not directly.
66
-
67
- ### `{target}-scope-{source}` / `{target}-scope-{source}/{opacity}`
68
-
69
- Applies a scoped palette color directly to a CSS property without going through `--current-*`.
70
-
71
- **Pattern:** `/^(bg|text|fill|stroke|border|outline|icon|caret)-scope-((?:color|dark|light|text|bg|white|black|icon)(?:-\d+)?)(\/\d{1,3})?$/`
72
-
73
- ```html
74
- <div class="bg-scope-color-100"> <!-- background: --scope-color-100 -->
75
- <div class="text-scope-dark-2"> <!-- color: --scope-dark-2 -->
76
- <div class="border-scope-light-1/50"> <!-- border: --scope-light-1 at 50% -->
77
- ```
78
-
79
- ### `icon-opacity-{0-100}`
80
-
81
- Sets `--un-icon-opacity`.
82
-
83
- **Pattern:** `/^icon-opacity-(\d{1,3})$/`
84
-
85
- ```html
86
- <div class="icon-opacity-50"> <!-- --un-icon-opacity: 0.5 -->
87
- ```
88
-
89
- ### `icon-color`
90
-
91
- Applies the current icon color with icon opacity.
92
-
93
- **Pattern:** exact match `icon-color`
94
-
95
- ```css
96
- .icon-color { color: rgb(var(--current-icon) / var(--un-icon-opacity, 1)); }
97
- ```
98
-
99
- ### `icon-size-{value}` / `icon-size`
100
-
101
- Sets or applies icon dimensions.
102
-
103
- **Pattern:** `/^icon-size-(.*)$/` and exact match `icon-size`
104
-
105
- | Class | Result |
106
- |-------|--------|
107
- | `icon-size-[2em]` | `--icon-size: 2em` |
108
- | `icon-size-4` | `--icon-size: 1rem` (from theme.spacing) |
109
- | `icon-size` | `width/height: var(--icon-size, 1em)` |
110
-
111
- ## Spacing rules
112
-
113
- ### `text-m{t|b|y}-{size}`
114
-
115
- Text margins with line-height compensation. Adjusts top/bottom margins using `--font-tc` (top correction) and `--font-bc` (bottom correction) so spacing between typographic elements is optically correct.
116
-
117
- **Pattern:** `/^text-m([bty])?-(.*)$/`
118
-
119
- | Suffix | Applies to |
120
- |--------|-----------|
121
- | `t` | top only |
122
- | `b` | bottom only |
123
- | `y` | top and bottom |
124
- | _(none)_ | all four sides |
125
-
126
- ```html
127
- <h1 class="text-h1 text-mb-$m">Title</h1>
128
- <!-- margin-bottom: calc(1em + var(--font-bc)) -->
129
-
130
- <p class="text-body text-mt-$s">Paragraph</p>
131
- <!-- margin-top: calc(0.62em + var(--font-tc)) -->
132
- ```
133
-
134
- Size values: spacing tokens (`$s`, `$m`, `$l`...), theme spacing keys, or numbers (multiplied by 0.25rem if unitless).
135
-
136
- ### `card-dense`
137
-
138
- Switches card spacing to the dense variant.
139
-
140
- **Pattern:** exact match `card-dense`
141
-
142
- ```css
143
- .card-dense { --card-spacing: var(--card-spacing-dense); }
144
- ```
145
-
146
- ### `card-{typography}`
147
-
148
- Sets `--card-spacing` proportional to a typography level's corrected font size.
149
-
150
- **Pattern:** `/^card-(.*)$/` (matches any valid `theme.fontSize` key)
151
-
152
- ```html
153
- <div class="card-h3">
154
- <!-- --card-spacing: <h3 corrected size * cardSpacingFactor.regular> -->
155
- <!-- --card-spacing-dense: <h3 corrected size * cardSpacingFactor.dense> -->
156
- <!-- --card-heading-size, --card-heading-bold, etc. also set -->
157
- <!-- padding: var(--card-spacing) -->
158
- </div>
159
- ```
160
-
161
- Valid keys: `h1`–`h6`, `subheading`, `body`, `body-l`, `body-s`, `callout`, `label`, `caption`, `overline`.
162
-
163
- ### `fingertip-{size}`
164
-
165
- Sets touch target height via `--v-fingertip` and `--v-fingertip-half`.
166
-
167
- **Pattern:** `/^fingertip-(.*)/`
168
-
169
- | Class | Source |
170
- |-------|--------|
171
- | `fingertip-xs` / `s` / `m` / `l` / `xl` | Named sizes from preset config |
172
- | `fingertip-4` | `theme.spacing['4']` |
173
- | `fingertip-[2.5rem]` | Bracket value used directly |
174
-
175
- ```css
176
- .fingertip-m { --v-fingertip: 3em; --v-fingertip-half: 1.5em; }
177
- ```
178
-
179
- ## Input (i8) rules
180
-
181
- ### `i8-{target}-{value}`
182
-
183
- Configures input border, outline, or background via CSS custom properties.
184
-
185
- **Pattern:** `/^i8-(border|outline|bg)-(.+)$/`
186
-
187
- | Value type | Example | CSS generated |
188
- |-----------|---------|---------------|
189
- | `none` | `i8-border-none` | `--i8-border-width: 0` |
190
- | `transparent` | `i8-bg-transparent` | `--i8-bg-color: 0` |
191
- | Scope ref | `i8-border-scope-color-500` | `--i8-border-color: var(--scope-color-500)` |
192
- | Theme color | `i8-border-primary` | `--i8-border-color: <rgb>` |
193
- | Width (px/em/rem) | `i8-border-2px` | `--i8-border-width: 2px` |
194
- | Bracket color | `i8-outline-[#ff0000]` | `--i8-outline-color: #ff0000` |
195
- | Bracket width | `i8-border-[0.5em]` | `--i8-border-width: 0.5em` |
196
-
197
- ### `i8-{target}-opacity-{0-100}`
198
-
199
- Sets opacity for input border, outline, or background.
200
-
201
- **Pattern:** `/^i8-(border|outline|bg)-opacity-(\d{1,3})$/`
202
-
203
- ```html
204
- <div class="i8-border-opacity-50"> <!-- --i8-border-opacity: 0.5 -->
205
- ```
206
-
207
- ### `i8-apply-{target}`
208
-
209
- Applies the configured i8 values to actual CSS properties. This is the final rendering step — used internally by `i8-flat`, `i8-filled`, `i8-round` shortcuts.
210
-
211
- **Pattern:** `/^i8-apply-(border|outline|bg)$/`
212
-
213
- | Class | CSS | Defaults |
214
- |-------|-----|----------|
215
- | `i8-apply-bg` | `background-color: rgb(var(--i8-bg-color, var(--current-bg)) / var(--i8-bg-opacity, 1))` | opacity: 1 |
216
- | `i8-apply-border` | `border-color: rgb(...)` + `border-width: var(--i8-border-width, 1px)` | opacity: 0.2, width: 1px |
217
- | `i8-apply-outline` | `outline-color: rgb(...)` + `outline-width: var(--i8-outline-width, 2px)` | opacity: 0.5, width: 2px |
218
-
219
- ## CSS custom properties reference
220
-
221
- Properties set by these rules:
222
-
223
- | Variable | Set by | Used by |
224
- |----------|--------|---------|
225
- | `--scope-color-{step}` | `scope-*` | `current-*-scope-*`, `*-scope-*` |
226
- | `--scope-light-{0-4}` | `scope-*` | `layer-*`, `*-scope-light-*` |
227
- | `--scope-dark-{0-4}` | `scope-*` | `layer-*`, `*-scope-dark-*` |
228
- | `--current-text` | `current-text-*` | `text-current` |
229
- | `--current-bg` | `current-bg-*` | `bg-current` |
230
- | `--current-icon` | `current-icon-*` | `icon-current`, `icon-color` |
231
- | `--current-border` | `current-border-*` | `border-current` |
232
- | `--current-hl` | `scope-*`, `current-hl-*` | `*-current-hl` |
233
- | `--font-tc` / `--font-bc` | `text-*` typography utilities | `text-mt-*`, `text-mb-*` |
234
- | `--card-spacing` | `card-*` | Card component padding |
235
- | `--card-spacing-dense` | `card-*` | `card-dense` |
236
- | `--v-fingertip` | `fingertip-*` | `h-fingertip`, button/input heights |
237
- | `--v-fingertip-half` | `fingertip-*` | `px-fingertip-half`, rounded buttons |
238
- | `--i8-border-color` | `i8-border-*` | `i8-apply-border` |
239
- | `--i8-border-width` | `i8-border-*` | `i8-apply-border` |
240
- | `--i8-border-opacity` | `i8-border-opacity-*` | `i8-apply-border` |
241
- | `--i8-bg-color` | `i8-bg-*` | `i8-apply-bg` |
242
- | `--i8-bg-opacity` | `i8-bg-opacity-*` | `i8-apply-bg` |
243
- | `--i8-outline-color` | `i8-outline-*` | `i8-apply-outline` |
244
- | `--i8-outline-width` | `i8-outline-*` | `i8-apply-outline` |
245
- | `--i8-outline-opacity` | `i8-outline-opacity-*` | `i8-apply-outline` |
246
- | `--un-icon-opacity` | `icon-opacity-*`, `icon-current` | `icon-color` |
247
- | `--icon-size` | `icon-size-*` | `icon-size` |
248
-
249
- ## Best practices
250
-
251
- - Use `scope-{color}` at the highest ancestor that shares a palette — avoids redundant variable declarations.
252
- - Prefer `text-mt-*` / `text-mb-*` over plain `mt-*` / `mb-*` between typographic elements — the font corrections produce optically correct spacing.
253
- - The `i8-*` rules are low-level — most of the time, use `i8-flat` / `i8-filled` / `i8-round` shortcuts which compose these rules internally.
254
- - `card-{typography}` sets `padding` directly — don't add extra padding classes on the same element.
255
-
256
- ## Gotchas
257
-
258
- - `scope-{color}` only sets CSS variables — it doesn't apply any visible styles. You need `layer-*`, `surface-*`, `bg-current`, or `*-scope-*` classes to consume the scope.
259
- - `icon-current` does **not** set `color` directly — it sets `--un-icon-opacity`. Use `icon-color` alongside it, or use the `icon-color icon-size` classes that components already include.
260
- - `i8-apply-border` defaults to `border-opacity: 0.2` and `width: 1px`. If you set `i8-border-primary` but see no visible border, check that `border-style` (e.g. `border-solid`) is also applied.
261
- - `text-m-$m` applies margins to **all four sides** (not just top/bottom). Use `text-my-$m` for vertical-only.
262
- - `fingertip-m` sets the CSS variable but doesn't apply height — use `h-fingertip` to consume it.
263
- - `card-dense` requires a `card-{typography}` rule on the same element (or ancestor) to have set `--card-spacing-dense`.
@@ -1,239 +0,0 @@
1
- # Shortcut objects — vunor
2
-
3
- > The structured shortcut system for defining and customizing component styles: defineShortcuts, mergeVunorShortcuts, c8/i8 style systems, and overriding built-in styles.
4
-
5
- ## Concepts
6
-
7
- Instead of flat UnoCSS shortcut strings, Vunor uses **structured nested objects** where keys are variant prefixes and values are the classes to apply. This makes styles readable, deeply mergeable, and surgically overridable.
8
-
9
- The key insight: when two shortcut objects define the same key, `mergeVunorShortcuts()` deep-merges them with later entries winning. You can override a single variant of a built-in style without rewriting the whole shortcut.
10
-
11
- ### How it works
12
-
13
- ```
14
- defineShortcuts({ ... }) → Shortcut object (nested)
15
-
16
- toUnoShortcut(obj) → Flat UnoCSS string
17
-
18
- mergeVunorShortcuts([...]) → Merged array (later wins)
19
-
20
- vunorShortcuts(overrides) → Final UnoCSS shortcuts config
21
- ```
22
-
23
- ## API Reference
24
-
25
- ### `defineShortcuts(obj)`
26
-
27
- Creates a typed shortcut object. Keys are shortcut names (CSS classes), values are variant-prefix objects.
28
-
29
- ```ts
30
- import { defineShortcuts } from 'vunor/theme'
31
-
32
- const shortcuts = defineShortcuts({
33
- 'btn': {
34
- '': 'h-fingertip flex items-center', // base classes (no prefix)
35
- 'hover:': 'bg-current/05', // → "hover:bg-current/05"
36
- 'dark:': 'text-white', // → "dark:text-white"
37
- '[&.btn-round]:': 'rounded-full', // → "[&.btn-round]:rounded-full"
38
- },
39
- })
40
- ```
41
-
42
- Each key is a **variant prefix** that gets prepended to every class in the value.
43
-
44
- ### Compound variants
45
-
46
- Nesting goes deeper for compound states:
47
-
48
- ```ts
49
- defineShortcuts({
50
- 'c8-filled': {
51
- '': 'current-bg-scope-color-500 text-white rounded-base',
52
- 'hover:': 'current-bg-scope-color-400',
53
- 'dark:': {
54
- '': 'text-white/90',
55
- 'hover:': 'current-bg-scope-color-600 border-solid',
56
- // produces: "dark:text-white/90 dark:hover:current-bg-scope-color-600 dark:hover:border-solid"
57
- },
58
- },
59
- })
60
- ```
61
-
62
- ### `toUnoShortcut(obj)`
63
-
64
- Flattens a shortcut object into a single UnoCSS string. Called internally but can be used directly for debugging.
65
-
66
- ```ts
67
- import { toUnoShortcut } from 'vunor/theme'
68
-
69
- const flat = toUnoShortcut({
70
- '': 'flex items-center',
71
- 'hover:': 'bg-blue-100',
72
- })
73
- // → "flex items-center hover:bg-blue-100"
74
- ```
75
-
76
- ### `mergeVunorShortcuts(arrays)`
77
-
78
- Deep-merges multiple shortcut arrays. Later arrays win on conflict.
79
-
80
- ```ts
81
- import { mergeVunorShortcuts } from 'vunor/theme'
82
-
83
- const merged = mergeVunorShortcuts([defaultShortcuts, myOverrides])
84
- ```
85
-
86
- ### `vunorShortcuts(overrides?, baseShortcuts?)`
87
-
88
- Returns the final merged shortcuts for UnoCSS config. This is what you put in `shortcuts: [...]`.
89
-
90
- ```ts
91
- import { vunorShortcuts } from 'vunor/theme'
92
-
93
- // Default styles
94
- shortcuts: [vunorShortcuts()]
95
-
96
- // With overrides (overrides take priority)
97
- shortcuts: [vunorShortcuts(myCustomShortcuts)]
98
- ```
99
-
100
- ## Built-in style systems
101
-
102
- ### c8 — Clickable styles
103
-
104
- Four visual variants for buttons and interactive elements. Use with `scope-{color}` to set the palette.
105
-
106
- | Class | Description |
107
- |-------|-------------|
108
- | `c8-filled` | Solid background, white text |
109
- | `c8-flat` | Transparent background, colored text, subtle hover |
110
- | `c8-outlined` | Border + colored text, transparent fill |
111
- | `c8-light` | Light tinted background, colored text |
112
-
113
- Each includes hover, active, focus, and disabled states.
114
-
115
- ```html
116
- <button class="scope-primary c8-filled">Save</button>
117
- <button class="scope-error c8-flat">Cancel</button>
118
- <button class="scope-good c8-outlined">Confirm</button>
119
- <button class="scope-warn c8-light">Warning</button>
120
- ```
121
-
122
- ### i8 — Input styles
123
-
124
- Three visual variants for form inputs.
125
-
126
- | Class | Description |
127
- |-------|-------------|
128
- | `i8-flat` | Bottom border only (minimal) |
129
- | `i8-filled` | Full border with background |
130
- | `i8-round` | Pill-shaped (fully rounded) |
131
-
132
- Sub-shortcuts used within input components:
133
-
134
- | Class | Purpose |
135
- |-------|---------|
136
- | `i8-input` | The `<input>` element itself |
137
- | `i8-textarea` | The `<textarea>` element |
138
- | `i8-label` | Floating label |
139
- | `i8-label-wrapper` | Label container for positioning |
140
- | `i8-hint` | Hint text below input |
141
- | `i8-counter` | Character counter |
142
- | `i8-prepend` / `i8-append` | Side icons/content (inside border) |
143
- | `i8-before` / `i8-after` | Side content (outside border) |
144
- | `i8-underline` | Bottom border element |
145
-
146
- ### Other built-in shortcuts
147
-
148
- | Prefix | Components |
149
- |--------|-----------|
150
- | `btn` | Button — `btn`, `btn-round`, `btn-square`, `btn-icon`, `btn-label` |
151
- | `card` | Card — `card`, `card-header`, `card-dense` |
152
- | `checkbox` | Checkbox — `checkbox-root`, `checkbox`, `checkbox-indicator`, `checkbox-icon`, `checkbox-label` |
153
- | `rb` | RadioGroup — `rb-container`, `rb-root`, `rb-item`, `rb-item-indicator`, `rb-item-label` |
154
- | `select` | Select — `select-content`, `select-item`, `select-grp-label`, `select-separator` |
155
- | `dialog` | Dialog — `dialog-overlay`, `dialog-card`, `dialog-header`, `dialog-title`, `dialog-close`, `dialog-footer` |
156
- | `tabs` | Tabs — `tabs-indicator`, `tab` |
157
- | `menu` | Menu — `menu-root`, `menu-item` |
158
- | `loading` | Loading — `loading-indicator`, `loading-indicator-wrapper`, `inner-loading` |
159
- | `toast` | Toasts — `toast-root`, `toasts-viewport` |
160
- | `progress` | ProgressBar — `progress-bar` |
161
-
162
- ## Common patterns
163
-
164
- ### Pattern: Override a single variant
165
-
166
- Change only the base classes of `c8-filled` while keeping all hover/dark/active variants intact:
167
-
168
- ```ts
169
- import { vunorShortcuts, defineShortcuts } from 'vunor/theme'
170
-
171
- const myOverrides = defineShortcuts({
172
- 'c8-filled': {
173
- '': 'rounded-full', // only overrides base — hover, dark, etc. stay
174
- },
175
- })
176
-
177
- export default defineConfig({
178
- presets: [presetVunor()],
179
- shortcuts: [vunorShortcuts(myOverrides)],
180
- })
181
- ```
182
-
183
- ### Pattern: Override input underline style
184
-
185
- ```ts
186
- const myOverrides = defineShortcuts({
187
- 'i8-flat': {
188
- '': 'border-b-2', // thicker bottom border
189
- },
190
- })
191
- ```
192
-
193
- ### Pattern: Add a new shortcut
194
-
195
- ```ts
196
- const myShortcuts = defineShortcuts({
197
- 'badge': {
198
- '': 'inline-flex items-center px-$xs py-$xxs text-caption rounded-full',
199
- 'dark:': 'bg-white/10',
200
- },
201
- })
202
-
203
- shortcuts: [vunorShortcuts(myShortcuts)]
204
- ```
205
-
206
- ### Pattern: Override component-specific shortcuts
207
-
208
- ```ts
209
- const myOverrides = defineShortcuts({
210
- 'btn': {
211
- '': 'uppercase tracking-wider', // make all buttons uppercase
212
- },
213
- 'card': {
214
- '': 'shadow-lg', // add shadow to all cards
215
- },
216
- })
217
- ```
218
-
219
- ## Integration
220
-
221
- - Shortcuts are consumed by UnoCSS — they expand to utility classes at build time.
222
- - The `c8-*` and `i8-*` systems are used internally by Vunor components (`VuButton` uses `c8-*`, `VuInput` uses `i8-*`).
223
- - You can use `c8-*` and `i8-*` classes on any HTML element, not just Vunor components.
224
- - `scope-{color}` must be set on an ancestor (or the element itself) for `c8-*` and `i8-*` to pick up colors.
225
-
226
- ## Best practices
227
-
228
- - Always pass overrides through `vunorShortcuts(overrides)` — don't try to manually merge with the defaults.
229
- - Override at the most specific variant level possible. Replacing `''` (base) is safe; replacing the entire shortcut object loses all variant handling.
230
- - Use `defineShortcuts()` for type safety — it validates the structure at the TypeScript level.
231
- - Keep custom shortcuts in a separate file (e.g., `shortcuts.custom.ts`) for clarity.
232
-
233
- ## Gotchas
234
-
235
- - Shortcut keys are **variant prefixes**, not CSS pseudo-classes. The key `'hover:'` (with colon) prepends `hover:` to each class in the value string.
236
- - The empty string key `''` is the base (no-prefix) classes. It's required for every shortcut.
237
- - `mergeVunorShortcuts` does **deep merge**, not replacement. If you need to completely replace a shortcut, set every key explicitly.
238
- - Shortcut names become CSS class names. Adding a shortcut `'my-card'` means you use `class="my-card"`.
239
- - The `i8` rules (`i8-border-*`, `i8-bg-*`, `i8-outline-*`) are separate UnoCSS rules, not shortcuts. They set CSS custom properties (`--i8-border-color`, `--i8-bg-color`) consumed by the `i8-*` shortcuts.