vuetiwatch 0.1.1 → 0.2.0
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 +196 -17
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +77 -7
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +104 -15
- package/dist/plugin.js.map +1 -1
- package/dist/registry.d.ts +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +6 -1
- package/dist/registry.js.map +1 -1
- package/dist/styles/atlas.css +99 -0
- package/dist/styles/atlas.min.css +1 -0
- package/dist/styles/atlasDark.css +99 -0
- package/dist/styles/atlasDark.min.css +1 -0
- package/dist/styles/atlasSepia.css +99 -0
- package/dist/styles/atlasSepia.min.css +1 -0
- package/dist/styles/aurora.css +97 -4
- package/dist/styles/aurora.min.css +1 -1
- package/dist/styles/brutalist.css +5 -0
- package/dist/styles/brutalist.min.css +1 -1
- package/dist/styles/calm.css +0 -4
- package/dist/styles/calm.min.css +1 -1
- package/dist/styles/candy.css +71 -0
- package/dist/styles/candy.min.css +1 -0
- package/dist/styles/clay.css +9 -0
- package/dist/styles/clay.min.css +1 -1
- package/dist/styles/core.css +231 -41
- package/dist/styles/core.min.css +1 -1
- package/dist/styles/graphite.css +29 -0
- package/dist/styles/graphite.min.css +1 -0
- package/dist/styles/morph.css +8 -0
- package/dist/styles/morph.min.css +1 -1
- package/dist/styles/neon.css +8 -0
- package/dist/styles/neon.min.css +1 -1
- package/dist/styles/sketchy.css +4 -0
- package/dist/styles/sketchy.min.css +1 -1
- package/dist/styles/soft.css +1 -12
- package/dist/styles/soft.min.css +1 -1
- package/dist/styles.css +561 -57
- package/dist/styles.min.css +1 -1
- package/dist/themes/atlas.d.ts +38 -0
- package/dist/themes/atlas.d.ts.map +1 -0
- package/dist/themes/atlas.js +203 -0
- package/dist/themes/atlas.js.map +1 -0
- package/dist/themes/atlasDark.d.ts +8 -0
- package/dist/themes/atlasDark.d.ts.map +1 -0
- package/dist/themes/atlasDark.js +64 -0
- package/dist/themes/atlasDark.js.map +1 -0
- package/dist/themes/atlasSepia.d.ts +11 -0
- package/dist/themes/atlasSepia.d.ts.map +1 -0
- package/dist/themes/atlasSepia.js +60 -0
- package/dist/themes/atlasSepia.js.map +1 -0
- package/dist/themes/aurora.d.ts.map +1 -1
- package/dist/themes/aurora.js +27 -2
- package/dist/themes/aurora.js.map +1 -1
- package/dist/themes/brutalist.d.ts.map +1 -1
- package/dist/themes/brutalist.js +26 -0
- package/dist/themes/brutalist.js.map +1 -1
- package/dist/themes/calm.d.ts.map +1 -1
- package/dist/themes/calm.js +22 -2
- package/dist/themes/calm.js.map +1 -1
- package/dist/themes/candy.d.ts +20 -0
- package/dist/themes/candy.d.ts.map +1 -0
- package/dist/themes/candy.js +142 -0
- package/dist/themes/candy.js.map +1 -0
- package/dist/themes/classic.d.ts.map +1 -1
- package/dist/themes/classic.js +2 -0
- package/dist/themes/classic.js.map +1 -1
- package/dist/themes/clay.d.ts.map +1 -1
- package/dist/themes/clay.js +20 -2
- package/dist/themes/clay.js.map +1 -1
- package/dist/themes/darkGlass.d.ts.map +1 -1
- package/dist/themes/darkGlass.js +21 -0
- package/dist/themes/darkGlass.js.map +1 -1
- package/dist/themes/graphite.d.ts +13 -0
- package/dist/themes/graphite.d.ts.map +1 -0
- package/dist/themes/graphite.js +147 -0
- package/dist/themes/graphite.js.map +1 -0
- package/dist/themes/index.d.ts +5 -0
- package/dist/themes/index.d.ts.map +1 -1
- package/dist/themes/index.js +5 -0
- package/dist/themes/index.js.map +1 -1
- package/dist/themes/liquidGlass.d.ts.map +1 -1
- package/dist/themes/liquidGlass.js +27 -3
- package/dist/themes/liquidGlass.js.map +1 -1
- package/dist/themes/lux.d.ts.map +1 -1
- package/dist/themes/lux.js +21 -2
- package/dist/themes/lux.js.map +1 -1
- package/dist/themes/morph.d.ts.map +1 -1
- package/dist/themes/morph.js +21 -5
- package/dist/themes/morph.js.map +1 -1
- package/dist/themes/neon.d.ts.map +1 -1
- package/dist/themes/neon.js +22 -0
- package/dist/themes/neon.js.map +1 -1
- package/dist/themes/paper.d.ts.map +1 -1
- package/dist/themes/paper.js +16 -0
- package/dist/themes/paper.js.map +1 -1
- package/dist/themes/sketchy.d.ts.map +1 -1
- package/dist/themes/sketchy.js +17 -0
- package/dist/themes/sketchy.js.map +1 -1
- package/dist/themes/slate.d.ts.map +1 -1
- package/dist/themes/slate.js +22 -1
- package/dist/themes/slate.js.map +1 -1
- package/dist/themes/soft.d.ts.map +1 -1
- package/dist/themes/soft.js +21 -2
- package/dist/themes/soft.js.map +1 -1
- package/dist/types.d.ts +40 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/defaults.d.ts.map +1 -1
- package/dist/util/defaults.js +11 -1
- package/dist/util/defaults.js.map +1 -1
- package/dist/util/defineTheme.d.ts.map +1 -1
- package/dist/util/defineTheme.js +11 -5
- package/dist/util/defineTheme.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +8 -2
- package/src/plugin.ts +214 -21
- package/src/registry.ts +10 -0
- package/src/styles/core.css +522 -38
- package/src/styles/index.css +4 -0
- package/src/styles/themes/_atlas.css +194 -0
- package/src/styles/themes/atlas.css +2 -0
- package/src/styles/themes/atlasDark.css +2 -0
- package/src/styles/themes/atlasSepia.css +2 -0
- package/src/styles/themes/aurora.css +151 -7
- package/src/styles/themes/brutalist.css +5 -3
- package/src/styles/themes/calm.css +0 -13
- package/src/styles/themes/candy.css +113 -0
- package/src/styles/themes/clay.css +11 -0
- package/src/styles/themes/graphite.css +63 -0
- package/src/styles/themes/liquidGlass.css +0 -2
- package/src/styles/themes/lux.css +0 -4
- package/src/styles/themes/morph.css +9 -0
- package/src/styles/themes/neon.css +8 -3
- package/src/styles/themes/paper.css +0 -2
- package/src/styles/themes/sketchy.css +9 -0
- package/src/styles/themes/slate.css +0 -4
- package/src/styles/themes/soft.css +1 -12
- package/src/themes/atlas.ts +211 -0
- package/src/themes/atlasDark.ts +64 -0
- package/src/themes/atlasSepia.ts +60 -0
- package/src/themes/aurora.ts +30 -2
- package/src/themes/brutalist.ts +29 -0
- package/src/themes/calm.ts +25 -2
- package/src/themes/candy.ts +144 -0
- package/src/themes/classic.ts +3 -0
- package/src/themes/clay.ts +22 -2
- package/src/themes/darkGlass.ts +24 -0
- package/src/themes/graphite.ts +150 -0
- package/src/themes/index.ts +5 -0
- package/src/themes/liquidGlass.ts +30 -3
- package/src/themes/lux.ts +24 -2
- package/src/themes/morph.ts +23 -5
- package/src/themes/neon.ts +25 -0
- package/src/themes/paper.ts +19 -0
- package/src/themes/sketchy.ts +20 -0
- package/src/themes/slate.ts +25 -1
- package/src/themes/soft.ts +24 -2
- package/src/types.ts +41 -0
- package/src/util/defaults.ts +12 -1
- package/src/util/defineTheme.ts +11 -5
package/README.md
CHANGED
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
🎨 Free, plug-and-play themes for **[Vuetify 4](https://vuetifyjs.com/)** — what [Bootswatch](https://bootswatch.com/)
|
|
8
8
|
is to Bootstrap.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
[](https://j-tap.github.io/vuetiwatch/)
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
**[See all nineteen themes →](https://j-tap.github.io/vuetiwatch/)**
|
|
13
|
+
|
|
14
|
+
Vuetify ships one look. Vuetiwatch ships nineteen, and they differ in more
|
|
15
|
+
than hue: radius, border weight, density, component variants, typography,
|
|
16
|
+
icons and motion all move together.
|
|
15
17
|
|
|
16
18
|
```sh
|
|
17
19
|
npm i vuetiwatch # or: bun add / yarn add / pnpm add
|
|
@@ -24,9 +26,13 @@ npm i vuetiwatch # or: bun add / yarn add / pnpm add
|
|
|
24
26
|
| `classic` | light | Stock Vuetify. Material Design, Roboto, familiar elevation |
|
|
25
27
|
| `paper` | light | Flat editorial — warm paper, ink accents, hairline borders, serif headings |
|
|
26
28
|
| `slate` | light | Dense dashboard — muted slate blue, compact density, outlined cards |
|
|
29
|
+
| `atlas` | light | Calm admin — soft neutrals, one cool accent, dense tables, hairlines not shadows |
|
|
30
|
+
| `atlasDark` | dark | The same panel after dark — soft greys, never pure black |
|
|
31
|
+
| `atlasSepia` | light | The same panel on warm paper and brown ink, for tired eyes |
|
|
27
32
|
| `calm` | light | Low contrast, desaturated naturals, no shadows, a lot of air |
|
|
28
33
|
| `lux` | light | Editorial luxury — hairline rules, wide tracking, small caps, muted gold |
|
|
29
34
|
| `soft` | light | Pastel and generous — large radii, diffuse tinted shadows, hearts for stars |
|
|
35
|
+
| `candy` | light | Plush and pillowy — warm cream, padded shapes, controls that squash |
|
|
30
36
|
| `clay` | light | Inflated pastel shapes with a triple shadow. Toy-like |
|
|
31
37
|
| `morph` | light | Neumorphic — one continuous surface, shaped only by light and shadow |
|
|
32
38
|
| `sketchy` | light | Hand-drawn wobble and pencil greys, for mockups and mirth |
|
|
@@ -36,7 +42,14 @@ npm i vuetiwatch # or: bun add / yarn add / pnpm add
|
|
|
36
42
|
| `aurora` | dark | Iridescent gradients on near-black — the showcase theme |
|
|
37
43
|
| `neon` | dark | Terminal black, cyan and magenta glow, zero radius, monospace headings |
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
`classic` is the control. It opts out of the core stylesheet entirely — not
|
|
46
|
+
one rule in this package matches a page running it — so switching to it
|
|
47
|
+
shows exactly what the other eighteen add.
|
|
48
|
+
|
|
49
|
+
## How to install and apply a theme in Vuetify 4
|
|
50
|
+
|
|
51
|
+
Register the themes in `createVuetify`, then add the plugin. `defaultTheme`
|
|
52
|
+
picks the one that loads; `useTheme().change(name)` switches at runtime.
|
|
40
53
|
|
|
41
54
|
**1. Register the themes.** They are plain Vuetify `ThemeDefinition`s.
|
|
42
55
|
|
|
@@ -72,27 +85,101 @@ treatment.
|
|
|
72
85
|
|
|
73
86
|
The plugin swaps Vuetify's **global component defaults** when the theme
|
|
74
87
|
changes, since those are global rather than per-theme. Your own
|
|
75
|
-
`createVuetify({ defaults })` is preserved; theme defaults layer on top.
|
|
88
|
+
`createVuetify({ defaults })` is preserved; theme defaults layer on top. It
|
|
89
|
+
also exposes `useVuetiwatch()` — see [switching themes](#switching-themes) —
|
|
90
|
+
and accepts `defaults`, `attribute` and `transitions` to turn any of it off.
|
|
76
91
|
|
|
77
92
|
### Switching themes
|
|
78
93
|
|
|
79
|
-
|
|
94
|
+
Vuetify's own `theme.change()` works as it always did. The plugin adds one
|
|
95
|
+
thing on top of it:
|
|
80
96
|
|
|
81
97
|
```vue
|
|
82
98
|
<script setup lang="ts">
|
|
83
|
-
import {
|
|
84
|
-
import { themeList } from 'vuetiwatch'
|
|
99
|
+
import { useVuetiwatch } from 'vuetiwatch'
|
|
85
100
|
|
|
86
|
-
const
|
|
101
|
+
const { themes, current, change } = useVuetiwatch()
|
|
87
102
|
</script>
|
|
88
103
|
|
|
89
104
|
<template>
|
|
90
|
-
<button v-for="t in
|
|
105
|
+
<button v-for="t in themes" :key="t.name" @click="change(t.name, $event)">
|
|
91
106
|
{{ t.meta.title }}
|
|
92
107
|
</button>
|
|
93
108
|
</template>
|
|
94
109
|
```
|
|
95
110
|
|
|
111
|
+
`useVuetiwatch()` also returns `siblings`: the variants of the theme that is
|
|
112
|
+
running, or an empty array when it has none. A theme declares what it belongs
|
|
113
|
+
to through `meta.family`, so a light/dark switch never has to know which
|
|
114
|
+
themes go together:
|
|
115
|
+
|
|
116
|
+
```vue
|
|
117
|
+
<script setup lang="ts">
|
|
118
|
+
const { siblings, current, change } = useVuetiwatch()
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<template>
|
|
122
|
+
<v-btn-toggle v-if="siblings.length > 1" :model-value="current?.name">
|
|
123
|
+
<v-btn
|
|
124
|
+
v-for="t in siblings"
|
|
125
|
+
:key="t.name"
|
|
126
|
+
:value="t.name"
|
|
127
|
+
:text="t.meta.variant"
|
|
128
|
+
@click="change(t.name, $event)"
|
|
129
|
+
/>
|
|
130
|
+
</v-btn-toggle>
|
|
131
|
+
</template>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
A theme can also offer **accents** — named colour sets it repaints itself
|
|
135
|
+
in, declared in `meta.accents` and applied through `setAccent()`:
|
|
136
|
+
|
|
137
|
+
```vue
|
|
138
|
+
<script setup lang="ts">
|
|
139
|
+
const { accents, accent, setAccent } = useVuetiwatch()
|
|
140
|
+
</script>
|
|
141
|
+
|
|
142
|
+
<template>
|
|
143
|
+
<button
|
|
144
|
+
v-for="item in accents"
|
|
145
|
+
:key="item.id"
|
|
146
|
+
:style="{ background: item.colors.primary }"
|
|
147
|
+
@click="setAccent(item.id)"
|
|
148
|
+
/>
|
|
149
|
+
</template>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Presets rather than a colour picker, because every one of them is measured
|
|
153
|
+
against the theme's ground — the Atlas accents run 5.8:1 to 8.3:1 on filled
|
|
154
|
+
controls across all three variants, which a dragged slider cannot promise.
|
|
155
|
+
The id is shared across the family, so the choice survives a light/dark
|
|
156
|
+
switch: each variant carries its own tone of the same accent.
|
|
157
|
+
|
|
158
|
+
`atlas`, `atlasDark` and `atlasSepia` are one family today. Vuetify's
|
|
159
|
+
`ThemeDefinition` is a single mode by definition, so a registered pair is
|
|
160
|
+
how a light/dark switch is built — the design lives in one place and only
|
|
161
|
+
the ground changes.
|
|
162
|
+
|
|
163
|
+
Pass the event and the new theme opens as a circle from the control that was
|
|
164
|
+
pressed, through the View Transitions API — Baseline since October 2025.
|
|
165
|
+
Where the API is missing, where the visitor asked for reduced motion, or
|
|
166
|
+
where you passed `transitions: false`, the same call switches instantly, so
|
|
167
|
+
it is always safe to use. Each theme sets its own pace through
|
|
168
|
+
`vw-theme-transition`: `neon` repaints in 180 ms, `calm` takes 560.
|
|
169
|
+
|
|
170
|
+
### Motion
|
|
171
|
+
|
|
172
|
+
Movement is the fourth axis these themes travel on, after colour, shape and
|
|
173
|
+
type. `brutalist` snaps in two steps, `lux` eases over a quarter-second,
|
|
174
|
+
`candy` overshoots and settles — and a checkbox, a toggle button or a rating
|
|
175
|
+
star pops when it turns on, the way a like button does.
|
|
176
|
+
|
|
177
|
+
None of it costs a byte of JavaScript: themes state a character in a handful
|
|
178
|
+
of variables and the core layer spends it on the same interactions
|
|
179
|
+
everywhere. Only `transform` is animated, so it stays on the compositor.
|
|
180
|
+
`prefers-reduced-motion: reduce` zeroes the variables themselves in one
|
|
181
|
+
place, which covers every theme at once — including one you wrote yourself.
|
|
182
|
+
|
|
96
183
|
### Fonts
|
|
97
184
|
|
|
98
185
|
Themes name the families they want but never load them — that stays your
|
|
@@ -107,7 +194,8 @@ fallback, so skipping this degrades gracefully rather than breaking.
|
|
|
107
194
|
### Shipping only a few themes
|
|
108
195
|
|
|
109
196
|
Pass just those to the plugin and import their stylesheets instead of the
|
|
110
|
-
combined one.
|
|
197
|
+
combined one. The whole stylesheet is about 6.6 kB gzipped; the core layer
|
|
198
|
+
plus one theme is under 3 kB.
|
|
111
199
|
|
|
112
200
|
```ts
|
|
113
201
|
import { createVuetiwatch, paper } from 'vuetiwatch'
|
|
@@ -166,29 +254,120 @@ Vuetify's own, it reads:
|
|
|
166
254
|
|
|
167
255
|
| Variable | Default | Effect |
|
|
168
256
|
| --- | --- | --- |
|
|
169
|
-
| `vw-radius` | `4px` | Fields, alerts, lists, menus, snackbars, slider track |
|
|
170
|
-
| `vw-radius-btn` | `vw-radius` | Buttons
|
|
257
|
+
| `vw-radius` | `4px` | Fields, alerts, lists, menus, snackbars, slider track, progress bars |
|
|
258
|
+
| `vw-radius-btn` | `vw-radius` | Buttons and button groups, for themes whose controls are capsules |
|
|
171
259
|
| `vw-radius-lg` | `vw-radius` | Cards, tables, expansion panels |
|
|
172
|
-
| `vw-radius-chip` | `9999px` | Chips |
|
|
260
|
+
| `vw-radius-chip` | `9999px` | Chips and badges |
|
|
173
261
|
| `vw-radius-thumb` | `50%` | Slider thumb |
|
|
262
|
+
| `vw-radius-icon-btn` | `50%` | Icon buttons, for themes with no curve anywhere |
|
|
263
|
+
| `vw-radius-avatar` / `-dot` | `50%` | Avatars, and the dot on a timeline |
|
|
174
264
|
| `vw-border-width` | `thin` | Outlined variants and field outlines |
|
|
265
|
+
| `vw-outlined-fill` | `surface` | What an outlined card paints — `transparent` for themes whose surface is the ground |
|
|
266
|
+
| `vw-panel-border` / `-shadow` / `-fill` | Vuetify's | A panel standing on its own: a bare list, sheet, banner or table |
|
|
267
|
+
| `vw-panel-image` / `-filter` | none | The same panel's gradient and backdrop filter, for the gradient and glass themes |
|
|
268
|
+
| `vw-outline-opacity` | `border-opacity` | The line around an outlined card, which Vuetify draws in the ink of its own text |
|
|
269
|
+
| `vw-skeleton-opacity` | clamped | The bones of a loading placeholder, which Vuetify ties to `border-opacity` |
|
|
270
|
+
| `vw-field-border-opacity` | `0.38` | The resting outline of a field — Vuetify's default reads about 2.3:1 |
|
|
175
271
|
| `vw-btn-weight` / `-tracking` / `-transform` | Vuetify's | Button typography |
|
|
176
272
|
| `vw-heading-weight` / `-tracking` / `-transform` | Vuetify's | Headings and the display / headline / title-large scale |
|
|
177
273
|
| `vw-th-weight` / `-tracking` / `-transform` | Vuetify's | Table headers |
|
|
274
|
+
| `vw-numeric` | `normal` | Figures in tables and pagination — `tabular-nums` lines columns up |
|
|
178
275
|
| `vw-tab-slider-height` | `2px` | Active tab indicator |
|
|
276
|
+
| `vw-tab-size` | `0.875rem` | Tab labels, which Vuetify builds out of buttons and sizes like body text |
|
|
277
|
+
| `vw-avatar-ratio` | `0.4` | Initials inside an avatar, as a share of its height |
|
|
278
|
+
| `vw-timeline-line` / `-width` | hairline | The timeline connector — any background, so a repeating gradient makes it dashed |
|
|
279
|
+
| `vw-timeline-line-opacity` | `border-opacity` | The connector's fade, when `vw-timeline-line` is left alone |
|
|
280
|
+
| `vw-relief-inset` | `0` | Padding inside chip groups, room for controls that lift |
|
|
179
281
|
| `vw-overlay-shadow` / `-border` | Vuetify's | Menus, dialogs and tooltips |
|
|
180
282
|
| `vw-list-bar` / `-color` | `0` | Accent bar on the active list item, leading edge |
|
|
181
283
|
| `vw-pagination-active-color` / `-opacity` | Vuetify's | The current page |
|
|
182
284
|
| `vw-link-decoration` | `underline` | Bare `<a>` elements |
|
|
183
285
|
| `font-body` / `font-heading` | Roboto | Native to Vuetify 4 |
|
|
184
|
-
|
|
185
|
-
|
|
286
|
+
| `vw-gradient` / `-btn` | none | A theme's gradient, and the deeper one used where text sits on it |
|
|
287
|
+
| `vw-motion-duration` / `-ease` | `160ms` / standard | Press, hover lift, tab slider |
|
|
288
|
+
| `vw-press-scale` / `-shift` | `0.98` / `0` | How far a control squashes and sinks under a press |
|
|
289
|
+
| `vw-hover-lift` | `0` | How far a linked card rises on hover |
|
|
290
|
+
| `vw-pop-scale` / `-duration` | `1.18` / `220ms` | The overshoot when a checkbox, toggle or star turns on |
|
|
291
|
+
| `vw-focus-ring` / `-offset` | `2px solid primary` / `2px` | `:focus-visible` on buttons, tabs, chips, list items |
|
|
292
|
+
| `vw-theme-transition` | `420ms` | The wipe when `change()` switches theme |
|
|
186
293
|
|
|
187
294
|
`font-heading` reaches `text-display-*`, `text-headline-*`, `text-title-large`
|
|
188
295
|
and bare `<h1>`–`<h6>`; the rest of the scale takes `font-body`. Vuetify 4
|
|
189
296
|
uses the Material 3 names — `text-h1`…`text-h6`, `text-body-1`, `text-caption`
|
|
190
297
|
and `text-overline` no longer exist.
|
|
191
298
|
|
|
299
|
+
### What wins
|
|
300
|
+
|
|
301
|
+
These variables set the *default* a component draws with — the same job
|
|
302
|
+
Vuetify's SASS variables do at build time, moved to runtime so the look can
|
|
303
|
+
be swapped without recompiling. Vuetify draws the same line itself: it ships
|
|
304
|
+
`$button-border-radius` for the default and `$button-rounded-border-radius`
|
|
305
|
+
for what the `rounded` prop asks for.
|
|
306
|
+
|
|
307
|
+
Four levels, loudest first:
|
|
308
|
+
|
|
309
|
+
| | Set by | Example |
|
|
310
|
+
| --- | --- | --- |
|
|
311
|
+
| **1. A prop or utility class** | the component's author | `<v-card rounded="xl">`, `class="rounded-0"` |
|
|
312
|
+
| **2. The theme's component defaults** | the theme | `candy` making every `VBtn` `size="large"` |
|
|
313
|
+
| **3. Your `createVuetify({ defaults })`** | you, once | `VDataTable: { hover: true }` |
|
|
314
|
+
| **4. Vuetify** | the framework | the 4px corner every component ships with |
|
|
315
|
+
|
|
316
|
+
Level 1 is why every rule in the core layer carries `:not([class*='rounded-'])`:
|
|
317
|
+
a component whose author asked for a corner keeps it whatever the theme says.
|
|
318
|
+
Level 2 sits above level 3 on purpose — a theme that could not change a
|
|
319
|
+
variant would not be a theme — and `createVuetiwatch(vuetify, { defaults:
|
|
320
|
+
'under' })` swaps the two when the app owns a decision the theme also has an
|
|
321
|
+
opinion on, an admin panel with its own density switch being the usual case.
|
|
322
|
+
It overrides only the keys it actually
|
|
323
|
+
sets, so the rest of your configuration survives every switch, including
|
|
324
|
+
defaults you assign at runtime. Level 4 holds because every declaration
|
|
325
|
+
falls back to Vuetify's own value, which is why importing the stylesheet
|
|
326
|
+
cannot change an app that is not running a Vuetiwatch theme.
|
|
327
|
+
|
|
328
|
+
A fifth level sits underneath, in CSS rather than in props: your own
|
|
329
|
+
stylesheet. Vuetify ships every rule inside `@layer vuetify-*`, and an
|
|
330
|
+
unlayered rule outranks a layered one whatever its specificity, so the core
|
|
331
|
+
layer needs no weight of its own to beat it — and takes none. Its scope is a
|
|
332
|
+
`:where()`, which leaves each rule weighing the component selector and its
|
|
333
|
+
guard, two classes at most. Anything with a second selector of its own clears
|
|
334
|
+
it:
|
|
335
|
+
|
|
336
|
+
```css
|
|
337
|
+
/* wins over the theme */
|
|
338
|
+
.v-application .v-btn { border-radius: 0 }
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
A theme file, which carries its own theme class, clears it the same way —
|
|
342
|
+
which is what makes a theme an opinion over the defaults rather than a peer
|
|
343
|
+
of them. The one exception is marked as such in the stylesheet: a short block
|
|
344
|
+
of corrections that has to outrank a theme, because a theme's own rule does
|
|
345
|
+
damage in a context it never considered — a per-button shadow drawing a seam
|
|
346
|
+
down every join of a button group.
|
|
347
|
+
|
|
348
|
+
The practical version: if a component looks wrong under a theme, look for a
|
|
349
|
+
`rounded`, `variant`, `elevation` or `density` prop on it first. That prop is
|
|
350
|
+
doing exactly what it is meant to do, and the theme is standing aside.
|
|
351
|
+
|
|
352
|
+
### Contrast
|
|
353
|
+
|
|
354
|
+
Vuetify picks the label colour for a filled control automatically, by
|
|
355
|
+
whichever of black or white scores higher on [APCA](https://github.com/Myndex/apca-w3) —
|
|
356
|
+
the WCAG 3 draft algorithm. It optimises, but it does not clear a bar: on
|
|
357
|
+
Vuetify's own light theme the filled `warning` button lands at 2.4:1 against
|
|
358
|
+
WCAG 2's 4.5:1 for label text.
|
|
359
|
+
|
|
360
|
+
Every theme here is measured against both, and states `on-*` explicitly
|
|
361
|
+
wherever the automatic pick falls short — which is the documented way to
|
|
362
|
+
control it. Where the two models disagreed the colour was deepened a few
|
|
363
|
+
points of lightness instead of flipping the label, so the hue survives and
|
|
364
|
+
both models pass; `aurora` keeps its bright gradient for decoration and
|
|
365
|
+
carries a deeper one behind anything with text on it.
|
|
366
|
+
|
|
367
|
+
The result: in all eighteen non-stock themes every filled control clears 4.5:1.
|
|
368
|
+
`classic` does not, and that is the point of it — it is stock Vuetify,
|
|
369
|
+
including this.
|
|
370
|
+
|
|
192
371
|
### Icons
|
|
193
372
|
|
|
194
373
|
Vuetify's icon *set* is global and cannot vary per theme, but which glyph a
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export { aurora, brutalist, calm, classic, clay, darkGlass, liquidGlass, lux, morph, neon, paper, sketchy, slate, soft, } from './themes/index.js';
|
|
1
|
+
export { atlas, atlasDark, atlasSepia, aurora, brutalist, calm, candy, classic, clay, darkGlass, graphite, liquidGlass, lux, morph, neon, paper, sketchy, slate, soft, } from './themes/index.js';
|
|
2
2
|
export { themeList, vuetiwatchDefaults, vuetiwatchMeta, vuetiwatchThemes, } from './registry.js';
|
|
3
|
-
export { createVuetiwatch } from './plugin.js';
|
|
3
|
+
export { createVuetiwatch, useVuetiwatch } from './plugin.js';
|
|
4
4
|
export { defineTheme } from './util/defineTheme.js';
|
|
5
5
|
export { bars, combine, controls, fields, icons, surfaces, tables } from './util/defaults.js';
|
|
6
|
-
export type { VuetiwatchOptions } from './plugin.js';
|
|
6
|
+
export type { Vuetiwatch, VuetiwatchOptions } from './plugin.js';
|
|
7
7
|
export type { VuetiwatchThemeName } from './registry.js';
|
|
8
8
|
export type { DefineThemeOptions } from './util/defineTheme.js';
|
|
9
9
|
export type { VuetiwatchIcons } from './util/defaults.js';
|
|
10
|
-
export type { VuetiwatchDefaults, VuetiwatchMeta, VuetiwatchTheme, } from './types.js';
|
|
10
|
+
export type { VuetiwatchAccent, VuetiwatchDefaults, VuetiwatchMeta, VuetiwatchTheme, } from './types.js';
|
|
11
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,SAAS,EACT,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,SAAS,EACT,WAAW,EACX,GAAG,EACH,KAAK,EACL,IAAI,EACJ,KAAK,EACL,OAAO,EACP,KAAK,EACL,IAAI,GACL,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,cAAc,EACd,gBAAgB,GACjB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,SAAS,EACT,UAAU,EACV,MAAM,EACN,SAAS,EACT,IAAI,EACJ,KAAK,EACL,OAAO,EACP,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,WAAW,EACX,GAAG,EACH,KAAK,EACL,IAAI,EACJ,KAAK,EACL,OAAO,EACP,KAAK,EACL,IAAI,GACL,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,cAAc,EACd,gBAAgB,GACjB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAE7F,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAChE,YAAY,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACxD,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC/D,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { aurora, brutalist, calm, classic, clay, darkGlass, liquidGlass, lux, morph, neon, paper, sketchy, slate, soft, } from './themes/index.js';
|
|
1
|
+
export { atlas, atlasDark, atlasSepia, aurora, brutalist, calm, candy, classic, clay, darkGlass, graphite, liquidGlass, lux, morph, neon, paper, sketchy, slate, soft, } from './themes/index.js';
|
|
2
2
|
export { themeList, vuetiwatchDefaults, vuetiwatchMeta, vuetiwatchThemes, } from './registry.js';
|
|
3
|
-
export { createVuetiwatch } from './plugin.js';
|
|
3
|
+
export { createVuetiwatch, useVuetiwatch } from './plugin.js';
|
|
4
4
|
export { defineTheme } from './util/defineTheme.js';
|
|
5
5
|
export { bars, combine, controls, fields, icons, surfaces, tables } from './util/defaults.js';
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,SAAS,EACT,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,SAAS,EACT,WAAW,EACX,GAAG,EACH,KAAK,EACL,IAAI,EACJ,KAAK,EACL,OAAO,EACP,KAAK,EACL,IAAI,GACL,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,cAAc,EACd,gBAAgB,GACjB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,SAAS,EACT,UAAU,EACV,MAAM,EACN,SAAS,EACT,IAAI,EACJ,KAAK,EACL,OAAO,EACP,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,WAAW,EACX,GAAG,EACH,KAAK,EACL,IAAI,EACJ,KAAK,EACL,OAAO,EACP,KAAK,EACL,IAAI,GACL,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,cAAc,EACd,gBAAgB,GACjB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA"}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
|
-
import type { Plugin } from 'vue';
|
|
2
|
-
import type { VuetiwatchTheme } from './types.js';
|
|
1
|
+
import type { ComputedRef, Plugin, Ref } from 'vue';
|
|
2
|
+
import type { VuetiwatchAccent, VuetiwatchTheme } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Anything that might carry viewport coordinates. Partial on purpose: a
|
|
5
|
+
* list item hands its handler `MouseEvent | KeyboardEvent`, and a keyboard
|
|
6
|
+
* activation should not have to be filtered out by every caller.
|
|
7
|
+
*/
|
|
8
|
+
interface Point {
|
|
9
|
+
clientX?: number;
|
|
10
|
+
clientY?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface Vuetiwatch {
|
|
13
|
+
/** The themes handed to the plugin, in the order a picker should show them. */
|
|
14
|
+
themes: readonly VuetiwatchTheme[];
|
|
15
|
+
/** The active theme, or `undefined` while a theme outside the list is on. */
|
|
16
|
+
current: ComputedRef<VuetiwatchTheme | undefined>;
|
|
17
|
+
/**
|
|
18
|
+
* The variants of the active theme's family, in registration order, or an
|
|
19
|
+
* empty array when it has none. This is what a light/dark switch iterates
|
|
20
|
+
* over — the family is declared in each theme's `meta`, so the app never
|
|
21
|
+
* has to know which themes belong together.
|
|
22
|
+
*/
|
|
23
|
+
siblings: ComputedRef<readonly VuetiwatchTheme[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Switches theme through a view transition when the browser has one.
|
|
26
|
+
*
|
|
27
|
+
* Pass the event that triggered it and the new theme opens as a circle
|
|
28
|
+
* from the pointer; without one it opens from the centre of the screen.
|
|
29
|
+
* Falls back to an instant change where the API is missing, where the
|
|
30
|
+
* user asked for reduced motion, or where `transitions` is off — so it is
|
|
31
|
+
* always safe to call.
|
|
32
|
+
*/
|
|
33
|
+
change: (name: string, event?: Point | Event | null) => void;
|
|
34
|
+
/** The accents the active theme offers, or an empty array. */
|
|
35
|
+
accents: ComputedRef<readonly VuetiwatchAccent[]>;
|
|
36
|
+
/** The accent in use, by id. `undefined` until one is chosen. */
|
|
37
|
+
accent: Readonly<Ref<string | undefined>>;
|
|
38
|
+
/**
|
|
39
|
+
* Repaints the theme in one of its accents.
|
|
40
|
+
*
|
|
41
|
+
* The colours are written into Vuetify's live definitions, which are
|
|
42
|
+
* reactive, so every surface follows within the frame. It is applied to
|
|
43
|
+
* the whole family at once — each variant has its own tone of the same
|
|
44
|
+
* accent — so the choice survives a light/dark switch.
|
|
45
|
+
*/
|
|
46
|
+
setAccent: (id: string) => void;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The plugin's API, for switching themes and reading the active one.
|
|
50
|
+
*
|
|
51
|
+
* ```ts
|
|
52
|
+
* const { themes, current, change } = useVuetiwatch()
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function useVuetiwatch(): Vuetiwatch;
|
|
3
56
|
export interface VuetiwatchOptions {
|
|
4
57
|
/**
|
|
5
58
|
* Themes to manage — normally the package's `themeList`, or just the ones
|
|
@@ -7,20 +60,36 @@ export interface VuetiwatchOptions {
|
|
|
7
60
|
*
|
|
8
61
|
* Required rather than defaulted, because a default would mean importing
|
|
9
62
|
* the registry here, and a static import cannot be shaken out: every app
|
|
10
|
-
* would carry all
|
|
63
|
+
* would carry all sixteen themes even after passing one.
|
|
11
64
|
*/
|
|
12
65
|
themes: readonly VuetiwatchTheme[];
|
|
13
66
|
/**
|
|
14
|
-
* Apply each theme's component defaults
|
|
15
|
-
*
|
|
67
|
+
* Apply each theme's component defaults.
|
|
68
|
+
*
|
|
69
|
+
* - `'over'` (or `true`) — the theme's values win, so a theme can restyle
|
|
70
|
+
* a component the app never thought about.
|
|
71
|
+
* - `'under'` — yours win, and the theme fills in only what you left
|
|
72
|
+
* unset. Use it when the app owns a decision the theme also has an
|
|
73
|
+
* opinion on: an admin panel with its own density switch, say.
|
|
74
|
+
* - `false` — no theme defaults at all.
|
|
75
|
+
*
|
|
76
|
+
* @default 'over'
|
|
16
77
|
*/
|
|
17
|
-
defaults?: boolean;
|
|
78
|
+
defaults?: boolean | 'over' | 'under';
|
|
18
79
|
/**
|
|
19
80
|
* Mirror the active theme name onto `<html data-vuetiwatch="...">`, so
|
|
20
81
|
* you can hang your own CSS off it.
|
|
21
82
|
* @default true
|
|
22
83
|
*/
|
|
23
84
|
attribute?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Animate theme changes made through `useVuetiwatch().change()`.
|
|
87
|
+
*
|
|
88
|
+
* Only that call is affected — an app switching through Vuetify's own
|
|
89
|
+
* `theme.change()` keeps the instant swap it has today.
|
|
90
|
+
* @default true
|
|
91
|
+
*/
|
|
92
|
+
transitions?: boolean;
|
|
24
93
|
}
|
|
25
94
|
/**
|
|
26
95
|
* Keeps Vuetify's global component defaults in sync with the active theme.
|
|
@@ -34,8 +103,9 @@ export interface VuetiwatchOptions {
|
|
|
34
103
|
* Install it after Vuetify:
|
|
35
104
|
*
|
|
36
105
|
* ```ts
|
|
37
|
-
* app.use(vuetify).use(createVuetiwatch(vuetify))
|
|
106
|
+
* app.use(vuetify).use(createVuetiwatch(vuetify, { themes: themeList }))
|
|
38
107
|
* ```
|
|
39
108
|
*/
|
|
40
109
|
export declare function createVuetiwatch(vuetify: unknown, options: VuetiwatchOptions): Plugin;
|
|
110
|
+
export {};
|
|
41
111
|
//# sourceMappingURL=plugin.d.ts.map
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAO,MAAM,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAO,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAsB,eAAe,EAAE,MAAM,YAAY,CAAA;AAmBvF;;;;GAIG;AACH,UAAU,KAAK;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAcD,MAAM,WAAW,UAAU;IACzB,+EAA+E;IAC/E,MAAM,EAAE,SAAS,eAAe,EAAE,CAAA;IAClC,6EAA6E;IAC7E,OAAO,EAAE,WAAW,CAAC,eAAe,GAAG,SAAS,CAAC,CAAA;IACjD;;;;;OAKG;IACH,QAAQ,EAAE,WAAW,CAAC,SAAS,eAAe,EAAE,CAAC,CAAA;IACjD;;;;;;;;OAQG;IACH,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,IAAI,KAAK,IAAI,CAAA;IAC5D,8DAA8D;IAC9D,OAAO,EAAE,WAAW,CAAC,SAAS,gBAAgB,EAAE,CAAC,CAAA;IACjD,iEAAiE;IACjE,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;IACzC;;;;;;;OAOG;IACH,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC;AAID;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAK,UAAU,CAW3C;AAMD,MAAM,WAAW,iBAAiB;IAChC;;;;;;;OAOG;IACH,MAAM,EAAE,SAAS,eAAe,EAAE,CAAA;IAClC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;IACrC;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,iBAAiB,GACzB,MAAM,CAiJR"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
|
-
import { watch } from 'vue';
|
|
1
|
+
import { computed, inject, ref, watch } from 'vue';
|
|
2
2
|
import { combine } from './util/defaults.js';
|
|
3
|
+
const injectionKey = Symbol.for('vuetiwatch');
|
|
4
|
+
/**
|
|
5
|
+
* The plugin's API, for switching themes and reading the active one.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* const { themes, current, change } = useVuetiwatch()
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export function useVuetiwatch() {
|
|
12
|
+
const api = inject(injectionKey);
|
|
13
|
+
if (!api) {
|
|
14
|
+
throw new Error('[vuetiwatch] useVuetiwatch() was called before the plugin was installed. ' +
|
|
15
|
+
'Add app.use(createVuetiwatch(vuetify, { themes })) first.');
|
|
16
|
+
}
|
|
17
|
+
return api;
|
|
18
|
+
}
|
|
19
|
+
const prefersReducedMotion = () => typeof window !== 'undefined' &&
|
|
20
|
+
window.matchMedia?.('(prefers-reduced-motion: reduce)').matches === true;
|
|
3
21
|
/**
|
|
4
22
|
* Keeps Vuetify's global component defaults in sync with the active theme.
|
|
5
23
|
*
|
|
@@ -12,41 +30,112 @@ import { combine } from './util/defaults.js';
|
|
|
12
30
|
* Install it after Vuetify:
|
|
13
31
|
*
|
|
14
32
|
* ```ts
|
|
15
|
-
* app.use(vuetify).use(createVuetiwatch(vuetify))
|
|
33
|
+
* app.use(vuetify).use(createVuetiwatch(vuetify, { themes: themeList }))
|
|
16
34
|
* ```
|
|
17
35
|
*/
|
|
18
36
|
export function createVuetiwatch(vuetify, options) {
|
|
19
|
-
const { themes, defaults: applyDefaults = true, attribute = true, } = options;
|
|
37
|
+
const { themes, defaults: applyDefaults = true, attribute = true, transitions = true, } = options;
|
|
20
38
|
// Only the defaults are ever read here, so the closure holds those rather
|
|
21
39
|
// than the whole theme objects — palettes and metadata for every theme
|
|
22
40
|
// would otherwise stay pinned for the life of the app.
|
|
23
41
|
const defaultsByName = new Map(themes.map(theme => [theme.name, theme.defaults]));
|
|
24
42
|
return {
|
|
25
|
-
install(
|
|
43
|
+
install(app) {
|
|
26
44
|
const instance = vuetify;
|
|
27
45
|
if (!instance?.theme?.global?.name) {
|
|
28
46
|
throw new Error('[vuetiwatch] createVuetiwatch() expects the object returned by createVuetify(). ' +
|
|
29
47
|
'Install it after Vuetify: app.use(vuetify).use(createVuetiwatch(vuetify))');
|
|
30
48
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Whatever the app configured itself — theme defaults layer on top.
|
|
51
|
+
*
|
|
52
|
+
* Re-read rather than captured once, because `defaults` is a public
|
|
53
|
+
* ref an app is allowed to reassign at runtime. Anything we did not
|
|
54
|
+
* write ourselves is the app speaking, and becomes the new base;
|
|
55
|
+
* without the check, the first theme change after such a reassignment
|
|
56
|
+
* would quietly throw the app's own defaults away.
|
|
57
|
+
*/
|
|
58
|
+
let userDefaults = instance.defaults.value ?? {};
|
|
59
|
+
let applied;
|
|
36
60
|
const defaultsFor = (name) => {
|
|
61
|
+
if (applied && instance.defaults.value !== applied) {
|
|
62
|
+
userDefaults = instance.defaults.value ?? {};
|
|
63
|
+
}
|
|
37
64
|
const themeDefaults = defaultsByName.get(name);
|
|
38
65
|
if (!themeDefaults)
|
|
39
66
|
return userDefaults;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
67
|
+
return applyDefaults === 'under'
|
|
68
|
+
? combine(themeDefaults, userDefaults)
|
|
69
|
+
: combine(userDefaults, themeDefaults);
|
|
70
|
+
};
|
|
71
|
+
const apply = (name) => {
|
|
72
|
+
if (instance.theme.change)
|
|
73
|
+
instance.theme.change(name);
|
|
74
|
+
else
|
|
75
|
+
instance.theme.global.name.value = name;
|
|
76
|
+
};
|
|
77
|
+
const change = (name, event) => {
|
|
78
|
+
const doc = typeof document !== 'undefined'
|
|
79
|
+
? document
|
|
80
|
+
: undefined;
|
|
81
|
+
if (!transitions || !doc?.startViewTransition || prefersReducedMotion()) {
|
|
82
|
+
apply(name);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Viewport coordinates, because the transition pseudo-elements are
|
|
87
|
+
* laid out against the viewport rather than against any element.
|
|
88
|
+
*
|
|
89
|
+
* A click synthesised by the keyboard reports 0/0, which would open
|
|
90
|
+
* the wipe from the corner of the screen rather than from anything
|
|
91
|
+
* the user can see — those fall back to the centre.
|
|
92
|
+
*/
|
|
93
|
+
const root = doc.documentElement;
|
|
94
|
+
const point = event && 'clientX' in event ? event : undefined;
|
|
95
|
+
const fromPointer = !!point?.clientX || !!point?.clientY;
|
|
96
|
+
root.style.setProperty('--vw-origin-x', fromPointer ? `${point?.clientX ?? 0}px` : '50%');
|
|
97
|
+
root.style.setProperty('--vw-origin-y', fromPointer ? `${point?.clientY ?? 0}px` : '50%');
|
|
98
|
+
/**
|
|
99
|
+
* A transition interrupted by the next one rejects `ready` and
|
|
100
|
+
* `finished`. Nothing here awaits them, so the rejection would
|
|
101
|
+
* surface as an unhandled one in the console the first time
|
|
102
|
+
* somebody clicks twice quickly — which is exactly what a theme
|
|
103
|
+
* picker invites.
|
|
104
|
+
*/
|
|
105
|
+
const transition = doc.startViewTransition(() => { apply(name); });
|
|
106
|
+
transition?.ready?.catch(() => { });
|
|
107
|
+
transition?.finished?.catch(() => { });
|
|
108
|
+
};
|
|
109
|
+
const current = computed(() => themes.find(theme => theme.name === instance.theme.global.name.value));
|
|
110
|
+
const accent = ref();
|
|
111
|
+
const setAccent = (id) => {
|
|
112
|
+
const definitions = instance.theme.themes?.value;
|
|
113
|
+
if (!definitions)
|
|
114
|
+
return;
|
|
115
|
+
for (const theme of themes) {
|
|
116
|
+
const preset = theme.meta.accents?.find(item => item.id === id);
|
|
117
|
+
const colors = definitions[theme.name]?.colors;
|
|
118
|
+
if (preset && colors)
|
|
119
|
+
Object.assign(colors, preset.colors);
|
|
44
120
|
}
|
|
45
|
-
|
|
121
|
+
accent.value = id;
|
|
46
122
|
};
|
|
123
|
+
app.provide(injectionKey, {
|
|
124
|
+
themes,
|
|
125
|
+
current,
|
|
126
|
+
accents: computed(() => current.value?.meta.accents ?? []),
|
|
127
|
+
accent,
|
|
128
|
+
setAccent,
|
|
129
|
+
siblings: computed(() => {
|
|
130
|
+
const family = current.value?.meta.family;
|
|
131
|
+
return family ? themes.filter(theme => theme.meta.family === family) : [];
|
|
132
|
+
}),
|
|
133
|
+
change,
|
|
134
|
+
});
|
|
47
135
|
watch(() => instance.theme.global.name.value, name => {
|
|
48
136
|
if (applyDefaults) {
|
|
49
|
-
|
|
137
|
+
applied = defaultsFor(name);
|
|
138
|
+
instance.defaults.value = applied;
|
|
50
139
|
}
|
|
51
140
|
if (attribute && typeof document !== 'undefined') {
|
|
52
141
|
document.documentElement.setAttribute('data-vuetiwatch', name);
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
package/dist/registry.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { VuetiwatchDefaults, VuetiwatchMeta, VuetiwatchTheme } from './type
|
|
|
4
4
|
* Every theme in the package, in the order a picker should show them:
|
|
5
5
|
* the everyday ones first, then the niche ones.
|
|
6
6
|
*/
|
|
7
|
-
export declare const themeList: readonly [VuetiwatchTheme<"classic">, VuetiwatchTheme<"paper">, VuetiwatchTheme<"slate">, VuetiwatchTheme<"calm">, VuetiwatchTheme<"lux">, VuetiwatchTheme<"soft">, VuetiwatchTheme<"clay">, VuetiwatchTheme<"morph">, VuetiwatchTheme<"sketchy">, VuetiwatchTheme<"brutalist">, VuetiwatchTheme<"liquidGlass">, VuetiwatchTheme<"darkGlass">, VuetiwatchTheme<"aurora">, VuetiwatchTheme<"neon">];
|
|
7
|
+
export declare const themeList: readonly [VuetiwatchTheme<"classic">, VuetiwatchTheme<"paper">, VuetiwatchTheme<"slate">, VuetiwatchTheme<"graphite">, VuetiwatchTheme<"atlas">, VuetiwatchTheme<"atlasDark">, VuetiwatchTheme<"atlasSepia">, VuetiwatchTheme<"calm">, VuetiwatchTheme<"lux">, VuetiwatchTheme<"soft">, VuetiwatchTheme<"candy">, VuetiwatchTheme<"clay">, VuetiwatchTheme<"morph">, VuetiwatchTheme<"sketchy">, VuetiwatchTheme<"brutalist">, VuetiwatchTheme<"liquidGlass">, VuetiwatchTheme<"darkGlass">, VuetiwatchTheme<"aurora">, VuetiwatchTheme<"neon">];
|
|
8
8
|
export type VuetiwatchThemeName = (typeof themeList)[number]['name'];
|
|
9
9
|
type ByName<T> = Record<VuetiwatchThemeName, T>;
|
|
10
10
|
/** Ready for `createVuetify({ theme: { themes: vuetiwatchThemes } })`. */
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAuBrF;;;GAGG;AACH,eAAO,MAAM,SAAS,khBAoByB,CAAA;AAE/C,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;AAEpE,KAAK,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAA;AAc/C,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,eAAe,CAAU,CAAA;AAE/D,6EAA6E;AAC7E,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,CAAY,CAAA;AAEtE,8EAA8E;AAC9E,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,cAAc,CAAQ,CAAA"}
|