kmcom-nuxt-layers 1.3.0 → 1.4.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.
Files changed (86) hide show
  1. package/layers/content/app/components/Blog/List.vue +5 -1
  2. package/layers/content/app/components/Gallery/AmbientImage.vue +5 -12
  3. package/layers/content/app/components/Gallery/Detail.vue +8 -6
  4. package/layers/content/app/components/Gallery/Grid.vue +11 -3
  5. package/layers/content/app/components/Portfolio/ColorPalette.vue +1 -4
  6. package/layers/content/app/components/Portfolio/Detail.vue +6 -1
  7. package/layers/content/app/components/Portfolio/List.vue +5 -1
  8. package/layers/content/app/components/content/Figure.vue +1 -7
  9. package/layers/content/package.json +5 -5
  10. package/layers/core/app/assets/css/main.css +5 -0
  11. package/layers/core/app/composables/useCache.ts +8 -4
  12. package/layers/core/app/composables/useErrorLog.ts +9 -5
  13. package/layers/core/app/composables/useScrollGuard.ts +4 -2
  14. package/layers/core/app/plugins/feature-detection.client.ts +1 -1
  15. package/layers/core/app/plugins/init.ts +2 -1
  16. package/layers/core/app/plugins/scroll-guard.client.ts +4 -1
  17. package/layers/core/app.config.ts +0 -9
  18. package/layers/forms/app/components/Form/Contact.vue +16 -7
  19. package/layers/forms/nuxt.config.ts +18 -0
  20. package/layers/forms/package.json +2 -0
  21. package/layers/layout/app/components/Layout/Container.vue +1 -4
  22. package/layers/layout/app/components/Layout/Grid/Debug.vue +0 -1
  23. package/layers/layout/app/components/Layout/Grid/Item.vue +12 -6
  24. package/layers/layout/app/components/Layout/Main.vue +1 -4
  25. package/layers/layout/app/components/Layout/Page/Container.vue +3 -1
  26. package/layers/layout/app/components/Layout/Page/Header.vue +16 -5
  27. package/layers/layout/app/components/Layout/Section/Grid.vue +1 -4
  28. package/layers/layout/app/components/Layout/Section/Sidebar.vue +6 -1
  29. package/layers/layout/app/components/Layout/Section/Stack.vue +1 -1
  30. package/layers/layout/app/components/Layout/Section/Title.vue +33 -0
  31. package/layers/layout/app/composables/useGridConfig.ts +6 -1
  32. package/layers/motion/app/components/Motion/HorizontalScroll.vue +61 -0
  33. package/layers/motion/app/components/Motion/PinnedSection.vue +77 -0
  34. package/layers/motion/app/components/Motion/ScrollProgress.vue +8 -56
  35. package/layers/motion/app/components/Motion/ScrollScene.vue +121 -0
  36. package/layers/motion/app/components/Motion/ScrollStep.vue +45 -0
  37. package/layers/motion/app/components/Motion/TextReveal.vue +28 -63
  38. package/layers/motion/app/composables/useScrollSteps.ts +41 -0
  39. package/layers/motion/app/composables/useSectionProgress.ts +58 -0
  40. package/layers/motion/app/composables/useSmoothScroll.ts +3 -2
  41. package/layers/motion/app/plugins/locomotive-scroll.client.ts +6 -6
  42. package/layers/motion/nuxt.config.ts +6 -0
  43. package/layers/motion/package.json +2 -1
  44. package/layers/shader/app/components/Preset/ThemeAurora.client.vue +86 -0
  45. package/layers/shader/app/components/Preset/ThemeFlow.client.vue +86 -0
  46. package/layers/shader/app/components/Preset/ThemeGradient.client.vue +87 -0
  47. package/layers/shader/app/components/Shader/Background.client.vue +6 -0
  48. package/layers/shader/app/composables/useAmbientMaterials.ts +150 -0
  49. package/layers/shader/app/composables/useThemeColors.ts +43 -0
  50. package/layers/shader/app/utils/tsl/oklch.ts +12 -6
  51. package/layers/theme/app/assets/css/theme.css +19 -14
  52. package/layers/theme/app/components/ThemePicker/AccentButton.vue +2 -2
  53. package/layers/theme/app/components/ThemePicker/Colors.vue +2 -4
  54. package/layers/theme/app/components/ThemePicker/Menu.vue +4 -13
  55. package/layers/theme/app/components/ThemePicker/MenuButton.vue +1 -7
  56. package/layers/theme/app/composables/useAccentColor.ts +38 -0
  57. package/layers/theme/app/composables/useTheme.ts +14 -0
  58. package/layers/theme/app/composables/useThemeContrast.ts +34 -0
  59. package/layers/theme/app/composables/useThemeMotion.ts +34 -0
  60. package/layers/theme/app/composables/useThemePreferences.ts +3 -156
  61. package/layers/theme/app/composables/useThemeTransparency.ts +41 -0
  62. package/layers/theme/app/plugins/theme.client.ts +3 -3
  63. package/layers/theme/app/types/theme.ts +4 -0
  64. package/layers/theme/nuxt.config.ts +7 -0
  65. package/layers/ui/app/app.config.ts +44 -0
  66. package/layers/ui/app/assets/css/main.css +14 -0
  67. package/layers/ui/app/components/Accent/Blob.vue +29 -0
  68. package/layers/ui/app/components/Accent/Scene.vue +38 -0
  69. package/layers/ui/app/components/Gradient/Background.vue +22 -0
  70. package/layers/ui/app/components/Gradient/Text.vue +22 -0
  71. package/layers/ui/app/components/Progress/Bar.vue +25 -0
  72. package/layers/ui/app/components/Progress/Circular.vue +69 -0
  73. package/layers/ui/app/components/Tint/Overlay.vue +25 -0
  74. package/layers/ui/app/components/Typography/CodeBlock.vue +2 -1
  75. package/layers/ui/app/components/Typography/Headline.vue +2 -1
  76. package/layers/ui/app/components/Typography/QuoteBlock.vue +2 -1
  77. package/layers/ui/app/components/Typography/TextStroke.vue +18 -16
  78. package/layers/ui/app/composables/accent.ts +51 -0
  79. package/layers/ui/app/composables/gradient.ts +79 -0
  80. package/layers/ui/app/composables/tint.ts +20 -0
  81. package/layers/ui/app/types/accent.ts +17 -0
  82. package/layers/ui/app/types/gradient.ts +27 -0
  83. package/layers/ui/app/types/tint.ts +25 -0
  84. package/package.json +32 -30
  85. package/layers/motion/app/utils/gsapAnimations.ts +0 -122
  86. package/layers/ui/app.config.ts +0 -12
