kmcom-nuxt-layers 4.0.1 → 5.0.1
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/docs/PEER-DEPENDENCY-MISMATCHES.md +48 -0
- package/docs/VITE-PLUS-MIGRATION.md +246 -0
- package/docs/superpowers/plans/2026-09-03-layout-layer-fluid-rebuild.md +8 -8
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-7-design.md +1 -1
- package/docs/superpowers/specs/2026-08-31-swiss-grid-layer-design.md +3 -3
- package/docs/superpowers/specs/2026-09-03-layout-layer-fluid-rebuild-design.md +3 -3
- package/layers/content/app/components/Blog/Article.vue +1 -1
- package/layers/content/app/components/Gallery/Detail.vue +8 -8
- package/layers/content/app/components/Gallery/Grid.vue +8 -8
- package/layers/content/app/components/Gallery/ImageDetail.vue +6 -6
- package/layers/content/app/components/Gallery/Lightbox.vue +3 -3
- package/layers/content/app/components/NuxtContent/Detail.vue +1 -1
- package/layers/content/app/components/NuxtContent/List.vue +1 -1
- package/layers/content/app/components/Portfolio/ColorPalette.vue +5 -5
- package/layers/content/app/components/Portfolio/Detail.vue +1 -1
- package/layers/content/app/components/Portfolio/List.vue +1 -1
- package/layers/content/app/components/Portfolio/Typography.vue +5 -5
- package/layers/content/app/components/content/Figure.vue +1 -1
- package/layers/core/app/assets/css/layout.css +1 -1
- package/layers/core/app/components/DiagnosticsPage.vue +15 -15
- package/layers/core/app/components/Element.test.ts +1 -1
- package/layers/core/app/components/ElementTw.test.ts +1 -1
- package/layers/core/app/components/ErrorBoundary.vue +5 -5
- package/layers/core/app/composables/useElementGrid.test.ts +1 -1
- package/layers/core/app/composables/useElementInteraction.test.ts +1 -1
- package/layers/core/app/composables/useElementLayout.test.ts +1 -1
- package/layers/core/app/composables/useElementSizing.test.ts +1 -1
- package/layers/core/app/composables/useElementSurface.test.ts +1 -1
- package/layers/core/app/composables/useLayoutAttrs.test.ts +1 -1
- package/layers/core/app/error.vue +1 -1
- package/layers/core/app/pages/[...slug].vue +2 -2
- package/layers/core/app/utils/browserInfo.test.ts +1 -1
- package/layers/core/app/utils/featureClasses.test.ts +1 -1
- package/layers/core/app/utils/scrollGuardClamp.test.ts +1 -1
- package/layers/core/app/utils/styleToTailwindClasses.test.ts +1 -1
- package/layers/data/forms/app/utils/contact-schema.test.ts +1 -1
- package/layers/data/forms/app/utils/fieldProps.test.ts +1 -1
- package/layers/data/metadata/providers/comicvine/server/utils/metadata-comicvine/normalise.test.ts +1 -1
- package/layers/data/metadata/providers/google-books/server/utils/metadata-google-books/client.test.ts +1 -1
- package/layers/data/metadata/providers/google-books/server/utils/metadata-google-books/normalise.test.ts +1 -1
- package/layers/data/metadata/providers/openlibrary/server/utils/metadata-openlibrary/client.test.ts +1 -1
- package/layers/data/metadata/providers/openlibrary/server/utils/metadata-openlibrary/normalise.test.ts +1 -1
- package/layers/data/metadata/providers/themoviedb/server/utils/metadata-tmdb/client.test.ts +1 -1
- package/layers/data/metadata/providers/themoviedb/server/utils/metadata-tmdb/normalise.test.ts +1 -1
- package/layers/data/metadata/server/utils/metadata/cache.test.ts +1 -1
- package/layers/data/metadata/server/utils/metadata/provider-registry.test.ts +1 -1
- package/layers/data/metadata/server/utils/metadata/search.test.ts +1 -1
- package/layers/delivery/feeds/app/assets/css/feeds.css +23 -11
- package/layers/delivery/feeds/app/components/Feeds/Index.vue +10 -10
- package/layers/delivery/feeds/app/components/Feeds/RouteCard.vue +1 -1
- package/layers/delivery/feeds/public/feed/style.css +16 -16
- package/layers/delivery/feeds/server/utils/content-adapter.test.ts +1 -1
- package/layers/delivery/feeds/server/utils/feed-author.test.ts +1 -1
- package/layers/delivery/feeds/server/utils/feed-config.test.ts +1 -1
- package/layers/delivery/feeds/server/utils/formats.test.ts +1 -1
- package/layers/delivery/scripts/app/components/BaselineStatus.vue +3 -1
- package/layers/delivery/seo/app/utils/seoConfig.test.ts +1 -1
- package/layers/design-system/theming/app/assets/css/theme.css +0 -1
- package/layers/design-system/theming/app/components/ThemePicker/AccentButton.vue +2 -2
- package/layers/design-system/theming/app/components/ThemePicker/Menu.vue +3 -3
- package/layers/design-system/theming/app/composables/useAccentColor.ts +4 -1
- package/layers/design-system/theming/app/plugins/theme.client.ts +15 -13
- package/layers/design-system/theming/app/utils/themeAdapter.ts +1 -1
- package/layers/design-system/theming/nuxt.config.ts +1 -1
- package/layers/design-system/theming/server/utils/accent-css.test.ts +1 -1
- package/layers/design-system/theming/server/utils/fouc-config.test.ts +1 -1
- package/layers/design-system/typography/app/components/Typography/TextStroke.vue +1 -1
- package/layers/design-system/visual/app/assets/css/visual.css +0 -1
- package/layers/design-system/visual/app/components/Accent/Scene.vue +2 -2
- package/layers/design-system/visual/app/components/Tint/Overlay.vue +1 -1
- package/layers/design-system/visual/app/utils/colorTokens.test.ts +1 -1
- package/layers/design-system/visual/app/utils/gradientStyle.test.ts +1 -1
- package/layers/design-system/visual/app/utils/responsiveSizes.test.ts +1 -1
- package/layers/motion/animations/app/components/Motion/Marquee.vue +1 -1
- package/layers/motion/animations/app/components/Motion/MarqueeText.vue +3 -3
- package/layers/motion/scroll/app/components/Motion/HorizontalScroll.vue +1 -1
- package/layers/motion/scroll/app/components/Motion/PinnedSection.vue +2 -2
- package/layers/motion/scroll/app/components/Motion/ScrollProgress.vue +2 -2
- package/layers/motion/scroll/app/components/Motion/ScrollStats.vue +6 -6
- package/layers/motion/transitions/app/assets/css/transitions.css +12 -12
- package/layers/render/canvas/nuxt.config.ts +1 -1
- package/layers/render/canvas/package.json +2 -1
- package/layers/render/shader/app/assets/css/shader.css +5 -5
- package/layers/render/shader/app/shaders/common/grain.ts +1 -1
- package/layers/render/shader/app/types/tsl.ts +1 -1
- package/layers/render/shader/app/utils/tsl/index.ts +2 -0
- package/layers/structure/grid/app/assets/css/grid.css +2 -0
- package/layers/structure/grid/app/composables/useGridTracks.test.ts +1 -1
- package/layers/structure/grid/app/types/tracks.test.ts +1 -1
- package/layers/structure/grid/app/utils/placement.test.ts +1 -1
- package/layers/structure/layout/app/components/Layout/Grid/Debug.vue +1 -1
- package/layers/structure/layout/app/components/Layout/Page/Header.vue +3 -3
- package/layers/structure/layout/app/components/Layout/Page/index.vue +16 -11
- package/layers/structure/layout/app/utils/gridPlacementStyle.test.ts +1 -1
- package/layers/structure/navigation/app/components/Mast/Nav.vue +1 -1
- package/layers/structure/navigation/app/components/Mast/NavModal.vue +2 -2
- package/layers/structure/navigation/app/utils/site.test.ts +1 -1
- package/layers/structure/routing/app/utils/resolveRoute.test.ts +1 -1
- package/package.json +26 -45
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Peer Dependency Mismatches
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
Checked on 2026-10-16 against the `KMCOMv5/frontend` consumer after upgrading its dependencies. `pnpm peers check` reports four unmet peers from `kmcom-nuxt-layers@5.0.0`:
|
|
6
|
+
|
|
7
|
+
| Package | Consumer version | Current layer peer range | Suggested supported majors, after testing |
|
|
8
|
+
| ---------------- | ---------------- | ------------------------ | ----------------------------------------- |
|
|
9
|
+
| `@nuxtjs/seo` | `6.0.1` | `^5.0.0` | 5 and 6 |
|
|
10
|
+
| `@vueuse/nuxt` | `15.0.0` | `^14.3.0` | 14 and 15 |
|
|
11
|
+
| `better-sqlite3` | `13.0.3` | `^12.10.0` | 12 and 13 |
|
|
12
|
+
| `pinia` | `4.1.0` | `^3.0.4` | 3 and 4 |
|
|
13
|
+
|
|
14
|
+
Suggested `peerDependencies` values:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"@nuxtjs/seo": "^5.0.0 || ^6.0.0",
|
|
19
|
+
"@vueuse/nuxt": "^14.3.0 || ^15.0.0",
|
|
20
|
+
"better-sqlite3": "^12.10.0 || ^13.0.0",
|
|
21
|
+
"pinia": "^3.0.4 || ^4.1.0"
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
These ranges are proposals, not proof of compatibility. A peer range is a support promise: widen it only after the layer code and tests pass against the new major. `@pinia/nuxt@1.0.3` itself requires `pinia@^4.1.0`, so the consumer's Pinia 4 pairing is intentional.
|
|
26
|
+
|
|
27
|
+
## Recommended Fix
|
|
28
|
+
|
|
29
|
+
1. In the layers repo root `package.json`, update the four entries under `peerDependencies` to the proposed ranges above. Keep their existing `peerDependenciesMeta` optional flags; these packages are optional for consumers that do not use those layers.
|
|
30
|
+
2. Align the layers repo's development catalog and test dependencies with the versions being claimed. In particular, test Pinia 4 together with `@pinia/nuxt` 1, not the existing Pinia 3 test setup.
|
|
31
|
+
3. Run the affected layer checks and tests, then build the playground with the SEO, VueUse, and database layers enabled. Check both the currently supported older majors and the new majors where practical. If an older major is no longer supported, remove it from the range and document the minimum instead of claiming compatibility.
|
|
32
|
+
4. Publish a patch release if the change only adds verified compatibility to the existing v5 contract. Use a breaking release if layer APIs or supported behavior must change.
|
|
33
|
+
5. In the frontend consumer, reinstall and run `pnpm peers check`. Do not silence these warnings with `allowedVersions` or overrides; those mechanisms do not establish runtime compatibility.
|
|
34
|
+
|
|
35
|
+
## Separate Transitive Warnings
|
|
36
|
+
|
|
37
|
+
The same consumer check also reported two warnings that do not come from `kmcom-nuxt-layers`' peer declarations:
|
|
38
|
+
|
|
39
|
+
| Package | Installed | Requested by `unctx@3.0.1` |
|
|
40
|
+
| ------------ | --------- | -------------------------- |
|
|
41
|
+
| `oxc-parser` | `0.139.0` | `>=0.140.0` |
|
|
42
|
+
| `unplugin` | `2.3.11` | `^3.3.0` |
|
|
43
|
+
|
|
44
|
+
Trace these with `pnpm why unctx` in the consumer and inspect the resolved dependency chain. Update the dependency that brings in `unctx` or move to a compatible `unctx` release; only use an override after confirming the affected packages support the overridden versions. Keep this investigation separate from the four layer peer-range changes.
|
|
45
|
+
|
|
46
|
+
## Runtime Note
|
|
47
|
+
|
|
48
|
+
`@nuxtjs/seo@6.0.1` declares Node support for `^22.22.3 || ^24.15.0 || >=26.0.0`. The frontend check used Node `24.22.0`, which is supported. Consumers on Node 25 or earlier Node 24 releases are outside that declared range and should not be considered covered by this upgrade.
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Migrating to Vite+ (nuxt-layers v5.0.0)
|
|
2
|
+
|
|
3
|
+
**Audience:** a developer or AI coding agent updating a downstream Nuxt app that consumes
|
|
4
|
+
`kmcom-nuxt-layers`, and wants the same toolchain this repo moved to in v5.0.0.
|
|
5
|
+
|
|
6
|
+
> Using the layers does **not** force you onto Vite+. The layers are plain Nuxt layers and keep
|
|
7
|
+
> working in an app on stock Vite/Vitest/Prettier. This guide is for apps that want to adopt the
|
|
8
|
+
> same toolchain. Sections 1–2 say what changed in the repo; sections 3–9 are the steps.
|
|
9
|
+
|
|
10
|
+
Work top to bottom. Each step has a **Detect** (does it apply?) and an **Apply**. Run the
|
|
11
|
+
**Verify** checklist at the end before calling it done. Where this guide and the repo disagree,
|
|
12
|
+
the repo wins — the reference files are listed at the bottom.
|
|
13
|
+
|
|
14
|
+
## 1. What Vite+ is, and what v5.0.0 changed
|
|
15
|
+
|
|
16
|
+
[Vite+](https://viteplus.dev) (`vp`, package `vite-plus`) is one CLI over the Vite ecosystem:
|
|
17
|
+
dev/build (Vite), test (Vitest), lint (oxlint), format (oxfmt), and a managed Node runtime.
|
|
18
|
+
|
|
19
|
+
In this repo the migration (`vp migrate`, then hand fixes) did:
|
|
20
|
+
|
|
21
|
+
| Area | Before | After |
|
|
22
|
+
| --------- | ---------------------------------- | -------------------------------------------------------------- |
|
|
23
|
+
| Vite | `vite: ^8` | `vite: npm:@voidzero-dev/vite-plus-core@1.2.0` (catalog alias) |
|
|
24
|
+
| Vitest | `vitest ^4` | `vitest 5.0.4` + `@vitest/coverage-v8 5.0.4` |
|
|
25
|
+
| Test API | `import … from 'vitest'` | `import … from 'vite-plus/test'` |
|
|
26
|
+
| Formatter | Prettier + 6 plugins | oxfmt (`vp fmt`), config in `vite.config.ts` |
|
|
27
|
+
| Linter | ESLint only | `vp lint` (oxlint) chained before the per-package ESLint |
|
|
28
|
+
| Scripts | `vitest run`, `prettier --write .` | `vp test run`, `vp fmt .` |
|
|
29
|
+
| Node file | `.nvmrc` | `.node-version` |
|
|
30
|
+
| pnpm | 11 | 12 (`packageManager: pnpm@12.10.1`) |
|
|
31
|
+
| `nuxt` | `latest` | pinned `4.6.1` |
|
|
32
|
+
|
|
33
|
+
**Breaking for consumers:** Node 24 only (`engines: >=24 <25`), and the toolchain/script names
|
|
34
|
+
above. The layers' runtime behaviour is unchanged.
|
|
35
|
+
|
|
36
|
+
## 2. Preconditions
|
|
37
|
+
|
|
38
|
+
- Node 24 (`node -v`). Vite+ can manage this for you via `.node-version`.
|
|
39
|
+
- pnpm 12 (`corepack enable && corepack use pnpm@12`). pnpm 10/11 will not read the new settings
|
|
40
|
+
locations in step 4.
|
|
41
|
+
- A clean git tree, on a new branch. `vp migrate` rewrites many files.
|
|
42
|
+
|
|
43
|
+
## 3. Install Vite+ and run the migration
|
|
44
|
+
|
|
45
|
+
**Detect:** your `package.json` has `vite`, `vitest`, or `prettier` in `devDependencies`.
|
|
46
|
+
|
|
47
|
+
**Apply:**
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git switch -c chore/migrate-to-vite-plus
|
|
51
|
+
vp migrate # rewrites vite/vitest deps, test imports, scripts, prettier → oxfmt
|
|
52
|
+
pnpm install
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`vp migrate` handles the mechanical parts: dependency swap, `vitest` → `vite-plus/test` imports,
|
|
56
|
+
script renames, `.nvmrc` → `.node-version`. Review the diff before moving on — it is the source
|
|
57
|
+
of truth for what changed in _your_ app.
|
|
58
|
+
|
|
59
|
+
If you use a pnpm catalog, point `vite` / `vitest` / `@vitest/coverage-v8` at the Vite+ entries
|
|
60
|
+
(see the table in section 1) and add overrides so transitive deps resolve to the same copy:
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
# pnpm-workspace.yaml
|
|
64
|
+
overrides:
|
|
65
|
+
vite@*: 'catalog:'
|
|
66
|
+
vitest@*: 'catalog:'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 4. pnpm 12 settings (don't skip)
|
|
70
|
+
|
|
71
|
+
**Detect:** your `.npmrc` contains `shamefully-hoist` or `strict-peer-dependencies`.
|
|
72
|
+
|
|
73
|
+
pnpm 12 **ignores those keys in `.npmrc`**. Symptom: dev server dies with
|
|
74
|
+
`"extend" … no default export` (a CJS interop failure from a package that is no longer hoisted).
|
|
75
|
+
|
|
76
|
+
**Apply:** move them to `pnpm-workspace.yaml` (create it for single-package projects):
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
shamefullyHoist: true
|
|
80
|
+
strictPeerDependencies: false
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Keep `allowBuilds` for packages with install scripts (`esbuild`, `sharp`, `@parcel/watcher`, …).
|
|
84
|
+
|
|
85
|
+
## 5. Pin Nuxt (and the Vite builder patch)
|
|
86
|
+
|
|
87
|
+
**Detect:** your `nuxt` version is `latest` or a loose range.
|
|
88
|
+
|
|
89
|
+
This repo patches `@nuxt/vite-builder` (a `listenAndRestrict` `ENOENT` guard on the vite-node
|
|
90
|
+
socket chmod). A pnpm patch targets an **exact** version, so an unpinned `nuxt` silently breaks it
|
|
91
|
+
on the next release.
|
|
92
|
+
|
|
93
|
+
**Apply:**
|
|
94
|
+
|
|
95
|
+
1. Pin `nuxt` to an exact version (this repo: `4.6.1`).
|
|
96
|
+
2. Only if you hit `NUXT_B7018` on the vite-node socket, copy
|
|
97
|
+
`patches/@nuxt__vite-builder@4.6.1.patch` and register it:
|
|
98
|
+
|
|
99
|
+
```yaml
|
|
100
|
+
patchedDependencies:
|
|
101
|
+
'@nuxt/vite-builder@4.6.1': patches/@nuxt__vite-builder@4.6.1.patch
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
If you do not hit that error, you do not need the patch. Patches are **not** inherited from the
|
|
105
|
+
layers package — they only apply where declared.
|
|
106
|
+
|
|
107
|
+
## 6. Declare `typescript` in every app
|
|
108
|
+
|
|
109
|
+
**Detect:** `pnpm why typescript` shows two versions (e.g. 5.9.3 and 6.0.3), or
|
|
110
|
+
`@nuxt/test-utils` fails with
|
|
111
|
+
`Failed to resolve import "./diagnostics/core.js" from … nuxt-vitest-app-entry.js`.
|
|
112
|
+
|
|
113
|
+
pnpm builds a separate `nuxt` install per set of resolved peers. If an app doesn't declare
|
|
114
|
+
`typescript`, it can resolve a different TS than the rest of the workspace and get a `nuxt`
|
|
115
|
+
variant whose generated test entry can't find its own files.
|
|
116
|
+
|
|
117
|
+
**Apply:** add `"typescript": "catalog:"` (or the same range as the rest of the repo) to each
|
|
118
|
+
app's `devDependencies`, then `pnpm install`. Confirm one `nuxt` variant per app with
|
|
119
|
+
`ls -d node_modules/.pnpm/nuxt@*`.
|
|
120
|
+
|
|
121
|
+
## 7. Replace Prettier with oxfmt
|
|
122
|
+
|
|
123
|
+
**Detect:** `prettier`, `prettier-plugin-*`, `.prettierrc*`, or `prettier` scripts exist.
|
|
124
|
+
|
|
125
|
+
**Apply:**
|
|
126
|
+
|
|
127
|
+
- Delete Prettier and every plugin from `devDependencies`, and remove `eslint-plugin-prettier` /
|
|
128
|
+
`stylelint-prettier` from your ESLint and Stylelint configs.
|
|
129
|
+
- Put formatter options in `vite.config.ts` under `fmt` (this repo: no semicolons, single quotes,
|
|
130
|
+
ES5 trailing commas, 100 columns, `sortTailwindcss: {}`, `vueIndentScriptAndStyle: true`).
|
|
131
|
+
- Move `.prettierignore` entries into `fmt.ignorePatterns`.
|
|
132
|
+
- Scripts: `"format": "vp fmt ."`, `"format:check": "vp fmt --check ."`.
|
|
133
|
+
- Run `vp fmt .` once as its own commit (`style: format with oxfmt`) so it stays out of review diffs.
|
|
134
|
+
|
|
135
|
+
Expect small differences from Prettier output (quote style in CSS `@import`, JSON line widths).
|
|
136
|
+
Reformatting generated baselines or lockfiles is churn — ignore them.
|
|
137
|
+
|
|
138
|
+
## 8. Lint: oxlint first, ESLint second
|
|
139
|
+
|
|
140
|
+
**Apply:**
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
// vite.config.ts
|
|
144
|
+
import { defineConfig } from 'vite-plus'
|
|
145
|
+
|
|
146
|
+
export default defineConfig({
|
|
147
|
+
lint: {
|
|
148
|
+
jsPlugins: [{ name: 'vite-plus', specifier: 'vite-plus/oxlint-plugin' }],
|
|
149
|
+
rules: { 'vite-plus/prefer-vite-plus-imports': 'error' },
|
|
150
|
+
// Nuxt auto-imports are invisible to oxlint's type analysis — keep these off
|
|
151
|
+
// or every `ref`/`computed`/`useFetch` is reported as undefined.
|
|
152
|
+
options: { typeAware: false, typeCheck: false },
|
|
153
|
+
},
|
|
154
|
+
})
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```jsonc
|
|
158
|
+
// package.json
|
|
159
|
+
"lint": "vp lint && turbo run lint", // or just `vp lint && eslint .` without Turbo
|
|
160
|
+
"lint:fix": "vp lint --fix && turbo run lint -- --fix"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`vp lint` is the built-in oxlint with rules from `vite.config.ts`; `pnpm lint` runs your
|
|
164
|
+
`package.json` script. They are different commands.
|
|
165
|
+
|
|
166
|
+
### Stylelint with Tailwind v4
|
|
167
|
+
|
|
168
|
+
If you run Stylelint, these false positives are expected with Tailwind v4 and layer CSS:
|
|
169
|
+
|
|
170
|
+
- `at-rule-no-unknown` / `no-invalid-position-at-import-rule`: ignore `layer`, `config`,
|
|
171
|
+
`custom-variant`, `source`, `plugin`, `utility`, `variant`.
|
|
172
|
+
- `no-invalid-position-declaration`: off for `.vue`.
|
|
173
|
+
- `value-keyword-case`: ignore font properties and `--*font*` custom properties.
|
|
174
|
+
- `declaration-no-important`: allow only in files that implement user-preference overrides
|
|
175
|
+
(reduced motion / transparency) that must beat utilities and inline styles. Scope it with an
|
|
176
|
+
`overrides` entry — do not disable it globally.
|
|
177
|
+
- Ignore `**/dist/**`, `**/.output/**`, `**/.nuxt/**`.
|
|
178
|
+
|
|
179
|
+
## 9. Tests
|
|
180
|
+
|
|
181
|
+
**Apply:**
|
|
182
|
+
|
|
183
|
+
- `vitest` imports → `vite-plus/test` (done by `vp migrate`; `vite-plus/prefer-vite-plus-imports`
|
|
184
|
+
keeps it that way).
|
|
185
|
+
- Scripts: `"test": "vp test run"`, `"test:watch": "vp test"`, `"test:coverage": "vp test run --coverage"`.
|
|
186
|
+
- If tests relied on mock call history surviving between tests, keep
|
|
187
|
+
`test: { clearMocks: false }` in `vite.config.ts` (newer Vitest versions clear mocks by default).
|
|
188
|
+
- `@nuxt/test-utils` projects (`defineVitestProject`) keep working; see step 6 if they fail to load.
|
|
189
|
+
- Playwright: the repo's Playwright wants its own browser build. Run
|
|
190
|
+
`pnpm exec playwright install chromium` (not a globally installed Playwright) before e2e.
|
|
191
|
+
Stop dev servers with `pkill -f "nuxt dev"` **and** delete a stale
|
|
192
|
+
`apps/<app>/.nuxt/nuxt.lock` if the next start says "Another Nuxt dev server is already running".
|
|
193
|
+
|
|
194
|
+
## 10. Nuxt-specific issues you may meet
|
|
195
|
+
|
|
196
|
+
These came up during this migration and are independent of Vite+ itself:
|
|
197
|
+
|
|
198
|
+
- **Hydration mismatches** after changing theme state: don't read `localStorage` during plugin
|
|
199
|
+
setup — apply stored prefs in `useNuxtApp().hook('app:mounted', …)`, and keep per-page state in
|
|
200
|
+
`useState`. A Playwright spec that fails on any `hydrat` console message is a cheap guard
|
|
201
|
+
(see `tests/playwright/hydration.spec.ts`).
|
|
202
|
+
- **`NUXT_E4004` / "Component inside `<Transition>` renders non-element root node"**: a page root
|
|
203
|
+
that is a fragment (multiple roots, a root-level HTML comment, or a renderless provider like
|
|
204
|
+
`ShaderRuntime`). Wrap in `<div class="contents">` — `display: contents` generates no box, so
|
|
205
|
+
grid children still lay out as direct children of the parent grid.
|
|
206
|
+
- **jiti warnings for layer configs**: use explicit `.ts` extensions in layer `nuxt.config.ts`
|
|
207
|
+
relative imports.
|
|
208
|
+
- **Dev prebundling**: this repo adds `@vue/devtools-core` to `vite.optimizeDeps.include`
|
|
209
|
+
(see `apps/playground/nuxt.config.ts`) so dev doesn't re-optimise it on first load. Only needed
|
|
210
|
+
if you see repeated dependency re-optimisation reloads.
|
|
211
|
+
|
|
212
|
+
## Verify checklist
|
|
213
|
+
|
|
214
|
+
Run all of these; every one should be clean.
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
pnpm install --frozen-lockfile # lockfile consistent
|
|
218
|
+
vp fmt --check . # formatting
|
|
219
|
+
command vp lint # oxlint, 0 warnings
|
|
220
|
+
pnpm lint # oxlint + ESLint across packages
|
|
221
|
+
pnpm typecheck # vue-tsc
|
|
222
|
+
vp test run # unit / nuxt-env tests
|
|
223
|
+
pnpm test:e2e # Playwright (needs matching chromium)
|
|
224
|
+
pnpm build # production build
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Also: `ls -d node_modules/.pnpm/nuxt@*` shows one variant per app, `pnpm why typescript` shows one
|
|
228
|
+
version, and the dev server starts with no `no default export` error.
|
|
229
|
+
|
|
230
|
+
## Rollback
|
|
231
|
+
|
|
232
|
+
The migration is mechanical and lives on one branch. To back out: `git revert` the migration and
|
|
233
|
+
format commits, restore `.nvmrc`, and `pnpm install`. Nothing in the layers package requires
|
|
234
|
+
Vite+, so rolling back does not break layer consumption.
|
|
235
|
+
|
|
236
|
+
## Reference files in this repo
|
|
237
|
+
|
|
238
|
+
| Topic | File |
|
|
239
|
+
| ---------------------- | ----------------------------------------- |
|
|
240
|
+
| Vite+ config | `vite.config.ts` |
|
|
241
|
+
| Catalog / overrides | `pnpm-workspace.yaml` |
|
|
242
|
+
| Scripts | `package.json` |
|
|
243
|
+
| Builder patch | `patches/@nuxt__vite-builder@4.6.1.patch` |
|
|
244
|
+
| Stylelint overrides | `stylelint.config.mjs` |
|
|
245
|
+
| Hydration spec | `tests/playwright/hydration.spec.ts` |
|
|
246
|
+
| Migration PR / release | PR #57, #58; release `v5.0.0` |
|
|
@@ -1146,7 +1146,7 @@ Column count is now emergent (native `auto-fill`), so there's nothing left to tr
|
|
|
1146
1146
|
|
|
1147
1147
|
<template>
|
|
1148
1148
|
<Teleport to="body">
|
|
1149
|
-
<div v-if="visible" :style class="layout-grid-debug
|
|
1149
|
+
<div v-if="visible" :style class="layout-grid-debug fixed inset-0 z-9999" aria-hidden="true" />
|
|
1150
1150
|
</Teleport>
|
|
1151
1151
|
</template>
|
|
1152
1152
|
```
|
|
@@ -1208,7 +1208,7 @@ The old page documented the 6/12/18 preset system in detail; that entire API is
|
|
|
1208
1208
|
<div class="flex items-center gap-4">
|
|
1209
1209
|
<UButton to="/" variant="ghost" icon="i-lucide-arrow-left" />
|
|
1210
1210
|
<div>
|
|
1211
|
-
<h1 class="text-3xl font-bold
|
|
1211
|
+
<h1 class="text-highlighted text-3xl font-bold">Layout Layer</h1>
|
|
1212
1212
|
<p class="text-muted">Fluid grid, sections, and page components</p>
|
|
1213
1213
|
</div>
|
|
1214
1214
|
<div class="ml-auto">
|
|
@@ -1232,14 +1232,14 @@ The old page documented the 6/12/18 preset system in detail; that entire API is
|
|
|
1232
1232
|
:span2="cell.span2"
|
|
1233
1233
|
>
|
|
1234
1234
|
<h3 class="font-mono text-sm">Cell {{ cell.id }}</h3>
|
|
1235
|
-
<p class="text-
|
|
1235
|
+
<p class="text-muted text-sm">variant: {{ cell.variant }}</p>
|
|
1236
1236
|
</LayoutCell>
|
|
1237
1237
|
</div>
|
|
1238
1238
|
</section>
|
|
1239
1239
|
|
|
1240
1240
|
<LayoutSection align="split" class="mt-12">
|
|
1241
1241
|
<div class="flex items-center justify-center">
|
|
1242
|
-
<div class="
|
|
1242
|
+
<div class="space-y-2 text-center">
|
|
1243
1243
|
<h2 class="text-2xl font-bold">LayoutSection align="split"</h2>
|
|
1244
1244
|
<p class="text-muted">
|
|
1245
1245
|
100svh block, optical 40/60 padding split derived from --lines/--fields.
|
|
@@ -1249,7 +1249,7 @@ The old page documented the 6/12/18 preset system in detail; that entire API is
|
|
|
1249
1249
|
</LayoutSection>
|
|
1250
1250
|
|
|
1251
1251
|
<LayoutSection align="center">
|
|
1252
|
-
<div class="
|
|
1252
|
+
<div class="space-y-2 text-center">
|
|
1253
1253
|
<h2 class="text-2xl font-bold">LayoutSection align="center"</h2>
|
|
1254
1254
|
<p class="text-muted">
|
|
1255
1255
|
place-content: center — true horizontal+vertical centering, no fighting align-items:
|
|
@@ -1268,7 +1268,7 @@ The old page documented the 6/12/18 preset system in detail; that entire API is
|
|
|
1268
1268
|
|
|
1269
1269
|
<section class="space-y-8">
|
|
1270
1270
|
<div>
|
|
1271
|
-
<h2 class="text-2xl font-bold
|
|
1271
|
+
<h2 class="mb-2 text-2xl font-bold">Z-Index System</h2>
|
|
1272
1272
|
<p class="text-muted">
|
|
1273
1273
|
Unchanged from the old system — read via
|
|
1274
1274
|
<code class="font-mono text-sm">useGridConfig().useZIndex(layer)</code>
|
|
@@ -1276,13 +1276,13 @@ The old page documented the 6/12/18 preset system in detail; that entire API is
|
|
|
1276
1276
|
</div>
|
|
1277
1277
|
<UCard>
|
|
1278
1278
|
<pre
|
|
1279
|
-
class="bg-muted text-highlighted
|
|
1279
|
+
class="bg-muted text-highlighted overflow-x-auto rounded-lg p-4 text-sm"
|
|
1280
1280
|
><code>const { useZIndex } = useGridConfig()
|
|
1281
1281
|
const zModal = useZIndex('modal') // → 400</code></pre>
|
|
1282
1282
|
</UCard>
|
|
1283
1283
|
</section>
|
|
1284
1284
|
|
|
1285
|
-
<div class="flex gap-4
|
|
1285
|
+
<div class="flex justify-center gap-4 pt-4">
|
|
1286
1286
|
<UButton to="/ui" variant="outline" icon="i-lucide-arrow-left">UI Layer Demo</UButton>
|
|
1287
1287
|
<UButton to="/" icon="i-lucide-home">Back to Home</UButton>
|
|
1288
1288
|
<UButton to="/core" variant="outline" trailing-icon="i-lucide-arrow-right">
|
|
@@ -52,7 +52,7 @@ regardless of layer), so `apps/playground/app/pages/baseline.vue` and `index.vue
|
|
|
52
52
|
playground. `app.config.ts`, `feed-catalog.ts` (collection resolution, missing-collection
|
|
53
53
|
warnings), `feed-head.ts` plugin (auto-discovery `<link rel="alternate">` tags), `feed-service.ts`,
|
|
54
54
|
all `server/routes/feed/**` handlers (sampled `rss.get.ts`, `index.get.ts` — consistent), `Feeds/
|
|
55
|
-
Index.vue`, `Feeds/RouteCard.vue` — real, thorough, already well-tested
|
|
55
|
+
Index.vue`, `Feeds/RouteCard.vue` — real, thorough, already well-tested
|
|
56
56
|
(`content-adapter.test.ts`, `feed-author.test.ts`, `feed-config.test.ts`, `formats.test.ts`),
|
|
57
57
|
consumed by `apps/playground/app/pages/feeds.vue`.
|
|
58
58
|
|
|
@@ -64,11 +64,11 @@ versa.
|
|
|
64
64
|
The artifact leaves the exact alias set open (§13). Resolved here:
|
|
65
65
|
|
|
66
66
|
- Aliases are expressed in a fixed **12-unit vocabulary**: `full=12,
|
|
67
|
-
half=6, third=4, quarter=3` — independent of the grid's live column
|
|
67
|
+
half=6, third=4, quarter=3` — independent of the grid's live column
|
|
68
68
|
count, the same mental model as Bootstrap-style 12-col systems.
|
|
69
69
|
- Because `columns` can be less than 12 at smaller breakpoints (4 at `xs`,
|
|
70
70
|
8 at `sm`/`md` per §1's default), an alias's rendered span is `min(alias
|
|
71
|
-
value, current column count)` — computed in the component, not CSS
|
|
71
|
+
value, current column count)` — computed in the component, not CSS
|
|
72
72
|
`calc()` (integer `grid-column: span N` can't come from a fractional
|
|
73
73
|
division). This keeps `half` always rendering as "half of whatever this
|
|
74
74
|
breakpoint's grid actually has," clamped so it never overflows a
|
|
@@ -85,7 +85,7 @@ New components, this layer, alongside `SwissGrid`/`SwissGridItem`:
|
|
|
85
85
|
For groups of items that wrap as a unit (tag lists, button rows,
|
|
86
86
|
metadata chips) — distinct from `SwissGrid`, which is column-track-based.
|
|
87
87
|
- **`Flow`** — vertical stack with automatic rhythm: renders `> * + *
|
|
88
|
-
{ margin-block-start: var(--fluid-space-<size>) }` scoped via a CSS
|
|
88
|
+
{ margin-block-start: var(--fluid-space-<size>) }` scoped via a CSS
|
|
89
89
|
class, `size` prop selecting the `core` fluid-space token. For prose-like
|
|
90
90
|
vertical sequences where every child needs the same gap, without each
|
|
91
91
|
child managing its own margin.
|
|
@@ -155,7 +155,7 @@ layers/structure/layout/app/components/
|
|
|
155
155
|
properties via `:style`.
|
|
156
156
|
- **`LayoutSection`** — see §4.
|
|
157
157
|
- **`LayoutCell`** (replaces `LayoutGridItem`) — `variant: 'block' |
|
|
158
|
-
'plate'` prop (border vs. filled, matching the prototype's `.block`/
|
|
158
|
+
'plate'` prop (border vs. filled, matching the prototype's `.block`/
|
|
159
159
|
`.plate`), `span2` / `span2Rows` / `span3Rows` boolean/enum props. No
|
|
160
160
|
`colStart`/`rowStart`/`preset`/`layer`/`bleed` — those go in the
|
|
161
161
|
migration guide as removed, with the nearest equivalent noted.
|
|
@@ -207,11 +207,11 @@ inspection.
|
|
|
207
207
|
resolved (narrow/medium viewport), squashing siblings. Fix:
|
|
208
208
|
`grid-auto-columns: minmax(measure-min, 1fr)` (belt-and-braces) plus
|
|
209
209
|
gating `.span-2` behind `@container viewport (min-width: 2 *
|
|
210
|
-
measure-min + gutter)` so it only activates once 2 columns have
|
|
210
|
+
measure-min + gutter)` so it only activates once 2 columns have
|
|
211
211
|
genuinely resolved.
|
|
212
212
|
3. **Hand-synced container-query thresholds.** `@container` conditions
|
|
213
213
|
can't reference custom properties — the `@container cell (max-width:
|
|
214
|
-
22rem)` / `@container viewport (min-width: 46rem)` thresholds must be
|
|
214
|
+
22rem)` / `@container viewport (min-width: 46rem)` thresholds must be
|
|
215
215
|
hand-kept in sync with `--measure-min`, commented at both ends.
|
|
216
216
|
|
|
217
217
|
## §8 Constraints to preserve
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<template>
|
|
21
21
|
<NuxtContentDetail collection="blog" :slug not-found-message="Blog post not found">
|
|
22
22
|
<template #headline="{ item }">
|
|
23
|
-
<div class="flex items-center gap-3 text-sm
|
|
23
|
+
<div class="text-muted flex items-center gap-3 text-sm">
|
|
24
24
|
<time v-if="asBlog(item).date">{{ formatDate(asBlog(item).date) }}</time>
|
|
25
25
|
<UBadge v-if="asBlog(item).badge" color="primary" variant="subtle">
|
|
26
26
|
{{ asBlog(item).badge }}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<NuxtContentDetail collection="gallery" :slug not-found-message="Gallery not found">
|
|
26
26
|
<template #headline="{ item }">
|
|
27
27
|
<div class="flex flex-wrap items-center gap-2">
|
|
28
|
-
<span v-if="asGallery(item).images?.length" class="text-
|
|
28
|
+
<span v-if="asGallery(item).images?.length" class="text-muted text-sm">
|
|
29
29
|
{{ asGallery(item).images!.length }} image{{
|
|
30
30
|
asGallery(item).images!.length === 1 ? '' : 's'
|
|
31
31
|
}}
|
|
@@ -49,30 +49,30 @@
|
|
|
49
49
|
<div
|
|
50
50
|
v-for="(img, idx) in asGallery(item).images"
|
|
51
51
|
:key="img.src"
|
|
52
|
-
class="overflow-hidden rounded-lg
|
|
52
|
+
class="group relative overflow-hidden rounded-lg"
|
|
53
53
|
>
|
|
54
|
-
<NuxtLink :to="`/gallery/${slug}/${idx}`" class="block
|
|
54
|
+
<NuxtLink :to="`/gallery/${slug}/${idx}`" class="relative block overflow-hidden">
|
|
55
55
|
<img
|
|
56
56
|
:src="img.src"
|
|
57
57
|
:alt="img.alt"
|
|
58
|
-
class="w-full
|
|
58
|
+
class="h-48 w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
59
59
|
/>
|
|
60
60
|
<div
|
|
61
|
-
class="absolute inset-0
|
|
61
|
+
class="bg-primary/60 absolute inset-0 flex items-center justify-center opacity-0 transition-opacity duration-300 group-hover:opacity-100"
|
|
62
62
|
>
|
|
63
|
-
<span class="
|
|
63
|
+
<span class="px-3 text-center text-sm font-semibold text-white">
|
|
64
64
|
{{ img.title || img.alt }}
|
|
65
65
|
</span>
|
|
66
66
|
</div>
|
|
67
67
|
</NuxtLink>
|
|
68
68
|
<button
|
|
69
|
-
class="absolute top-2 right-2 z-10
|
|
69
|
+
class="absolute top-2 right-2 z-10 cursor-pointer rounded-full bg-black/50 p-1.5 text-white opacity-0 transition-opacity duration-300 group-hover:opacity-100 hover:bg-black/70"
|
|
70
70
|
@click.stop="() => openLightbox(asGallery(item).images!, idx)"
|
|
71
71
|
>
|
|
72
72
|
<UIcon name="i-lucide-expand" class="size-4" />
|
|
73
73
|
</button>
|
|
74
74
|
<div v-if="img.caption" class="p-2">
|
|
75
|
-
<p class="text-
|
|
75
|
+
<p class="text-muted line-clamp-1 text-xs">
|
|
76
76
|
{{ img.caption }}
|
|
77
77
|
</p>
|
|
78
78
|
</div>
|
|
@@ -15,30 +15,30 @@
|
|
|
15
15
|
v-for="item in items"
|
|
16
16
|
:key="item.id"
|
|
17
17
|
:to="item.path"
|
|
18
|
-
class="group
|
|
18
|
+
class="group border-default block overflow-hidden rounded-lg border"
|
|
19
19
|
>
|
|
20
20
|
<div class="relative overflow-hidden">
|
|
21
21
|
<img
|
|
22
22
|
v-if="item.images?.[0]"
|
|
23
23
|
:src="item.images[0].src"
|
|
24
24
|
:alt="item.images[0].alt"
|
|
25
|
-
class="w-full
|
|
25
|
+
class="h-48 w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
26
26
|
/>
|
|
27
27
|
<div
|
|
28
|
-
class="absolute inset-0
|
|
28
|
+
class="bg-primary/60 absolute inset-0 flex items-center justify-center opacity-0 transition-opacity duration-300 group-hover:opacity-100"
|
|
29
29
|
>
|
|
30
|
-
<span class="text-
|
|
30
|
+
<span class="px-3 text-center font-semibold text-white">
|
|
31
31
|
{{ item.title }}
|
|
32
32
|
</span>
|
|
33
33
|
</div>
|
|
34
34
|
</div>
|
|
35
|
-
<div class="
|
|
36
|
-
<p class="font-medium
|
|
37
|
-
<p v-if="item.description" class="text-
|
|
35
|
+
<div class="space-y-1.5 p-3">
|
|
36
|
+
<p class="text-highlighted font-medium">{{ item.title }}</p>
|
|
37
|
+
<p v-if="item.description" class="text-muted line-clamp-2 text-sm">
|
|
38
38
|
{{ item.description }}
|
|
39
39
|
</p>
|
|
40
40
|
<div class="flex flex-wrap items-center gap-2">
|
|
41
|
-
<span v-if="item.images?.length" class="text-
|
|
41
|
+
<span v-if="item.images?.length" class="text-muted text-xs">
|
|
42
42
|
{{ item.images.length }} image{{ item.images.length === 1 ? '' : 's' }}
|
|
43
43
|
</span>
|
|
44
44
|
<UBadge v-for="tag in item.tags" :key="tag" color="neutral" variant="subtle" size="xs">
|
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
<!-- Ambient glow radiating from the image -->
|
|
44
44
|
<div
|
|
45
45
|
v-if="image.ambient"
|
|
46
|
-
class="absolute -inset-12 sm:-inset-20
|
|
46
|
+
class="pointer-events-none absolute -inset-12 sm:-inset-20"
|
|
47
47
|
aria-hidden="true"
|
|
48
48
|
>
|
|
49
49
|
<img
|
|
50
50
|
:src="image.src"
|
|
51
51
|
alt=""
|
|
52
|
-
class="
|
|
52
|
+
class="h-full w-full scale-x-200 object-cover opacity-40 bg-blend-luminosity mix-blend-darken blur-3xl saturate-300 dark:opacity-35 dark:mix-blend-lighten"
|
|
53
53
|
/>
|
|
54
54
|
<!-- class="w-full h-full object-cover rounded-3xl blur-3xl opacity-80 dark:opacity-30 saturate-200 scale-110 bg-blend-overlay dark:mix-blend-color" -->
|
|
55
55
|
</div>
|
|
@@ -63,13 +63,13 @@
|
|
|
63
63
|
/>
|
|
64
64
|
<figcaption
|
|
65
65
|
v-if="image.caption"
|
|
66
|
-
class="relative mt-3 text-center text-sm
|
|
66
|
+
class="text-muted relative mt-3 text-center text-sm italic"
|
|
67
67
|
>
|
|
68
68
|
{{ image.caption }}
|
|
69
69
|
</figcaption>
|
|
70
70
|
</figure>
|
|
71
71
|
|
|
72
|
-
<div class="flex items-center justify-between
|
|
72
|
+
<div class="mt-6 flex items-center justify-between">
|
|
73
73
|
<UButton
|
|
74
74
|
v-if="hasPrevious"
|
|
75
75
|
:to="`/gallery/${slug}/${index - 1}`"
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
</UButton>
|
|
81
81
|
<span v-else />
|
|
82
82
|
|
|
83
|
-
<span class="text-
|
|
83
|
+
<span class="text-muted text-sm"> {{ index + 1 }} / {{ totalImages }} </span>
|
|
84
84
|
|
|
85
85
|
<UButton
|
|
86
86
|
v-if="hasNext"
|
|
@@ -94,5 +94,5 @@
|
|
|
94
94
|
</div>
|
|
95
95
|
</UPageBody>
|
|
96
96
|
</div>
|
|
97
|
-
<div v-else class="text-muted text-center
|
|
97
|
+
<div v-else class="text-muted py-12 text-center">Image not found</div>
|
|
98
98
|
</template>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
:ui="{ content: 'bg-black/95 flex items-center justify-center' }"
|
|
51
51
|
>
|
|
52
52
|
<template #content>
|
|
53
|
-
<div class="relative
|
|
53
|
+
<div class="relative flex h-full w-full items-center justify-center p-4">
|
|
54
54
|
<!-- Close -->
|
|
55
55
|
<UButton
|
|
56
56
|
icon="i-lucide-x"
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
:alt="currentImage.alt"
|
|
83
83
|
:width="currentImage.width"
|
|
84
84
|
:height="currentImage.height"
|
|
85
|
-
class="max-
|
|
85
|
+
class="max-h-full max-w-full object-contain"
|
|
86
86
|
/>
|
|
87
87
|
|
|
88
88
|
<!-- Next -->
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
/>
|
|
98
98
|
|
|
99
99
|
<!-- Counter -->
|
|
100
|
-
<div class="absolute bottom-4 left-1/2 -translate-x-1/2 text-
|
|
100
|
+
<div class="absolute bottom-4 left-1/2 -translate-x-1/2 text-sm text-white">
|
|
101
101
|
{{ currentIndex + 1 }} / {{ images.length }}
|
|
102
102
|
</div>
|
|
103
103
|
</div>
|
|
@@ -58,6 +58,6 @@
|
|
|
58
58
|
<NuxtContentToc v-bind="item.body?.toc?.links ? { links: item.body.toc.links } : {}" />
|
|
59
59
|
</template>
|
|
60
60
|
</UPage>
|
|
61
|
-
<div v-else class="text-muted text-center
|
|
61
|
+
<div v-else class="text-muted py-12 text-center">{{ notFoundMessage }}</div>
|
|
62
62
|
</div>
|
|
63
63
|
</template>
|
|
@@ -16,18 +16,18 @@
|
|
|
16
16
|
|
|
17
17
|
<template>
|
|
18
18
|
<div v-if="colors.length" class="space-y-6">
|
|
19
|
-
<h3 class="text-2xl font-semibold tracking-tight
|
|
19
|
+
<h3 class="text-highlighted text-2xl font-semibold tracking-tight">Palette</h3>
|
|
20
20
|
<LinksGroup class="palette-grid gap-3">
|
|
21
21
|
<div v-for="color in colors" :key="color.value">
|
|
22
22
|
<div
|
|
23
|
-
class="aspect-2/3
|
|
23
|
+
class="border-default/20 flex aspect-2/3 cursor-default flex-col justify-between rounded-lg border p-4 shadow-lg"
|
|
24
24
|
:style="{ backgroundColor: color.value, color: contrastColor(color.value) }"
|
|
25
25
|
>
|
|
26
26
|
<div>
|
|
27
|
-
<p class="text-sm font-medium
|
|
28
|
-
<p v-if="color.usage" class="text-xs
|
|
27
|
+
<p class="text-sm leading-tight font-medium">{{ color.name }}</p>
|
|
28
|
+
<p v-if="color.usage" class="mt-1 text-xs opacity-60">{{ color.usage }}</p>
|
|
29
29
|
</div>
|
|
30
|
-
<p class="text-xs
|
|
30
|
+
<p class="font-mono text-xs uppercase opacity-50">{{ color.value }}</p>
|
|
31
31
|
</div>
|
|
32
32
|
</div>
|
|
33
33
|
</LinksGroup>
|