kmcom-nuxt-layers 1.3.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/layers/content/app/components/Blog/List.vue +5 -1
- package/layers/content/app/components/Gallery/AmbientImage.vue +5 -12
- package/layers/content/app/components/Gallery/Detail.vue +8 -6
- package/layers/content/app/components/Gallery/Grid.vue +11 -3
- package/layers/content/app/components/Portfolio/ColorPalette.vue +1 -4
- package/layers/content/app/components/Portfolio/Detail.vue +6 -1
- package/layers/content/app/components/Portfolio/List.vue +5 -1
- package/layers/content/app/components/content/Figure.vue +1 -7
- package/layers/content/nuxt.config.ts +16 -5
- package/layers/content/package.json +5 -5
- package/layers/core/app/assets/css/main.css +5 -0
- package/layers/core/app/composables/useCache.ts +8 -4
- package/layers/core/app/composables/useErrorLog.ts +9 -5
- package/layers/core/app/composables/useScrollGuard.ts +4 -2
- package/layers/core/app/plugins/feature-detection.client.ts +1 -1
- package/layers/core/app/plugins/init.ts +2 -1
- package/layers/core/app/plugins/scroll-guard.client.ts +4 -1
- package/layers/core/app.config.ts +0 -9
- package/layers/forms/app/components/Form/Contact.vue +16 -7
- package/layers/forms/nuxt.config.ts +18 -0
- package/layers/forms/package.json +2 -0
- package/layers/layout/app/components/Layout/Container.vue +1 -4
- package/layers/layout/app/components/Layout/Grid/Debug.vue +0 -1
- package/layers/layout/app/components/Layout/Grid/Item.vue +12 -6
- package/layers/layout/app/components/Layout/Main.vue +1 -4
- package/layers/layout/app/components/Layout/Page/Container.vue +3 -1
- package/layers/layout/app/components/Layout/Page/Header.vue +16 -7
- package/layers/layout/app/components/Layout/Section/Grid.vue +1 -4
- package/layers/layout/app/components/Layout/Section/Sidebar.vue +6 -1
- package/layers/layout/app/components/Layout/Section/Stack.vue +1 -1
- package/layers/layout/app/composables/useGridConfig.ts +6 -1
- package/layers/motion/app/components/Motion/HorizontalScroll.vue +61 -0
- package/layers/motion/app/components/Motion/PinnedSection.vue +77 -0
- package/layers/motion/app/components/Motion/ScrollProgress.vue +8 -56
- package/layers/motion/app/components/Motion/ScrollScene.vue +121 -0
- package/layers/motion/app/components/Motion/ScrollStep.vue +45 -0
- package/layers/motion/app/components/Motion/TextReveal.vue +28 -63
- package/layers/motion/app/composables/useScrollSteps.ts +41 -0
- package/layers/motion/app/composables/useSectionProgress.ts +58 -0
- package/layers/motion/app/composables/useSmoothScroll.ts +3 -2
- package/layers/motion/app/plugins/locomotive-scroll.client.ts +6 -6
- package/layers/motion/nuxt.config.ts +6 -0
- package/layers/motion/package.json +2 -1
- package/layers/routing/app/app.config.ts +20 -0
- package/layers/routing/app/composables/useFeatures.ts +12 -0
- package/layers/routing/app/composables/useMaintenance.ts +7 -0
- package/layers/routing/app/composables/useRoutingConfig.ts +20 -0
- package/layers/routing/app/middleware/01.maintenance.global.ts +6 -0
- package/layers/routing/app/middleware/02.governance.global.ts +25 -0
- package/layers/routing/app/plugins/feature-flags.client.ts +15 -0
- package/layers/routing/app/plugins/scroll-routing.client.ts +21 -0
- package/layers/routing/app/types/route-meta.d.ts +6 -0
- package/layers/routing/app/types/routing.ts +48 -0
- package/layers/routing/nuxt.config.ts +27 -0
- package/layers/routing/package.json +6 -0
- package/layers/shader/app/components/Preset/ThemeAurora.client.vue +86 -0
- package/layers/shader/app/components/Preset/ThemeBubble.client.vue +87 -0
- package/layers/shader/app/components/Preset/ThemeFlow.client.vue +86 -0
- package/layers/shader/app/components/Preset/ThemeGradient.client.vue +87 -0
- package/layers/shader/app/components/Preset/ThemeLavaLamp.client.vue +86 -0
- package/layers/shader/app/components/Preset/ThemePlasma.client.vue +86 -0
- package/layers/shader/app/components/Preset/ThemeWave.client.vue +86 -0
- package/layers/shader/app/components/Shader/Background.client.vue +15 -0
- package/layers/shader/app/composables/useAmbientMaterials.ts +306 -0
- package/layers/shader/app/composables/useThemeColors.ts +52 -0
- package/layers/shader/app/utils/tsl/oklch.ts +12 -6
- package/layers/theme/app/assets/css/theme.css +19 -14
- package/layers/theme/app/components/ThemePicker/AccentButton.vue +2 -2
- package/layers/theme/app/components/ThemePicker/Colors.vue +2 -4
- package/layers/theme/app/components/ThemePicker/Menu.vue +4 -13
- package/layers/theme/app/components/ThemePicker/MenuButton.vue +1 -7
- package/layers/theme/app/composables/useAccentColor.ts +38 -0
- package/layers/theme/app/composables/useTheme.ts +14 -0
- package/layers/theme/app/composables/useThemeContrast.ts +34 -0
- package/layers/theme/app/composables/useThemeMotion.ts +34 -0
- package/layers/theme/app/composables/useThemePreferences.ts +3 -156
- package/layers/theme/app/composables/useThemeTransparency.ts +41 -0
- package/layers/theme/app/plugins/theme.client.ts +3 -3
- package/layers/theme/app/types/theme.ts +4 -0
- package/layers/theme/nuxt.config.ts +7 -0
- package/layers/ui/app/app.config.ts +44 -0
- package/layers/ui/app/assets/css/main.css +14 -0
- package/layers/ui/app/components/Accent/Blob.vue +29 -0
- package/layers/ui/app/components/Accent/Scene.vue +38 -0
- package/layers/ui/app/components/Gradient/Background.vue +22 -0
- package/layers/ui/app/components/Gradient/Text.vue +22 -0
- package/layers/ui/app/components/Progress/Bar.vue +25 -0
- package/layers/ui/app/components/Progress/Circular.vue +69 -0
- package/layers/ui/app/components/Tint/Overlay.vue +25 -0
- package/layers/ui/app/components/Typography/CodeBlock.vue +2 -1
- package/layers/ui/app/components/Typography/Headline.vue +2 -1
- package/layers/ui/app/components/Typography/QuoteBlock.vue +2 -1
- package/layers/ui/app/components/Typography/TextStroke.vue +18 -16
- package/layers/ui/app/composables/accent.ts +51 -0
- package/layers/ui/app/composables/gradient.ts +79 -0
- package/layers/ui/app/composables/tint.ts +20 -0
- package/layers/ui/app/types/accent.ts +17 -0
- package/layers/ui/app/types/gradient.ts +27 -0
- package/layers/ui/app/types/tint.ts +25 -0
- package/package.json +37 -31
- package/layers/motion/app/utils/gsapAnimations.ts +0 -122
- package/layers/ui/app.config.ts +0 -12
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const {
|
|
3
|
+
progress,
|
|
4
|
+
size = 120,
|
|
5
|
+
strokeWidth = 8,
|
|
6
|
+
showPercentage = true,
|
|
7
|
+
colors = ['var(--color-primary-500)', 'rgb(168, 85, 247)', 'rgb(236, 72, 153)'],
|
|
8
|
+
bgColor = 'rgb(31, 41, 55)',
|
|
9
|
+
} = defineProps<{
|
|
10
|
+
progress: number
|
|
11
|
+
size?: number
|
|
12
|
+
strokeWidth?: number
|
|
13
|
+
showPercentage?: boolean
|
|
14
|
+
colors?: [string, string, string]
|
|
15
|
+
bgColor?: string
|
|
16
|
+
}>()
|
|
17
|
+
|
|
18
|
+
const percentage = computed(() => Math.round(progress * 100))
|
|
19
|
+
const radius = computed(() => (size - strokeWidth) / 2)
|
|
20
|
+
const circumference = computed(() => 2 * Math.PI * radius.value)
|
|
21
|
+
const strokeDasharray = computed(() => `${progress * circumference.value} ${circumference.value}`)
|
|
22
|
+
|
|
23
|
+
const gradientId = `progress-circular-gradient-${Math.random().toString(36).slice(2, 9)}`
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<div class="progress-circular relative inline-flex items-center justify-center">
|
|
28
|
+
<svg :width="size" :height="size" class="-rotate-90" :viewBox="`0 0 ${size} ${size}`">
|
|
29
|
+
<circle
|
|
30
|
+
:cx="size / 2"
|
|
31
|
+
:cy="size / 2"
|
|
32
|
+
:r="radius"
|
|
33
|
+
fill="none"
|
|
34
|
+
:stroke="bgColor"
|
|
35
|
+
:stroke-width="strokeWidth"
|
|
36
|
+
/>
|
|
37
|
+
<circle
|
|
38
|
+
:cx="size / 2"
|
|
39
|
+
:cy="size / 2"
|
|
40
|
+
:r="radius"
|
|
41
|
+
fill="none"
|
|
42
|
+
:stroke="`url(#${gradientId})`"
|
|
43
|
+
:stroke-width="strokeWidth"
|
|
44
|
+
stroke-linecap="round"
|
|
45
|
+
:stroke-dasharray
|
|
46
|
+
class="transition-[stroke-dasharray] duration-100"
|
|
47
|
+
/>
|
|
48
|
+
<defs>
|
|
49
|
+
<linearGradient :id="gradientId" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
50
|
+
<stop offset="0%" :style="`stop-color: ${colors[0]}`" />
|
|
51
|
+
<stop offset="50%" :style="`stop-color: ${colors[1]}`" />
|
|
52
|
+
<stop offset="100%" :style="`stop-color: ${colors[2]}`" />
|
|
53
|
+
</linearGradient>
|
|
54
|
+
</defs>
|
|
55
|
+
</svg>
|
|
56
|
+
<span
|
|
57
|
+
v-if="showPercentage"
|
|
58
|
+
class="absolute inset-0 flex items-center justify-center text-2xl font-bold tabular-nums"
|
|
59
|
+
>
|
|
60
|
+
{{ percentage }}%
|
|
61
|
+
</span>
|
|
62
|
+
</div>
|
|
63
|
+
</template>
|
|
64
|
+
|
|
65
|
+
<style scoped>
|
|
66
|
+
.tabular-nums {
|
|
67
|
+
font-variant-numeric: tabular-nums;
|
|
68
|
+
}
|
|
69
|
+
</style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ColorShade, TintColorSlot, TintLevel } from '../../types/tint'
|
|
3
|
+
|
|
4
|
+
const props = withDefaults(
|
|
5
|
+
defineProps<{
|
|
6
|
+
color: TintColorSlot
|
|
7
|
+
level: TintLevel
|
|
8
|
+
shade?: ColorShade
|
|
9
|
+
tag?: string
|
|
10
|
+
}>(),
|
|
11
|
+
{
|
|
12
|
+
shade: 500,
|
|
13
|
+
tag: 'div',
|
|
14
|
+
}
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
const { style } = useTint(props)
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<component :is="tag" class="relative">
|
|
22
|
+
<slot />
|
|
23
|
+
<span class="absolute inset-0 pointer-events-none" :style="style" aria-hidden="true" />
|
|
24
|
+
</component>
|
|
25
|
+
</template>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
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 = withDefaults(
|
|
8
9
|
defineProps<{
|
|
9
10
|
language?: string
|
|
@@ -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">{{
|
|
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
|
-
|
|
55
|
-
|
|
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"
|
|
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
|
-
>
|
|
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"
|
|
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
|
-
>
|
|
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.
|
|
4
|
+
"version": "1.5.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.
|
|
25
|
-
"@nuxt/eslint": "^1.
|
|
24
|
+
"@nuxt/content": "^3.12.0",
|
|
25
|
+
"@nuxt/eslint": "^1.15.2",
|
|
26
26
|
"@nuxt/image": "^2.0.0",
|
|
27
|
-
"@nuxt/ui": "^4.5.
|
|
27
|
+
"@nuxt/ui": "^4.5.1",
|
|
28
28
|
"@nuxtjs/device": "^4.0.0",
|
|
29
|
-
"@tresjs/cientos": "^5.
|
|
30
|
-
"@tresjs/core": "^5.
|
|
31
|
-
"@tresjs/nuxt": "^5.
|
|
32
|
-
"@tresjs/post-processing": "^3.
|
|
33
|
-
"@vite-pwa/nuxt": "^1.
|
|
34
|
-
"@vueuse/nuxt": "^14.
|
|
35
|
-
"better-sqlite3": "^12.
|
|
36
|
-
"gsap": "^3.
|
|
37
|
-
"locomotive-scroll": "^5.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.
|
|
40
|
-
"pinia": "^3.0.
|
|
39
|
+
"nuxt-studio": "^1.4.0",
|
|
40
|
+
"pinia": "^3.0.4",
|
|
41
41
|
"tailwindcss": "^4.2.1",
|
|
42
|
-
"three": "^0.
|
|
43
|
-
"zod": "^3.
|
|
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.
|
|
91
|
-
"@commitlint/config-conventional": "^20.4.
|
|
92
|
-
"@culur/config-stylelint": "^1.6.
|
|
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.
|
|
96
|
+
"@iconify-json/lucide": "^1.2.95",
|
|
97
97
|
"@nuxt/eslint": "^1.15.2",
|
|
98
|
-
"@nuxt/fonts": "^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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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,10 @@
|
|
|
155
157
|
"last 2 Edge major versions"
|
|
156
158
|
],
|
|
157
159
|
"dependencies": {
|
|
158
|
-
"skills": "^1.4.
|
|
160
|
+
"skills": "^1.4.3"
|
|
161
|
+
},
|
|
162
|
+
"engines": {
|
|
163
|
+
"node": ">=18 <21"
|
|
159
164
|
},
|
|
160
165
|
"scripts": {
|
|
161
166
|
"dev": "pnpm -F playground dev",
|
|
@@ -195,6 +200,7 @@
|
|
|
195
200
|
"layer:rebuild:ui": "pnpm build:ui && pnpm layer:generate:ui && pnpm layer:ui",
|
|
196
201
|
"layer:rebuild:layout": "pnpm build:layout && pnpm layer:generate:layout && pnpm layer:layout",
|
|
197
202
|
"layer:rebuild:motion": "pnpm build:motion && pnpm layer:generate:motion && pnpm layer:motion",
|
|
198
|
-
"browserlist": "npx update-browserslist-db@latest"
|
|
203
|
+
"browserlist": "npx update-browserslist-db@latest",
|
|
204
|
+
"deploy": "turbo run build --filter playground"
|
|
199
205
|
}
|
|
200
206
|
}
|