@@ -1,5 +1,4 @@
1
1
  <script lang="ts" setup>
2
- defineOptions({ inheritAttrs: false })
3
2
  import { useColor } from '../../composables/color'
4
3
  import { useTypography } from '../../composables/typography'
5
4
  import type { UiColors } from '../../types/colors'
@@ -14,6 +13,8 @@ import type {
14
13
  TextTransform,
15
14
  } from '../../types/typography'
16
15
 
16
+ defineOptions({ inheritAttrs: false })
17
+
17
18
  const props = withDefaults(
18
19
  defineProps<{
19
20
  level?: 1 | 2 | 3 | 4 | 5 | 6
@@ -1,9 +1,10 @@
1
1
  <script setup lang="ts">
2
- defineOptions({ inheritAttrs: false })
3
2
  import { useColor } from '../../composables/color'
4
3
  import type { UiColors } from '../../types/colors'
5
4
  import type { FontSize } from '../../types/typography'
6
5
 
6
+ defineOptions({ inheritAttrs: false })
7
+
7
8
  const props = defineProps<{ color?: UiColors; size?: FontSize }>()
8
9
  const colorClass = useColor(props.color, 'text')
9
10
  </script>
@@ -35,7 +35,9 @@ const effectiveStrokeWidth = computed(() =>
35
35
  v-bind="$attrs"
36
36
  >
37
37
  <!-- Sizer: transparent text for natural sizing + selectable -->
38
- <span class="text-stroke-sizer text-transparent whitespace-nowrap" aria-hidden="true">{{ text }}</span>
38
+ <span class="text-stroke-sizer text-transparent whitespace-nowrap" aria-hidden="true">{{
39
+ text
40
+ }}</span>
39
41
 
40
42
  <!-- Visual SVG stroke -->
41
43
  <svg
@@ -50,18 +52,16 @@ const effectiveStrokeWidth = computed(() =>
50
52
  <defs v-if="fill === 'none'">
51
53
  <mask :id="`stroke-mask-${id}`">
52
54
  <rect x="-9999" y="-9999" width="99999" height="99999" fill="white" />
53
- <text
54
- x="0" y="0"
55
- dominant-baseline="text-before-edge"
56
- fill="black"
57
- style="font: inherit"
58
- >{{ text }}</text>
55
+ <text x="0" y="0" dominant-baseline="text-before-edge" fill="black" style="font: inherit">
56
+ {{ text }}
57
+ </text>
59
58
  </mask>
60
59
  </defs>
61
60
 
62
61
  <text
63
62
  v-if="fill === 'none'"
64
- x="0" y="0"
63
+ x="0"
64
+ y="0"
65
65
  dominant-baseline="text-before-edge"
66
66
  fill="none"
67
67
  :stroke="strokeColor"
@@ -70,12 +70,15 @@ const effectiveStrokeWidth = computed(() =>
70
70
  vector-effect="non-scaling-stroke"
71
71
  :mask="`url(#stroke-mask-${id})`"
72
72
  style="font: inherit"
73
- >{{ text }}</text>
73
+ >
74
+ {{ text }}
75
+ </text>
74
76
 
75
77
  <!-- Fill+stroke mode: paint-order renders stroke behind fill -->
76
78
  <text
77
79
  v-else
78
- x="0" y="0"
80
+ x="0"
81
+ y="0"
79
82
  dominant-baseline="text-before-edge"
80
83
  :fill="fill"
81
84
  :stroke="strokeColor"
@@ -84,24 +87,23 @@ const effectiveStrokeWidth = computed(() =>
84
87
  vector-effect="non-scaling-stroke"
85
88
  paint-order="stroke"
86
89
  style="font: inherit"
87
- >{{ text }}</text>
90
+ >
91
+ {{ text }}
92
+ </text>
88
93
  </svg>
89
94
 
90
95
  <!-- Accessible label for screen readers -->
91
- <span
92
- :id="`stroke-text-${id}`"
93
- class="sr-only"
94
- >{{ text }}</span>
96
+ <span :id="`stroke-text-${id}`" class="sr-only">{{ text }}</span>
95
97
  </component>
96
98
  </template>
97
99
 
98
100
  <style scoped>
99
-
100
101
  @media (forced-colors: active) {
101
102
  .text-stroke-sizer {
102
103
  visibility: visible;
103
104
  color: CanvasText;
104
105
  }
106
+
105
107
  .text-stroke-svg {
106
108
  display: none;
107
109
  }
@@ -0,0 +1,51 @@
1
+ import type { ComputedRef, CSSProperties } from 'vue'
2
+ import type { BlobBlur, BlobConfig } from '../types/accent'
3
+
4
+ const BLUR_PX_MAP: Record<string, number> = {
5
+ none: 0,
6
+ sm: 8,
7
+ md: 12,
8
+ lg: 16,
9
+ xl: 24,
10
+ '2xl': 40,
11
+ '3xl': 64,
12
+ }
13
+
14
+ function resolveBlurPx(blur: BlobBlur = '3xl'): number {
15
+ if (typeof blur === 'number') return blur
16
+ return BLUR_PX_MAP[blur] ?? 64
17
+ }
18
+
19
+ function resolveColor(config: BlobConfig): string {
20
+ const { color = 'primary', shade = 500, customColor } = config
21
+ if (color === 'custom') return customColor ?? 'transparent'
22
+ if (color === 'white') return '#ffffff'
23
+ if (color === 'black') return '#000000'
24
+ return `var(--ui-color-${color}-${shade})`
25
+ }
26
+
27
+ export function useAccentBlob(config: BlobConfig): {
28
+ style: ComputedRef<CSSProperties>
29
+ } {
30
+ const style = computed((): CSSProperties => {
31
+ const size = config.size ?? '40rem'
32
+ const opacity = (config.opacity ?? 25) / 100
33
+ const blurPx = resolveBlurPx(config.blur)
34
+
35
+ return {
36
+ position: 'absolute',
37
+ left: `${config.x}%`,
38
+ top: `${config.y}%`,
39
+ transform: 'translate(-50%, -50%)',
40
+ width: size,
41
+ height: size,
42
+ backgroundColor: resolveColor(config),
43
+ opacity,
44
+ borderRadius: '9999px',
45
+ filter: blurPx > 0 ? `blur(${blurPx}px)` : undefined,
46
+ pointerEvents: 'none',
47
+ }
48
+ })
49
+
50
+ return { style }
51
+ }
@@ -0,0 +1,79 @@
1
+ import type { ComputedRef, CSSProperties, MaybeRefOrGetter } from 'vue'
2
+ import type { GradientConfig, GradientStop } from '../types/gradient'
3
+
4
+ const DEFAULT_CONFIG: GradientConfig = {
5
+ shape: 'linear',
6
+ direction: 'to-br',
7
+ from: { color: 'primary', shade: 500 },
8
+ to: { color: 'secondary', shade: 500 },
9
+ }
10
+
11
+ const DIRECTION_MAP: Record<string, string> = {
12
+ 'to-t': 'to top',
13
+ 'to-tr': 'to top right',
14
+ 'to-r': 'to right',
15
+ 'to-br': 'to bottom right',
16
+ 'to-b': 'to bottom',
17
+ 'to-bl': 'to bottom left',
18
+ 'to-l': 'to left',
19
+ 'to-tl': 'to top left',
20
+ }
21
+
22
+ function resolveColor(stop: GradientStop): string {
23
+ const { color, shade = 500, opacity } = stop
24
+ if (color === 'transparent') return 'transparent'
25
+ if (color === 'white') {
26
+ return opacity !== undefined ? `rgb(255 255 255 / ${opacity / 100})` : '#ffffff'
27
+ }
28
+ if (color === 'black') {
29
+ return opacity !== undefined ? `rgb(0 0 0 / ${opacity / 100})` : '#000000'
30
+ }
31
+ const v = `var(--ui-color-${color}-${shade})`
32
+ return opacity !== undefined ? `color-mix(in srgb, ${v} ${opacity}%, transparent)` : v
33
+ }
34
+
35
+ function buildStyle(cfg: GradientConfig): CSSProperties {
36
+ const from = resolveColor(cfg.from)
37
+ const to = resolveColor(cfg.to)
38
+ const via = cfg.via ? resolveColor(cfg.via) : undefined
39
+ const stops = via ? `${from}, ${via}, ${to}` : `${from}, ${to}`
40
+
41
+ if (cfg.shape === 'radial') {
42
+ return { backgroundImage: `radial-gradient(circle, ${stops})` }
43
+ }
44
+ if (cfg.shape === 'conic') {
45
+ return { backgroundImage: `conic-gradient(${stops})` }
46
+ }
47
+ const dir = DIRECTION_MAP[cfg.direction ?? 'to-br'] ?? 'to bottom right'
48
+ return { backgroundImage: `linear-gradient(${dir}, ${stops})` }
49
+ }
50
+
51
+ export function useGradient(
52
+ config: MaybeRefOrGetter<GradientConfig | string>,
53
+ overrides?: MaybeRefOrGetter<Partial<GradientConfig>>
54
+ ): { style: ComputedRef<CSSProperties> } {
55
+ const appConfig = useAppConfig()
56
+
57
+ const style = computed((): CSSProperties => {
58
+ const raw = toValue(config)
59
+ const override = overrides ? toValue(overrides) : undefined
60
+
61
+ let resolved: GradientConfig
62
+ if (typeof raw === 'string') {
63
+ const preset = (appConfig.uiLayer as Record<string, unknown> | undefined)?.gradients as
64
+ | Record<string, GradientConfig>
65
+ | undefined
66
+ resolved = preset?.[raw] ?? DEFAULT_CONFIG
67
+ } else {
68
+ resolved = raw
69
+ }
70
+
71
+ if (override) {
72
+ resolved = { ...resolved, ...override }
73
+ }
74
+
75
+ return buildStyle(resolved)
76
+ })
77
+
78
+ return { style }
79
+ }
@@ -0,0 +1,20 @@
1
+ import type { ComputedRef, CSSProperties } from 'vue'
2
+ import { TINT_LEVEL_OPACITY } from '../types/tint'
3
+ import type { TintConfig } from '../types/tint'
4
+
5
+ export function useTint(config: TintConfig): {
6
+ style: ComputedRef<CSSProperties>
7
+ } {
8
+ const style = computed((): CSSProperties => {
9
+ const { color, shade = 500 } = config
10
+ let backgroundColor: string
11
+ if (color === 'white') backgroundColor = '#ffffff'
12
+ else if (color === 'black') backgroundColor = '#000000'
13
+ else backgroundColor = `var(--ui-color-${color}-${shade})`
14
+
15
+ const opacity = TINT_LEVEL_OPACITY[config.level] / 100
16
+ return { backgroundColor, opacity }
17
+ })
18
+
19
+ return { style }
20
+ }
@@ -0,0 +1,17 @@
1
+ export type AccentBlobColor = 'primary' | 'secondary' | 'info' | 'white' | 'black' | 'custom'
2
+ export type BlobBlur = 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | number
3
+
4
+ export interface BlobConfig {
5
+ x: number
6
+ y: number
7
+ size?: string
8
+ blur?: BlobBlur
9
+ opacity?: number
10
+ color?: AccentBlobColor
11
+ shade?: 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950
12
+ customColor?: string
13
+ }
14
+
15
+ export interface AccentSceneConfig {
16
+ blobs: BlobConfig[]
17
+ }
@@ -0,0 +1,27 @@
1
+ export type GradientColorSlot = 'primary' | 'secondary' | 'info' | 'white' | 'black' | 'transparent'
2
+ export type GradientDirection =
3
+ | 'to-t'
4
+ | 'to-tr'
5
+ | 'to-r'
6
+ | 'to-br'
7
+ | 'to-b'
8
+ | 'to-bl'
9
+ | 'to-l'
10
+ | 'to-tl'
11
+ export type GradientShape = 'linear' | 'radial' | 'conic'
12
+ export type GradientOpacity = 0 | 10 | 20 | 25 | 30 | 40 | 50 | 60 | 70 | 75 | 80 | 90 | 100
13
+ export type ColorShade = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950
14
+
15
+ export interface GradientStop {
16
+ color: GradientColorSlot
17
+ shade?: ColorShade
18
+ opacity?: GradientOpacity
19
+ }
20
+
21
+ export interface GradientConfig {
22
+ shape?: GradientShape
23
+ direction?: GradientDirection
24
+ from: GradientStop
25
+ via?: GradientStop
26
+ to: GradientStop
27
+ }
@@ -0,0 +1,25 @@
1
+ export type TintLevel = 'subtle' | 'light' | 'medium' | 'strong' | 'heavy'
2
+ export type TintColorSlot =
3
+ | 'primary'
4
+ | 'secondary'
5
+ | 'info'
6
+ | 'success'
7
+ | 'warning'
8
+ | 'error'
9
+ | 'white'
10
+ | 'black'
11
+ export type ColorShade = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950
12
+
13
+ export const TINT_LEVEL_OPACITY: Record<TintLevel, number> = {
14
+ subtle: 5,
15
+ light: 10,
16
+ medium: 20,
17
+ strong: 35,
18
+ heavy: 50,
19
+ }
20
+
21
+ export interface TintConfig {
22
+ color: TintColorSlot
23
+ level: TintLevel
24
+ shade?: ColorShade
25
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kmcom-nuxt-layers",
3
3
  "private": false,
4
- "version": "1.3.0",
4
+ "version": "1.4.0",
5
5
  "description": "Composable Nuxt 4 layers for building scalable Vue applications",
6
6
  "files": [
7
7
  "layers/*/nuxt.config.ts",
@@ -21,26 +21,26 @@
21
21
  "packages/*"
22
22
  ],
23
23
  "peerDependencies": {
24
- "@nuxt/content": "^3.0.0",
25
- "@nuxt/eslint": "^1.0.0",
24
+ "@nuxt/content": "^3.12.0",
25
+ "@nuxt/eslint": "^1.15.2",
26
26
  "@nuxt/image": "^2.0.0",
27
- "@nuxt/ui": "^4.5.0",
27
+ "@nuxt/ui": "^4.5.1",
28
28
  "@nuxtjs/device": "^4.0.0",
29
- "@tresjs/cientos": "^5.0.0",
30
- "@tresjs/core": "^5.0.0",
31
- "@tresjs/nuxt": "^5.0.0",
32
- "@tresjs/post-processing": "^3.0.0",
33
- "@vite-pwa/nuxt": "^1.0.0",
34
- "@vueuse/nuxt": "^14.0.0",
35
- "better-sqlite3": "^12.0.0",
36
- "gsap": "^3.12.0",
37
- "locomotive-scroll": "^5.0.0",
29
+ "@tresjs/cientos": "^5.4.0",
30
+ "@tresjs/core": "^5.5.0",
31
+ "@tresjs/nuxt": "^5.3.0",
32
+ "@tresjs/post-processing": "^3.4.0",
33
+ "@vite-pwa/nuxt": "^1.1.1",
34
+ "@vueuse/nuxt": "^14.2.1",
35
+ "better-sqlite3": "^12.6.2",
36
+ "gsap": "^3.14.2",
37
+ "locomotive-scroll": "^5.0.1",
38
38
  "nuxt": "^4.3.1",
39
- "nuxt-studio": "^1.0.0",
40
- "pinia": "^3.0.0",
39
+ "nuxt-studio": "^1.4.0",
40
+ "pinia": "^3.0.4",
41
41
  "tailwindcss": "^4.2.1",
42
- "three": "^0.170.0",
43
- "zod": "^3.20.0"
42
+ "three": "^0.183.2",
43
+ "zod": "^4.3.6"
44
44
  },
45
45
  "peerDependenciesMeta": {
46
46
  "@nuxtjs/device": {
@@ -87,21 +87,21 @@
87
87
  }
88
88
  },
89
89
  "devDependencies": {
90
- "@commitlint/cli": "^20.4.1",
91
- "@commitlint/config-conventional": "^20.4.1",
92
- "@culur/config-stylelint": "^1.6.4",
90
+ "@commitlint/cli": "^20.4.3",
91
+ "@commitlint/config-conventional": "^20.4.3",
92
+ "@culur/config-stylelint": "^1.6.5",
93
93
  "@eslint/json": "^1.0.1",
94
94
  "@eslint/markdown": "^7.5.1",
95
95
  "@ianvs/prettier-plugin-sort-imports": "^4.7.1",
96
- "@iconify-json/lucide": "^1.2.89",
96
+ "@iconify-json/lucide": "^1.2.95",
97
97
  "@nuxt/eslint": "^1.15.2",
98
- "@nuxt/fonts": "^0.13.0",
98
+ "@nuxt/fonts": "^0.14.0",
99
99
  "@nuxt/image": "^2.0.0",
100
100
  "@nuxt/ui": "latest",
101
101
  "@nuxtjs/device": "^4.0.0",
102
102
  "@perplex-digital/stylelint-config": "^17.3.0",
103
103
  "@pinia/nuxt": "^0.11.3",
104
- "@types/node": "^25.3.0",
104
+ "@types/node": "^25.3.3",
105
105
  "@typescript-eslint/eslint-plugin": "^8.56.1",
106
106
  "@typescript-eslint/parser": "^8.56.1",
107
107
  "@vue/eslint-config-typescript": "^14.7.0",
@@ -113,7 +113,7 @@
113
113
  "cypress": "^15.10.0",
114
114
  "depcheck": "^1.4.7",
115
115
  "eslint": "^10.0.2",
116
- "eslint-plugin-compat": "^6.2.0",
116
+ "eslint-plugin-compat": "^6.2.1",
117
117
  "eslint-plugin-glsl": "0.0.0-wip",
118
118
  "eslint-plugin-oxlint": "^1.50.0",
119
119
  "eslint-plugin-pnpm": "^1.6.0",
@@ -121,7 +121,7 @@
121
121
  "eslint-plugin-unicorn": "^63.0.0",
122
122
  "eslint-plugin-unused-imports": "^4.4.1",
123
123
  "eslint-plugin-vue": "^10.8.0",
124
- "npm-check-updates": "^19.5.0",
124
+ "npm-check-updates": "^19.6.3",
125
125
  "nuxt": "latest",
126
126
  "oxlint": "^1.43.0",
127
127
  "pinia": "^3.0.4",
@@ -129,11 +129,11 @@
129
129
  "postcss-html": "^1.8.1",
130
130
  "prettier": "^3.8.1",
131
131
  "prettier-plugin-css-order": "^2.2.0",
132
- "prettier-plugin-glsl": "^0.2.3",
132
+ "prettier-plugin-glsl": "^0.2.4",
133
133
  "prettier-plugin-organize-attributes": "^1.0.0",
134
134
  "prettier-plugin-tailwind-styled-components": "^2.0.2",
135
135
  "prettier-plugin-tailwindcss": "^0.7.2",
136
- "stylelint": "^17.1.1",
136
+ "stylelint": "^17.4.0",
137
137
  "stylelint-config-recommended-vue": "^1.6.1",
138
138
  "stylelint-config-standard": "^40.0.0",
139
139
  "stylelint-config-standard-vue": "^1.0.0",
@@ -141,12 +141,14 @@
141
141
  "stylelint-no-unsupported-browser-features": "^8.1.1",
142
142
  "stylelint-prettier": "^5.0.3",
143
143
  "tailwindcss": "^4.2.1",
144
- "turbo": "^2.8.10",
144
+ "turbo": "^2.8.13",
145
145
  "typescript": "^5.9.3",
146
146
  "vite-plugin-checker": "^0.12.0",
147
147
  "vitest": "^4.0.18",
148
148
  "vue": "latest",
149
- "vue-tsc": "^3.2.5"
149
+ "vue-tsc": "^3.2.5",
150
+ "zod": "^4.3.6",
151
+ "zod-to-json-schema": "^3.25.1"
150
152
  },
151
153
  "browserslist": [
152
154
  "last 2 Chrome major versions",
@@ -155,7 +157,7 @@
155
157
  "last 2 Edge major versions"
156
158
  ],
157
159
  "dependencies": {
158
- "skills": "^1.4.1"
160
+ "skills": "^1.4.3"
159
161
  },
160
162
  "scripts": {
161
163
  "dev": "pnpm -F playground dev",
@@ -1,122 +0,0 @@
1
- import type { gsap as GsapType } from 'gsap'
2
- import type { ScrollTrigger as ScrollTriggerType } from 'gsap/ScrollTrigger'
3
-
4
- type GsapInstance = typeof GsapType
5
- type ScrollTriggerInstance = typeof ScrollTriggerType
6
-
7
- /**
8
- * Create a fade-in animation with scroll trigger
9
- */
10
- export function createFadeIn(
11
- gsap: GsapInstance,
12
- target: string | Element,
13
- options?: {
14
- duration?: number
15
- delay?: number
16
- y?: number
17
- start?: string
18
- toggleActions?: string
19
- }
20
- ) {
21
- const {
22
- duration = 1,
23
- delay = 0,
24
- y = 30,
25
- start = 'top 85%',
26
- toggleActions = 'play none none none',
27
- } = options ?? {}
28
-
29
- gsap.set(target, { opacity: 0, y })
30
-
31
- return gsap.to(target, {
32
- opacity: 1,
33
- y: 0,
34
- duration,
35
- delay,
36
- ease: 'power2.out',
37
- scrollTrigger: {
38
- trigger: target,
39
- start,
40
- toggleActions,
41
- },
42
- })
43
- }
44
-
45
- /**
46
- * Create a parallax effect
47
- */
48
- export function createParallax(
49
- gsap: GsapInstance,
50
- target: string | Element,
51
- options?: {
52
- speed?: number
53
- trigger?: string | Element
54
- }
55
- ) {
56
- const { speed = 0.5, trigger } = options ?? {}
57
-
58
- return gsap.to(target, {
59
- y: () => window.innerHeight * speed,
60
- ease: 'none',
61
- scrollTrigger: {
62
- trigger: trigger ?? target,
63
- start: 'top bottom',
64
- end: 'bottom top',
65
- scrub: true,
66
- },
67
- })
68
- }
69
-
70
- /**
71
- * Create a pinned section
72
- */
73
- export function createPin(
74
- ScrollTrigger: ScrollTriggerInstance,
75
- target: string | Element,
76
- options?: {
77
- start?: string
78
- end?: string
79
- pinSpacing?: boolean
80
- }
81
- ) {
82
- const { start = 'top top', end = '+=100%', pinSpacing = true } = options ?? {}
83
-
84
- return ScrollTrigger.create({
85
- trigger: target,
86
- start,
87
- end,
88
- pin: true,
89
- pinSpacing,
90
- })
91
- }
92
-
93
- /**
94
- * Create staggered fade-in for multiple elements
95
- */
96
- export function createStaggeredFadeIn(
97
- gsap: GsapInstance,
98
- targets: string | Element[],
99
- options?: {
100
- duration?: number
101
- stagger?: number
102
- y?: number
103
- start?: string
104
- }
105
- ) {
106
- const { duration = 0.8, stagger = 0.1, y = 30, start = 'top 85%' } = options ?? {}
107
-
108
- gsap.set(targets, { opacity: 0, y })
109
-
110
- return gsap.to(targets, {
111
- opacity: 1,
112
- y: 0,
113
- duration,
114
- stagger,
115
- ease: 'power2.out',
116
- scrollTrigger: {
117
- trigger: typeof targets === 'string' ? targets : targets[0],
118
- start,
119
- toggleActions: 'play none none none',
120
- },
121
- })
122
- }
@@ -1,12 +0,0 @@
1
- export default defineAppConfig({
2
- uiLayer: {},
3
- })
4
-
5
- declare module '@nuxt/schema' {
6
- interface AppConfigInput {
7
- uiLayer?: {
8
- /** Project name */
9
- name?: string
10
- }
11
- }
12
- }