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.
Files changed (99) hide show
  1. package/docs/PEER-DEPENDENCY-MISMATCHES.md +48 -0
  2. package/docs/VITE-PLUS-MIGRATION.md +246 -0
  3. package/docs/superpowers/plans/2026-09-03-layout-layer-fluid-rebuild.md +8 -8
  4. package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-7-design.md +1 -1
  5. package/docs/superpowers/specs/2026-08-31-swiss-grid-layer-design.md +3 -3
  6. package/docs/superpowers/specs/2026-09-03-layout-layer-fluid-rebuild-design.md +3 -3
  7. package/layers/content/app/components/Blog/Article.vue +1 -1
  8. package/layers/content/app/components/Gallery/Detail.vue +8 -8
  9. package/layers/content/app/components/Gallery/Grid.vue +8 -8
  10. package/layers/content/app/components/Gallery/ImageDetail.vue +6 -6
  11. package/layers/content/app/components/Gallery/Lightbox.vue +3 -3
  12. package/layers/content/app/components/NuxtContent/Detail.vue +1 -1
  13. package/layers/content/app/components/NuxtContent/List.vue +1 -1
  14. package/layers/content/app/components/Portfolio/ColorPalette.vue +5 -5
  15. package/layers/content/app/components/Portfolio/Detail.vue +1 -1
  16. package/layers/content/app/components/Portfolio/List.vue +1 -1
  17. package/layers/content/app/components/Portfolio/Typography.vue +5 -5
  18. package/layers/content/app/components/content/Figure.vue +1 -1
  19. package/layers/core/app/assets/css/layout.css +1 -1
  20. package/layers/core/app/components/DiagnosticsPage.vue +15 -15
  21. package/layers/core/app/components/Element.test.ts +1 -1
  22. package/layers/core/app/components/ElementTw.test.ts +1 -1
  23. package/layers/core/app/components/ErrorBoundary.vue +5 -5
  24. package/layers/core/app/composables/useElementGrid.test.ts +1 -1
  25. package/layers/core/app/composables/useElementInteraction.test.ts +1 -1
  26. package/layers/core/app/composables/useElementLayout.test.ts +1 -1
  27. package/layers/core/app/composables/useElementSizing.test.ts +1 -1
  28. package/layers/core/app/composables/useElementSurface.test.ts +1 -1
  29. package/layers/core/app/composables/useLayoutAttrs.test.ts +1 -1
  30. package/layers/core/app/error.vue +1 -1
  31. package/layers/core/app/pages/[...slug].vue +2 -2
  32. package/layers/core/app/utils/browserInfo.test.ts +1 -1
  33. package/layers/core/app/utils/featureClasses.test.ts +1 -1
  34. package/layers/core/app/utils/scrollGuardClamp.test.ts +1 -1
  35. package/layers/core/app/utils/styleToTailwindClasses.test.ts +1 -1
  36. package/layers/data/forms/app/utils/contact-schema.test.ts +1 -1
  37. package/layers/data/forms/app/utils/fieldProps.test.ts +1 -1
  38. package/layers/data/metadata/providers/comicvine/server/utils/metadata-comicvine/normalise.test.ts +1 -1
  39. package/layers/data/metadata/providers/google-books/server/utils/metadata-google-books/client.test.ts +1 -1
  40. package/layers/data/metadata/providers/google-books/server/utils/metadata-google-books/normalise.test.ts +1 -1
  41. package/layers/data/metadata/providers/openlibrary/server/utils/metadata-openlibrary/client.test.ts +1 -1
  42. package/layers/data/metadata/providers/openlibrary/server/utils/metadata-openlibrary/normalise.test.ts +1 -1
  43. package/layers/data/metadata/providers/themoviedb/server/utils/metadata-tmdb/client.test.ts +1 -1
  44. package/layers/data/metadata/providers/themoviedb/server/utils/metadata-tmdb/normalise.test.ts +1 -1
  45. package/layers/data/metadata/server/utils/metadata/cache.test.ts +1 -1
  46. package/layers/data/metadata/server/utils/metadata/provider-registry.test.ts +1 -1
  47. package/layers/data/metadata/server/utils/metadata/search.test.ts +1 -1
  48. package/layers/delivery/feeds/app/assets/css/feeds.css +23 -11
  49. package/layers/delivery/feeds/app/components/Feeds/Index.vue +10 -10
  50. package/layers/delivery/feeds/app/components/Feeds/RouteCard.vue +1 -1
  51. package/layers/delivery/feeds/public/feed/style.css +16 -16
  52. package/layers/delivery/feeds/server/utils/content-adapter.test.ts +1 -1
  53. package/layers/delivery/feeds/server/utils/feed-author.test.ts +1 -1
  54. package/layers/delivery/feeds/server/utils/feed-config.test.ts +1 -1
  55. package/layers/delivery/feeds/server/utils/formats.test.ts +1 -1
  56. package/layers/delivery/scripts/app/components/BaselineStatus.vue +3 -1
  57. package/layers/delivery/seo/app/utils/seoConfig.test.ts +1 -1
  58. package/layers/design-system/theming/app/assets/css/theme.css +0 -1
  59. package/layers/design-system/theming/app/components/ThemePicker/AccentButton.vue +2 -2
  60. package/layers/design-system/theming/app/components/ThemePicker/Menu.vue +3 -3
  61. package/layers/design-system/theming/app/composables/useAccentColor.ts +4 -1
  62. package/layers/design-system/theming/app/plugins/theme.client.ts +15 -13
  63. package/layers/design-system/theming/app/utils/themeAdapter.ts +1 -1
  64. package/layers/design-system/theming/nuxt.config.ts +1 -1
  65. package/layers/design-system/theming/server/utils/accent-css.test.ts +1 -1
  66. package/layers/design-system/theming/server/utils/fouc-config.test.ts +1 -1
  67. package/layers/design-system/typography/app/components/Typography/TextStroke.vue +1 -1
  68. package/layers/design-system/visual/app/assets/css/visual.css +0 -1
  69. package/layers/design-system/visual/app/components/Accent/Scene.vue +2 -2
  70. package/layers/design-system/visual/app/components/Tint/Overlay.vue +1 -1
  71. package/layers/design-system/visual/app/utils/colorTokens.test.ts +1 -1
  72. package/layers/design-system/visual/app/utils/gradientStyle.test.ts +1 -1
  73. package/layers/design-system/visual/app/utils/responsiveSizes.test.ts +1 -1
  74. package/layers/motion/animations/app/components/Motion/Marquee.vue +1 -1
  75. package/layers/motion/animations/app/components/Motion/MarqueeText.vue +3 -3
  76. package/layers/motion/scroll/app/components/Motion/HorizontalScroll.vue +1 -1
  77. package/layers/motion/scroll/app/components/Motion/PinnedSection.vue +2 -2
  78. package/layers/motion/scroll/app/components/Motion/ScrollProgress.vue +2 -2
  79. package/layers/motion/scroll/app/components/Motion/ScrollStats.vue +6 -6
  80. package/layers/motion/transitions/app/assets/css/transitions.css +12 -12
  81. package/layers/render/canvas/nuxt.config.ts +1 -1
  82. package/layers/render/canvas/package.json +2 -1
  83. package/layers/render/shader/app/assets/css/shader.css +5 -5
  84. package/layers/render/shader/app/shaders/common/grain.ts +1 -1
  85. package/layers/render/shader/app/types/tsl.ts +1 -1
  86. package/layers/render/shader/app/utils/tsl/index.ts +2 -0
  87. package/layers/structure/grid/app/assets/css/grid.css +2 -0
  88. package/layers/structure/grid/app/composables/useGridTracks.test.ts +1 -1
  89. package/layers/structure/grid/app/types/tracks.test.ts +1 -1
  90. package/layers/structure/grid/app/utils/placement.test.ts +1 -1
  91. package/layers/structure/layout/app/components/Layout/Grid/Debug.vue +1 -1
  92. package/layers/structure/layout/app/components/Layout/Page/Header.vue +3 -3
  93. package/layers/structure/layout/app/components/Layout/Page/index.vue +16 -11
  94. package/layers/structure/layout/app/utils/gridPlacementStyle.test.ts +1 -1
  95. package/layers/structure/navigation/app/components/Mast/Nav.vue +1 -1
  96. package/layers/structure/navigation/app/components/Mast/NavModal.vue +2 -2
  97. package/layers/structure/navigation/app/utils/site.test.ts +1 -1
  98. package/layers/structure/routing/app/utils/resolveRoute.test.ts +1 -1
  99. package/package.json +26 -45
@@ -12,13 +12,13 @@
12
12
  }
13
13
 
14
14
  :root {
15
- --feed-bg: var(--ui-bg, #fff);
16
- --feed-bg-elevated: var(--ui-bg-elevated, #f8fafc);
17
- --feed-bg-accented: var(--ui-bg-accented, #f1f5f9);
18
- --feed-border: var(--ui-border, #e2e8f0);
19
- --feed-text: var(--ui-text, #0f172a);
20
- --feed-text-muted: var(--ui-text-muted, #64748b);
21
- --feed-text-toned: var(--ui-text-toned, #475569);
15
+ --feed-bg: var(--ui-bg, oklch(100% 0 0deg));
16
+ --feed-bg-elevated: var(--ui-bg-elevated, oklch(98.42% 0.0034 247.86deg));
17
+ --feed-bg-accented: var(--ui-bg-accented, oklch(96.83% 0.0069 247.9deg));
18
+ --feed-border: var(--ui-border, oklch(92.88% 0.0126 255.51deg));
19
+ --feed-text: var(--ui-text, oklch(20.77% 0.0398 265.75deg));
20
+ --feed-text-muted: var(--ui-text-muted, oklch(55.44% 0.0407 257.42deg));
21
+ --feed-text-toned: var(--ui-text-toned, oklch(44.55% 0.0374 257.28deg));
22
22
  --feed-primary: var(--ui-color-primary-500, oklch(62.3% 0.214 259.5deg));
23
23
  --feed-primary-text: var(--ui-color-primary-600, oklch(54.6% 0.214 259.5deg));
24
24
  --feed-primary-bg: var(--ui-color-primary-50, oklch(97% 0.03 259.5deg));
@@ -28,13 +28,13 @@
28
28
 
29
29
  @media (prefers-color-scheme: dark) {
30
30
  :root {
31
- --feed-bg: var(--ui-bg, #020617);
32
- --feed-bg-elevated: var(--ui-bg-elevated, #0f172a);
33
- --feed-bg-accented: var(--ui-bg-accented, #1e293b);
34
- --feed-border: var(--ui-border, #1e293b);
35
- --feed-text: var(--ui-text, #f8fafc);
36
- --feed-text-muted: var(--ui-text-muted, #94a3b8);
37
- --feed-text-toned: var(--ui-text-toned, #cbd5e1);
31
+ --feed-bg: var(--ui-bg, oklch(12.88% 0.0406 264.7deg));
32
+ --feed-bg-elevated: var(--ui-bg-elevated, oklch(20.77% 0.0398 265.75deg));
33
+ --feed-bg-accented: var(--ui-bg-accented, oklch(27.95% 0.0368 260.03deg));
34
+ --feed-border: var(--ui-border, oklch(27.95% 0.0368 260.03deg));
35
+ --feed-text: var(--ui-text, oklch(98.42% 0.0034 247.86deg));
36
+ --feed-text-muted: var(--ui-text-muted, oklch(71.07% 0.0351 256.79deg));
37
+ --feed-text-toned: var(--ui-text-toned, oklch(86.9% 0.0198 252.89deg));
38
38
  --feed-primary: var(--ui-color-primary-400, oklch(71% 0.18 259.5deg));
39
39
  --feed-primary-text: var(--ui-color-primary-400, oklch(71% 0.18 259.5deg));
40
40
  --feed-primary-bg: var(--ui-color-primary-950, oklch(22% 0.06 259.5deg));
@@ -160,7 +160,7 @@ a:hover {
160
160
  border-radius: var(--feed-radius);
161
161
  background-color: var(--feed-primary);
162
162
  padding: 0.5rem 1.125rem;
163
- color: #fff;
163
+ color: oklch(100% 0 0deg);
164
164
  font-weight: 600;
165
165
  font-size: 0.875rem;
166
166
  line-height: 1;
@@ -169,7 +169,7 @@ a:hover {
169
169
 
170
170
  .feed-subscribe:hover {
171
171
  opacity: 0.85;
172
- color: #fff;
172
+ color: oklch(100% 0 0deg);
173
173
  text-decoration: none;
174
174
  }
175
175
 
@@ -1,5 +1,5 @@
1
1
  import type { H3Event } from 'h3'
2
- import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { beforeEach, describe, expect, it, vi } from 'vite-plus/test'
3
3
 
4
4
  import { getContentFeedItems } from './content-adapter'
5
5
 
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { describe, expect, it } from 'vite-plus/test'
2
2
 
3
3
  import { resolveFeedAuthor, resolveFeedDate } from './feed-author'
4
4
 
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { describe, expect, it } from 'vite-plus/test'
2
2
 
3
3
  import {
4
4
  createFeedConfig,
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { describe, expect, it } from 'vite-plus/test'
2
2
 
3
3
  import { toAtom } from './formats/atom'
4
4
  import { toJSONFeed } from './formats/json'
@@ -10,7 +10,9 @@
10
10
  },
11
11
  },
12
12
  setup(props) {
13
- if (import.meta.server) return () => null
13
+ // Render the custom element on server AND client. Returning null on the server
14
+ // produced a comment node there but an element on the client (hydration mismatch);
15
+ // an unregistered custom element is inert until the client script upgrades it.
14
16
  return () => h('baseline-status', { featureId: props.featureId })
15
17
  },
16
18
  })
@@ -1,5 +1,5 @@
1
1
  import type { SiteConfig } from '#layers/core/app/types/site'
2
- import { describe, expect, it } from 'vitest'
2
+ import { describe, expect, it } from 'vite-plus/test'
3
3
 
4
4
  import { resolveSeoConfig } from './seoConfig'
5
5
 
@@ -36,7 +36,6 @@
36
36
 
37
37
  [data-theme-transparency='reduced'] :where([class*='backdrop-blur']) {
38
38
  backdrop-filter: none !important;
39
- backdrop-filter: none !important;
40
39
  }
41
40
 
42
41
  [data-theme-transparency='reduced'] :where([class*='bg-default/'], [class*='bg-elevated/']) {
@@ -21,8 +21,8 @@
21
21
  class="size-8 rounded-full ring-1 ring-offset-2 transition-shadow duration-200"
22
22
  :class="[
23
23
  active
24
- ? 'ring-2 ring-primary ring-offset-bg shadow-lg'
25
- : 'ring-transparent hover:ring-muted ring-offset-bg hover:shadow-md',
24
+ ? 'ring-primary ring-offset-bg shadow-lg ring-2'
25
+ : 'hover:ring-muted ring-offset-bg ring-transparent hover:shadow-md',
26
26
  ]"
27
27
  :style="bgStyle"
28
28
  :aria-label="`Set accent color to ${color}`"
@@ -34,7 +34,7 @@
34
34
  <li class="flex w-full flex-row items-center justify-between py-1">
35
35
  <div>
36
36
  <h2 class="text-lg font-semibold">High Contrast</h2>
37
- <p v-if="contrastOverride === 'system'" class="text-sm text-muted">
37
+ <p v-if="contrastOverride === 'system'" class="text-muted text-sm">
38
38
  Following system
39
39
  </p>
40
40
  </div>
@@ -49,7 +49,7 @@
49
49
  <li class="flex w-full flex-row items-center justify-between py-1">
50
50
  <div>
51
51
  <h2 class="text-lg font-semibold">Reduce Motion</h2>
52
- <p v-if="motionOverride === 'system'" class="text-sm text-muted">
52
+ <p v-if="motionOverride === 'system'" class="text-muted text-sm">
53
53
  Following system
54
54
  </p>
55
55
  </div>
@@ -64,7 +64,7 @@
64
64
  <li class="flex w-full flex-row items-center justify-between py-1">
65
65
  <div>
66
66
  <h2 class="text-lg font-semibold">Reduce Transparency</h2>
67
- <p v-if="transparencyOverride === 'system'" class="text-sm text-muted">
67
+ <p v-if="transparencyOverride === 'system'" class="text-muted text-sm">
68
68
  Following system
69
69
  </p>
70
70
  </div>
@@ -13,7 +13,10 @@ export const useAccentColor = createSharedComposable(() => {
13
13
  // theme.client.ts reads localStorage after hydration and calls setAccent.
14
14
  const accent = useState<AccentColor>('theme-accent', () => defaultAccent)
15
15
 
16
- const pageAccent = ref<AccentColor | null>(null)
16
+ // Page-scoped override. Must be useState (not a module-level ref): pages call
17
+ // setPageAccent() during setup, and a plain ref is not carried in the SSR payload,
18
+ // so the server rendered the default accent while the client had the page's.
19
+ const pageAccent = useState<AccentColor | null>('theme-page-accent', () => null)
17
20
  const activeAccent = computed<AccentColor>(() => pageAccent.value ?? accent.value)
18
21
 
19
22
  function setAccent(color: AccentColor) {
@@ -10,19 +10,21 @@ export default defineNuxtPlugin({
10
10
  const { setTransparencyOverride, effectiveReducedTransparency } = useThemeTransparency()
11
11
 
12
12
  // Restore persisted preferences. useState defaults were serialized from the
13
- // server, so hydration matches. We update to the stored values here, after
14
- // hydration completes, to avoid any mismatch.
15
- const storedAccent = localStorage.getItem('theme-colour')
16
- if (storedAccent) setAccent(storedAccent as AccentColor)
17
-
18
- const storedContrast = localStorage.getItem('theme-contrast')
19
- if (storedContrast) setContrastOverride(storedContrast as PreferenceOverride)
20
-
21
- const storedMotion = localStorage.getItem('theme-motion')
22
- if (storedMotion) setMotionOverride(storedMotion as PreferenceOverride)
23
-
24
- const storedTransparency = localStorage.getItem('theme-transparency')
25
- if (storedTransparency) setTransparencyOverride(storedTransparency as PreferenceOverride)
13
+ // server, so hydration matches only if client state is still at those defaults
14
+ // while hydrating. Plugin setup runs BEFORE hydration, so the stored values are
15
+ // applied on `app:mounted` (after hydration) instead of here.
16
+ useNuxtApp().hook('app:mounted', () => {
17
+ const restorers: Array<[key: string, apply: (value: string) => void]> = [
18
+ ['theme-colour', (v) => setAccent(v as AccentColor)],
19
+ ['theme-contrast', (v) => setContrastOverride(v as PreferenceOverride)],
20
+ ['theme-motion', (v) => setMotionOverride(v as PreferenceOverride)],
21
+ ['theme-transparency', (v) => setTransparencyOverride(v as PreferenceOverride)],
22
+ ]
23
+ for (const [key, apply] of restorers) {
24
+ const stored = localStorage.getItem(key)
25
+ if (stored) apply(stored)
26
+ }
27
+ })
26
28
 
27
29
  watch(
28
30
  activeAccent,
@@ -1,4 +1,4 @@
1
- import { ACCENT_PALETTES, type AccentName } from '../../server/utils/accent-css'
1
+ import { ACCENT_PALETTES, type AccentName } from '../../server/utils/accent-css.ts'
2
2
 
3
3
  /**
4
4
  * Derives Nuxt UI's `ui.colors` shape from the same accent-relationship table
@@ -1,5 +1,5 @@
1
1
  // Theme Layer - Color mode, accent colors, accessibility preferences
2
- import { resolveUiColors } from './app/utils/themeAdapter'
2
+ import { resolveUiColors } from './app/utils/themeAdapter.ts'
3
3
 
4
4
  declare module '@nuxt/schema' {
5
5
  interface PublicRuntimeConfig {
@@ -1,5 +1,5 @@
1
1
  import twColors from 'tailwindcss/colors'
2
- import { describe, expect, it } from 'vitest'
2
+ import { describe, expect, it } from 'vite-plus/test'
3
3
 
4
4
  import { buildAccentCSS } from './accent-css'
5
5
 
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { describe, expect, it } from 'vite-plus/test'
2
2
 
3
3
  import { buildThemeInitScript } from './fouc-config'
4
4
 
@@ -31,7 +31,7 @@
31
31
  v-bind="$attrs"
32
32
  >
33
33
  <!-- Sizer: transparent text for natural sizing + selectable -->
34
- <span class="text-stroke-sizer text-transparent whitespace-nowrap" aria-hidden="true">{{
34
+ <span class="text-stroke-sizer whitespace-nowrap text-transparent" aria-hidden="true">{{
35
35
  text
36
36
  }}</span>
37
37
 
@@ -1,5 +1,4 @@
1
1
  .gradient-text {
2
- background-clip: text;
3
2
  background-clip: text;
4
3
  color: transparent;
5
4
  }
@@ -25,8 +25,8 @@
25
25
  </script>
26
26
 
27
27
  <template>
28
- <component :is="tag" class="relative overflow-clip isolate">
29
- <div class="absolute inset-0 pointer-events-none" aria-hidden="true">
28
+ <component :is="tag" class="relative isolate overflow-clip">
29
+ <div class="pointer-events-none absolute inset-0" aria-hidden="true">
30
30
  <AccentBlob
31
31
  v-for="(blob, i) in resolvedBlobs"
32
32
  :key="i"
@@ -20,6 +20,6 @@
20
20
  <template>
21
21
  <component :is="tag" class="relative">
22
22
  <slot />
23
- <span class="absolute inset-0 pointer-events-none" :style aria-hidden="true" />
23
+ <span class="pointer-events-none absolute inset-0" :style aria-hidden="true" />
24
24
  </component>
25
25
  </template>
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { describe, expect, it } from 'vite-plus/test'
2
2
 
3
3
  import { resolveUiColorToken } from './colorTokens'
4
4
 
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { describe, expect, it } from 'vite-plus/test'
2
2
 
3
3
  import type { GradientConfig } from '../types/gradient'
4
4
  import { buildGradientStyle, resolveGradientColor } from './gradientStyle'
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { describe, expect, it } from 'vite-plus/test'
2
2
 
3
3
  import type { ResponsiveSizes } from '../types/media'
4
4
  import { buildResponsiveSizesQueries } from './responsiveSizes'
@@ -147,7 +147,7 @@
147
147
  >
148
148
  <div
149
149
  ref="contentRef"
150
- class="motion-marquee__content flex will-change-transform w-max"
150
+ class="motion-marquee__content flex w-max will-change-transform"
151
151
  :style="{ gap }"
152
152
  >
153
153
  <slot />
@@ -95,8 +95,8 @@
95
95
  onMounted(async () => {
96
96
  await nextTick()
97
97
 
98
- baseX.value = new Array(texts.length).fill(0)
99
- directionFactors.value = new Array(texts.length).fill(1)
98
+ baseX.value = Array.from({ length: texts.length }, () => 0)
99
+ directionFactors.value = Array.from({ length: texts.length }, () => 1)
100
100
  animLastTime = performance.now()
101
101
 
102
102
  const firstContainer = containerRef.value?.[0]
@@ -131,7 +131,7 @@
131
131
  :style="parallaxStyle"
132
132
  >
133
133
  <div
134
- :class="`${scrollerClassName} font-styles-logo flex py-2 text-center font-sans text-4xl font-medium tracking-[-0.02em] whitespace-nowrap text-neutral drop-shadow md:text-[5rem] md:leading-20`"
134
+ :class="`${scrollerClassName} font-styles-logo text-neutral flex py-2 text-center font-sans text-4xl font-medium tracking-[-0.02em] whitespace-nowrap drop-shadow md:text-[5rem] md:leading-20`"
135
135
  :style="{ transform: `translateX(${scrollTransforms[index] ?? '0px'})`, ...scrollerStyle }"
136
136
  >
137
137
  <span
@@ -57,7 +57,7 @@
57
57
 
58
58
  <template>
59
59
  <section ref="sectionRef" class="motion-horizontal-scroll h-screen overflow-hidden">
60
- <div ref="trackRef" class="h-full flex items-center will-change-transform">
60
+ <div ref="trackRef" class="flex h-full items-center will-change-transform">
61
61
  <slot />
62
62
  </div>
63
63
  </section>
@@ -72,8 +72,8 @@
72
72
  </script>
73
73
 
74
74
  <template>
75
- <section ref="sectionRef" class="motion-pinned-section h-screen relative">
76
- <div ref="contentRef" class="motion-pinned-content h-full flex items-center justify-center">
75
+ <section ref="sectionRef" class="motion-pinned-section relative h-screen">
76
+ <div ref="contentRef" class="motion-pinned-content flex h-full items-center justify-center">
77
77
  <slot />
78
78
  </div>
79
79
  </section>
@@ -49,7 +49,7 @@
49
49
  <!-- Linear Progress -->
50
50
  <div v-if="type === 'linear'" class="motion-scroll-progress-linear" v-bind="$attrs">
51
51
  <div
52
- class="w-full rounded-full overflow-hidden"
52
+ class="w-full overflow-hidden rounded-full"
53
53
  :style="{ height: `${height}px`, backgroundColor: bgColor }"
54
54
  >
55
55
  <div
@@ -60,7 +60,7 @@
60
60
  }"
61
61
  />
62
62
  </div>
63
- <span v-if="showPercentage" class="text-sm font-medium tabular-nums mt-2 block text-center">
63
+ <span v-if="showPercentage" class="mt-2 block text-center text-sm font-medium tabular-nums">
64
64
  {{ percentage }}%
65
65
  </span>
66
66
  </div>
@@ -51,7 +51,7 @@
51
51
 
52
52
  <template>
53
53
  <div
54
- class="bg-black/80 backdrop-blur-sm text-white rounded-xl transition-opacity duration-300"
54
+ class="rounded-xl bg-black/80 text-white backdrop-blur-sm transition-opacity duration-300"
55
55
  :class="[
56
56
  ...positionClasses,
57
57
  isScrolling ? 'opacity-100' : 'opacity-60',
@@ -61,31 +61,31 @@
61
61
  <div class="flex items-center" :class="[compact ? 'gap-4 text-xs' : 'gap-6 text-sm']">
62
62
  <!-- Scrolling indicator -->
63
63
  <div
64
- class="w-2 h-2 rounded-full transition-colors duration-100"
64
+ class="h-2 w-2 rounded-full transition-colors duration-100"
65
65
  :class="isScrolling ? 'bg-green-500' : 'bg-gray-600'"
66
66
  />
67
67
 
68
68
  <!-- Velocity -->
69
69
  <div v-if="show.includes('velocity')" class="text-center">
70
- <div v-if="!compact" class="text-gray-500 text-xs mb-0.5">VEL</div>
70
+ <div v-if="!compact" class="mb-0.5 text-xs text-gray-500">VEL</div>
71
71
  <div class="font-medium tabular-nums">{{ velocity.toFixed(2) }}</div>
72
72
  </div>
73
73
 
74
74
  <!-- Progress -->
75
75
  <div v-if="show.includes('progress')" class="text-center">
76
- <div v-if="!compact" class="text-gray-500 text-xs mb-0.5">PROG</div>
76
+ <div v-if="!compact" class="mb-0.5 text-xs text-gray-500">PROG</div>
77
77
  <div class="font-medium tabular-nums">{{ (progress * 100).toFixed(0) }}%</div>
78
78
  </div>
79
79
 
80
80
  <!-- Direction -->
81
81
  <div v-if="show.includes('direction')" class="text-center">
82
- <div v-if="!compact" class="text-gray-500 text-xs mb-0.5">DIR</div>
82
+ <div v-if="!compact" class="mb-0.5 text-xs text-gray-500">DIR</div>
83
83
  <div class="font-medium">{{ directionLabel }}</div>
84
84
  </div>
85
85
 
86
86
  <!-- Scroll Y -->
87
87
  <div v-if="show.includes('scrollY')" class="text-center">
88
- <div v-if="!compact" class="text-gray-500 text-xs mb-0.5">Y</div>
88
+ <div v-if="!compact" class="mb-0.5 text-xs text-gray-500">Y</div>
89
89
  <div class="font-medium tabular-nums">{{ Math.round(scrollY) }}px</div>
90
90
  </div>
91
91
  </div>
@@ -21,31 +21,31 @@
21
21
 
22
22
  /* Animation classes */
23
23
  .animate-fade-in {
24
- animation: fadeIn 0.3s ease-out forwards;
24
+ animation: fade-in 0.3s ease-out forwards;
25
25
  }
26
26
 
27
27
  .animate-fade-out {
28
- animation: fadeOut 0.3s ease-out forwards;
28
+ animation: fade-out 0.3s ease-out forwards;
29
29
  }
30
30
 
31
31
  .animate-slide-in-up {
32
- animation: slideInUp 0.3s ease-out forwards;
32
+ animation: slide-in-up 0.3s ease-out forwards;
33
33
  }
34
34
 
35
35
  .animate-slide-in-down {
36
- animation: slideInDown 0.3s ease-out forwards;
36
+ animation: slide-in-down 0.3s ease-out forwards;
37
37
  }
38
38
 
39
39
  .animate-slide-in-left {
40
- animation: slideInLeft 0.3s ease-out forwards;
40
+ animation: slide-in-left 0.3s ease-out forwards;
41
41
  }
42
42
 
43
43
  .animate-slide-in-right {
44
- animation: slideInRight 0.3s ease-out forwards;
44
+ animation: slide-in-right 0.3s ease-out forwards;
45
45
  }
46
46
 
47
47
  /* Keyframes */
48
- @keyframes fadeIn {
48
+ @keyframes fade-in {
49
49
  from {
50
50
  opacity: 0;
51
51
  }
@@ -55,7 +55,7 @@
55
55
  }
56
56
  }
57
57
 
58
- @keyframes fadeOut {
58
+ @keyframes fade-out {
59
59
  from {
60
60
  opacity: 1;
61
61
  }
@@ -65,7 +65,7 @@
65
65
  }
66
66
  }
67
67
 
68
- @keyframes slideInUp {
68
+ @keyframes slide-in-up {
69
69
  from {
70
70
  transform: translateY(20px);
71
71
  opacity: 0;
@@ -77,7 +77,7 @@
77
77
  }
78
78
  }
79
79
 
80
- @keyframes slideInDown {
80
+ @keyframes slide-in-down {
81
81
  from {
82
82
  transform: translateY(-20px);
83
83
  opacity: 0;
@@ -89,7 +89,7 @@
89
89
  }
90
90
  }
91
91
 
92
- @keyframes slideInLeft {
92
+ @keyframes slide-in-left {
93
93
  from {
94
94
  transform: translateX(-20px);
95
95
  opacity: 0;
@@ -101,7 +101,7 @@
101
101
  }
102
102
  }
103
103
 
104
- @keyframes slideInRight {
104
+ @keyframes slide-in-right {
105
105
  from {
106
106
  transform: translateX(20px);
107
107
  opacity: 0;
@@ -1,4 +1,4 @@
1
- import type { Plugin } from 'vite'
1
+ import type { Plugin } from 'vite-plus'
2
2
 
3
3
  /**
4
4
  * Intercepts three.webgpu.js in SSR via the transform hook.
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/three": "catalog:",
22
- "vite": "catalog:"
22
+ "vite": "catalog:",
23
+ "vite-plus": "catalog:"
23
24
  }
24
25
  }
@@ -12,17 +12,17 @@
12
12
  z-index: 9999;
13
13
  backdrop-filter: blur(8px);
14
14
  border-radius: 0.5rem;
15
- background: rgb(0 0 0 / 0.8);
15
+ background: oklch(0% 0 0deg / 0.8);
16
16
  padding: 0.75rem 1rem;
17
17
  min-width: 200px;
18
- color: #fff;
18
+ color: oklch(100% 0 0deg);
19
19
  font-size: 0.75rem;
20
20
  font-family: ui-monospace, monospace;
21
21
  }
22
22
 
23
23
  .shader-debug-title {
24
24
  margin-bottom: 0.5rem;
25
- color: #a78bfa;
25
+ color: oklch(70.9% 0.1592 293.54deg);
26
26
  font-weight: 600;
27
27
  }
28
28
 
@@ -33,9 +33,9 @@
33
33
  }
34
34
 
35
35
  .shader-debug-label {
36
- color: #94a3b8;
36
+ color: oklch(71.07% 0.0351 256.79deg);
37
37
  }
38
38
 
39
39
  .shader-debug-value {
40
- color: #22c55e;
40
+ color: oklch(72.27% 0.192 149.58deg);
41
41
  }
@@ -334,7 +334,7 @@ export function crtEffect(
334
334
 
335
335
  // 8x8 Bayer matrix values (normalized to 0-1 range)
336
336
  const BAYER_8X8_MATRIX = [
337
- 0.0 / 64.0,
337
+ 0, // 0 / 64
338
338
  48.0 / 64.0,
339
339
  12.0 / 64.0,
340
340
  60.0 / 64.0,
@@ -37,7 +37,7 @@ export type TSLMat4 = any
37
37
 
38
38
  export type GradientStop = {
39
39
  position: number
40
- color: TSLColor | TSLVec3
40
+ color: TSLColor
41
41
  }
42
42
 
43
43
  export type FBMOptions = {
@@ -1,2 +1,4 @@
1
1
  // Auto-imports are sourced directly from individual files in this directory.
2
2
  // See: noise.ts, math.ts, color.ts, uv.ts, animation.ts, patterns.ts, tween.ts, oklch.ts
3
+
4
+ export {}
@@ -12,6 +12,7 @@
12
12
  :root {
13
13
  /* rem term keeps this WCAG 1.4.4 zoom-safe; svi never vw (viewport-bar-collapse safe) */
14
14
  --fs: clamp(1rem, 0.95rem + 0.25svi, 1.125rem);
15
+
15
16
  /* rounded, not raw, so grid boundaries land on-pixel */
16
17
  line-height: round(nearest, var(--fs) * 1.5, 1px);
17
18
  }
@@ -21,6 +22,7 @@
21
22
  line-height */
22
23
  --unit: 1rlh;
23
24
  --measure-min: 22rem;
25
+
24
26
  /* margin floor > 0, steep svi slope so it shrinks fast on narrow screens */
25
27
  --edge-min: clamp(1rlh, 8svi, 3rlh);
26
28
  --avail-b: calc(100svh - 2 * var(--edge-min));
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { describe, expect, it } from 'vite-plus/test'
2
2
 
3
3
  import type { GridPreset, TrackConfig } from '../types/tracks'
4
4
  import { resolve, toColumnsCss } from './useGridTracks'
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { describe, expect, it } from 'vite-plus/test'
2
2
 
3
3
  import type { GridPreset, TrackConfig, TrackSegment } from './tracks'
4
4
 
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { describe, expect, it } from 'vite-plus/test'
2
2
 
3
3
  import { placementFromIndex, placementFromLines } from './placement'
4
4
 
@@ -72,7 +72,7 @@
72
72
 
73
73
  <template>
74
74
  <Teleport to="body">
75
- <div v-if="visible" :style class="grid-debug z-9999 fixed inset-0" aria-hidden="true">
75
+ <div v-if="visible" :style class="grid-debug fixed inset-0 z-9999" aria-hidden="true">
76
76
  <div v-for="i in cols" :key="i" :style="{ backgroundColor: color }" class="h-full" />
77
77
  </div>
78
78
  </Teleport>