kmcom-nuxt-layers 2.2.12 → 2.2.13
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/FEEDS.md +1 -2
- package/layers/animations/app/composables/useMagneticElement.ts +11 -9
- package/layers/animations/app/composables/useTiltEffect.ts +11 -9
- package/layers/animations/app/utils/pointerMotion.ts +31 -0
- package/layers/canvas/app/components/ShaderCanvas.vue +2 -2
- package/layers/content/app/composables/useCollectionItems.ts +28 -0
- package/layers/content/app/composables/useGalleryItems.ts +8 -14
- package/layers/content/app/composables/usePortfolioItems.ts +10 -18
- package/layers/core/app/composables/useBrowser.ts +9 -82
- package/layers/core/app/composables/useFeatures.ts +3 -27
- package/layers/core/app/plugins/init.ts +157 -135
- package/layers/core/app/utils/browserInfo.ts +115 -0
- package/layers/core/app/utils/featureClasses.ts +40 -0
- package/layers/core/app/utils/helpers.test.ts +51 -0
- package/layers/feeds/app/components/Feeds/Index.vue +1 -1
- package/layers/feeds/app/components/Feeds/RouteCard.vue +3 -9
- package/layers/feeds/app/utils/feed-catalog.ts +9 -4
- package/layers/feeds/nuxt.config.ts +0 -1
- package/layers/feeds/server/utils/content-adapter.test.ts +68 -0
- package/layers/feeds/server/utils/content-adapter.ts +2 -22
- package/layers/feeds/server/utils/feed-author.ts +32 -0
- package/layers/feeds/server/utils/feed-config.ts +88 -0
- package/layers/feeds/server/utils/feed-service.ts +11 -30
- package/layers/feeds/server/utils/feed-xml.ts +26 -0
- package/layers/feeds/server/utils/formats/rss.ts +10 -15
- package/layers/feeds/server/utils/formats.test.ts +71 -0
- package/layers/forms/app/components/Form/Field.vue +42 -30
- package/layers/forms/app/utils/fieldProps.ts +65 -0
- package/layers/layout/app/components/Layout/Grid/Item.vue +29 -146
- package/layers/layout/app/utils/gridPlacementStyle.ts +195 -0
- package/layers/mailer/app/types/mailer.ts +7 -25
- package/layers/mailer/server/utils/email.ts +28 -13
- package/layers/mailer/server/utils/hooks.ts +1 -20
- package/layers/navigation/app/composables/useSite.ts +2 -9
- package/layers/navigation/app/utils/site.ts +26 -0
- package/layers/routing/app/utils/resolveRoute.test.ts +47 -0
- package/layers/routing/app/utils/resolveRoute.ts +19 -10
- package/layers/scripts/app/composables/useAnalytics.ts +8 -41
- package/layers/scripts/app/composables/useGtm.ts +6 -13
- package/layers/scripts/app/utils/scriptClients.ts +70 -0
- package/layers/scroll/app/composables/useSmoothScroll.ts +9 -43
- package/layers/scroll/app/utils/scroll.ts +103 -0
- package/layers/seo/app/composables/useSeoConfig.ts +3 -9
- package/layers/seo/app/utils/seoConfig.ts +38 -0
- package/layers/shader/app/components/Material/AmbientAurora.client.vue +11 -33
- package/layers/shader/app/components/Material/AmbientFlow.client.vue +10 -37
- package/layers/shader/app/components/Material/AmbientGradientMesh.client.vue +10 -37
- package/layers/shader/app/components/Material/AmbientNebula.client.vue +12 -37
- package/layers/shader/app/components/Material/AmbientOcean.client.vue +9 -33
- package/layers/shader/app/components/Material/Gradient.client.vue +25 -46
- package/layers/shader/app/components/Material/Image.client.vue +10 -55
- package/layers/shader/app/components/Material/Node.client.vue +18 -5
- package/layers/shader/app/components/Material/Noise.client.vue +9 -43
- package/layers/shader/app/components/Preset/ThemeBubble.client.vue +2 -1
- package/layers/shader/app/components/Preset/ThemeFlow.client.vue +2 -1
- package/layers/shader/app/components/Preset/ThemeGradient.client.vue +2 -1
- package/layers/shader/app/components/Preset/ThemeLavaLamp.client.vue +2 -1
- package/layers/shader/app/components/Preset/ThemePlasma.client.vue +2 -1
- package/layers/shader/app/components/Preset/ThemeWave.client.vue +2 -1
- package/layers/shader/app/components/Shader/Background.client.vue +44 -24
- package/layers/shader/app/composables/useAmbientMaterials.ts +5 -1
- package/layers/shader/app/composables/useShader.ts +38 -23
- package/layers/shader/app/composables/useShaderGraph.ts +11 -6
- package/layers/shader/app/composables/useShaderMixBlend.ts +4 -4
- package/layers/shader/app/composables/useShaderRuntime.ts +0 -1
- package/layers/shader/app/composables/useShaderVec2.ts +2 -4
- package/layers/shader/app/composables/useThemePreset.ts +34 -8
- package/layers/shader/app/composables/useUniformWatchers.ts +15 -0
- package/layers/shader/app/composables/useUniforms.ts +0 -1
- package/layers/shader/app/shaders/common/blend.ts +4 -4
- package/layers/shader/app/shaders/common/effects.ts +38 -21
- package/layers/shader/app/shaders/common/grain.ts +46 -49
- package/layers/shader/app/shaders/common/lighting.ts +17 -15
- package/layers/shader/app/shaders/common/math.ts +2 -4
- package/layers/shader/app/shaders/common/nodes.ts +17 -0
- package/layers/shader/app/shaders/common/palette.ts +21 -11
- package/layers/shader/app/shaders/common/patterns.ts +25 -14
- package/layers/shader/app/shaders/common/shapes.ts +97 -88
- package/layers/shader/app/shaders/common/uv.ts +33 -34
- package/layers/shader/app/shaders/createMaterial.ts +92 -78
- package/layers/shader/app/shaders/layers/paperShading.ts +22 -10
- package/layers/shader/app/shaders/layers/shaderGradient.ts +46 -21
- package/layers/shader/app/utils/tsl/tween.ts +2 -4
- package/layers/shader/package.json +5 -1
- package/layers/theme/app/components/ThemePicker/Menu.vue +3 -25
- package/layers/theme/app/composables/useThemePreferenceModels.ts +39 -0
- package/layers/theme/server/plugins/theme-fouc.ts +1 -92
- package/layers/theme/server/utils/accent-css.ts +75 -0
- package/layers/typography/app/composables/typography.ts +3 -7
- package/layers/visual/app/composables/accent.ts +2 -9
- package/layers/visual/app/composables/gradient.ts +33 -46
- package/layers/visual/app/composables/picture.ts +2 -79
- package/layers/visual/app/utils/colorTokens.ts +23 -0
- package/layers/visual/app/utils/gradientStyle.ts +41 -0
- package/layers/visual/app/utils/responsiveSizes.ts +49 -0
- package/package.json +15 -4
- package/layers/feeds/app/utils/feed-catalog.test.ts +0 -71
- package/layers/feeds/server/routes/feed/discovery.get.ts +0 -31
|
@@ -106,102 +106,116 @@ const sideMap: Record<string, Side> = {
|
|
|
106
106
|
double: DoubleSide,
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
// Factory Functions
|
|
111
|
-
// ============================================
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Create a shader material from a shader type
|
|
115
|
-
*/
|
|
116
|
-
export function createShaderMaterial<T extends ShaderType>(
|
|
109
|
+
function createShaderUniforms<T extends ShaderType>(
|
|
117
110
|
type: T,
|
|
118
|
-
shaderOptions?: ShaderOptions<T
|
|
119
|
-
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
111
|
+
shaderOptions?: ShaderOptions<T>
|
|
112
|
+
): ShaderUniforms<T> {
|
|
113
|
+
switch (type) {
|
|
114
|
+
case 'stripe':
|
|
115
|
+
return createStripeGradientUniforms(shaderOptions as StripeGradientOptions) as ShaderUniforms<T>
|
|
116
|
+
case 'meshGradient':
|
|
117
|
+
return createMeshGradientUniforms(shaderOptions as MeshGradientOptions) as ShaderUniforms<T>
|
|
118
|
+
case 'aurora':
|
|
119
|
+
return createAuroraUniforms(shaderOptions as AuroraOptions) as ShaderUniforms<T>
|
|
120
|
+
case 'shaderGradient':
|
|
121
|
+
return createShaderGradientUniforms(shaderOptions as ShaderGradientOptions) as ShaderUniforms<T>
|
|
122
|
+
case 'paperShading':
|
|
123
|
+
return createPaperShadingUniforms(shaderOptions as PaperShadingOptions) as ShaderUniforms<T>
|
|
124
|
+
}
|
|
125
|
+
}
|
|
128
126
|
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
function createShaderColorNode<T extends ShaderType>(type: T, uniforms: ShaderUniforms<T>): TSLNode {
|
|
128
|
+
switch (type) {
|
|
129
|
+
case 'stripe':
|
|
130
|
+
return stripeGradient(uniforms as StripeGradientUniforms)
|
|
131
|
+
case 'meshGradient':
|
|
132
|
+
return meshGradient(uniforms as MeshGradientUniforms)
|
|
133
|
+
case 'aurora':
|
|
134
|
+
return aurora(uniforms as AuroraUniforms)
|
|
135
|
+
case 'shaderGradient':
|
|
136
|
+
return shaderGradient(uniforms as ShaderGradientUniforms)
|
|
137
|
+
case 'paperShading':
|
|
138
|
+
return paperShading(uniforms as PaperShadingUniforms)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
131
141
|
|
|
132
|
-
|
|
142
|
+
function updateShaderUniforms<T extends ShaderType>(
|
|
143
|
+
type: T,
|
|
144
|
+
uniforms: ShaderUniforms<T>,
|
|
145
|
+
options: Partial<ShaderOptions<T>>
|
|
146
|
+
): void {
|
|
133
147
|
switch (type) {
|
|
134
148
|
case 'stripe':
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
149
|
+
updateStripeGradientUniforms(
|
|
150
|
+
uniforms as StripeGradientUniforms,
|
|
151
|
+
options as Partial<StripeGradientOptions>
|
|
152
|
+
)
|
|
153
|
+
return
|
|
140
154
|
case 'meshGradient':
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
155
|
+
updateMeshGradientUniforms(
|
|
156
|
+
uniforms as MeshGradientUniforms,
|
|
157
|
+
options as Partial<MeshGradientOptions>
|
|
158
|
+
)
|
|
159
|
+
return
|
|
146
160
|
case 'aurora':
|
|
147
|
-
uniforms
|
|
148
|
-
|
|
149
|
-
break
|
|
161
|
+
updateAuroraUniforms(uniforms as AuroraUniforms, options as Partial<AuroraOptions>)
|
|
162
|
+
return
|
|
150
163
|
case 'shaderGradient':
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
164
|
+
updateShaderGradientUniforms(
|
|
165
|
+
uniforms as ShaderGradientUniforms,
|
|
166
|
+
options as Partial<ShaderGradientOptions>
|
|
167
|
+
)
|
|
168
|
+
return
|
|
156
169
|
case 'paperShading':
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
break
|
|
162
|
-
default:
|
|
163
|
-
throw new Error(`Unknown shader type: ${type}`)
|
|
170
|
+
updatePaperShadingUniforms(
|
|
171
|
+
uniforms as PaperShadingUniforms,
|
|
172
|
+
options as Partial<PaperShadingOptions>
|
|
173
|
+
)
|
|
164
174
|
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function applyMaterialOptions(
|
|
178
|
+
material: MeshBasicNodeMaterial,
|
|
179
|
+
colorNode: TSLNode,
|
|
180
|
+
options?: MaterialOptions
|
|
181
|
+
): void {
|
|
182
|
+
const {
|
|
183
|
+
transparent = false,
|
|
184
|
+
side = 'double',
|
|
185
|
+
opacity = 1,
|
|
186
|
+
depthTest = true,
|
|
187
|
+
depthWrite = true,
|
|
188
|
+
} = options || {}
|
|
165
189
|
|
|
166
|
-
// Create material
|
|
167
|
-
const material = new MeshBasicNodeMaterial()
|
|
168
|
-
material.colorNode = transparent ? vec4(colorNode, opacity) : colorNode
|
|
169
190
|
material.transparent = transparent
|
|
170
191
|
material.side = sideMap[side] || DoubleSide
|
|
171
192
|
material.depthTest = depthTest
|
|
172
193
|
material.depthWrite = depthWrite
|
|
194
|
+
material.colorNode = transparent ? vec4(colorNode, opacity) : colorNode
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ============================================
|
|
198
|
+
// Factory Functions
|
|
199
|
+
// ============================================
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Create a shader material from a shader type
|
|
203
|
+
*/
|
|
204
|
+
export function createShaderMaterial<T extends ShaderType>(
|
|
205
|
+
type: T,
|
|
206
|
+
shaderOptions?: ShaderOptions<T>,
|
|
207
|
+
materialOptions?: MaterialOptions
|
|
208
|
+
): ShaderMaterialResult<T> {
|
|
209
|
+
const uniforms = createShaderUniforms(type, shaderOptions)
|
|
210
|
+
const colorNode = createShaderColorNode(type, uniforms)
|
|
211
|
+
|
|
212
|
+
// Create material
|
|
213
|
+
const material = new MeshBasicNodeMaterial()
|
|
214
|
+
applyMaterialOptions(material, colorNode, materialOptions)
|
|
173
215
|
|
|
174
216
|
// Update function
|
|
175
217
|
const update = (options: Partial<ShaderOptions<T>>) => {
|
|
176
|
-
|
|
177
|
-
case 'stripe':
|
|
178
|
-
updateStripeGradientUniforms(
|
|
179
|
-
uniforms as StripeGradientUniforms,
|
|
180
|
-
options as Partial<StripeGradientOptions>
|
|
181
|
-
)
|
|
182
|
-
break
|
|
183
|
-
case 'meshGradient':
|
|
184
|
-
updateMeshGradientUniforms(
|
|
185
|
-
uniforms as MeshGradientUniforms,
|
|
186
|
-
options as Partial<MeshGradientOptions>
|
|
187
|
-
)
|
|
188
|
-
break
|
|
189
|
-
case 'aurora':
|
|
190
|
-
updateAuroraUniforms(uniforms as AuroraUniforms, options as Partial<AuroraOptions>)
|
|
191
|
-
break
|
|
192
|
-
case 'shaderGradient':
|
|
193
|
-
updateShaderGradientUniforms(
|
|
194
|
-
uniforms as ShaderGradientUniforms,
|
|
195
|
-
options as Partial<ShaderGradientOptions>
|
|
196
|
-
)
|
|
197
|
-
break
|
|
198
|
-
case 'paperShading':
|
|
199
|
-
updatePaperShadingUniforms(
|
|
200
|
-
uniforms as PaperShadingUniforms,
|
|
201
|
-
options as Partial<PaperShadingOptions>
|
|
202
|
-
)
|
|
203
|
-
break
|
|
204
|
-
}
|
|
218
|
+
updateShaderUniforms(type, uniforms, options)
|
|
205
219
|
}
|
|
206
220
|
|
|
207
221
|
// Dispose function
|
|
@@ -9,6 +9,18 @@ import { grain as grainFn, paperTexture } from '../common/grain'
|
|
|
9
9
|
import { fbm2D, simplexNoise2D } from '../common/noise'
|
|
10
10
|
import type { FloatUniform, TSLNode } from '../types'
|
|
11
11
|
|
|
12
|
+
const DEFAULT_PAPER_SHADING_OPTIONS = {
|
|
13
|
+
paperColor: '#f5f0e6',
|
|
14
|
+
inkColor: '#1a1a1a',
|
|
15
|
+
inkColor2: '#3d5a80',
|
|
16
|
+
speed: 0.1,
|
|
17
|
+
textureIntensity: 0.15,
|
|
18
|
+
flowIntensity: 1,
|
|
19
|
+
grainIntensity: 0.08,
|
|
20
|
+
bleed: 0.5,
|
|
21
|
+
edgeDarkness: 0.3,
|
|
22
|
+
} as const
|
|
23
|
+
|
|
12
24
|
export type PaperShadingOptions = {
|
|
13
25
|
/** Base paper color */
|
|
14
26
|
paperColor?: string
|
|
@@ -49,16 +61,16 @@ export function createPaperShadingUniforms(
|
|
|
49
61
|
options: PaperShadingOptions = {}
|
|
50
62
|
): PaperShadingUniforms {
|
|
51
63
|
const {
|
|
52
|
-
paperColor
|
|
53
|
-
inkColor
|
|
54
|
-
inkColor2
|
|
55
|
-
speed
|
|
56
|
-
textureIntensity
|
|
57
|
-
flowIntensity
|
|
58
|
-
grainIntensity
|
|
59
|
-
bleed
|
|
60
|
-
edgeDarkness
|
|
61
|
-
} = options
|
|
64
|
+
paperColor,
|
|
65
|
+
inkColor,
|
|
66
|
+
inkColor2,
|
|
67
|
+
speed,
|
|
68
|
+
textureIntensity,
|
|
69
|
+
flowIntensity,
|
|
70
|
+
grainIntensity,
|
|
71
|
+
bleed,
|
|
72
|
+
edgeDarkness,
|
|
73
|
+
} = { ...DEFAULT_PAPER_SHADING_OPTIONS, ...options }
|
|
62
74
|
|
|
63
75
|
return {
|
|
64
76
|
paperColor: uniform(new Color(paperColor)),
|
|
@@ -8,6 +8,31 @@ import { cos, mix, pow, sin, smoothstep, time as tslTime, uniform, uv, vec2, vec
|
|
|
8
8
|
import { fbm2D, simplexNoise2D } from '../common/noise'
|
|
9
9
|
import type { FloatUniform, TSLNode } from '../types'
|
|
10
10
|
|
|
11
|
+
const DEFAULT_SHADER_GRADIENT_OPTIONS = {
|
|
12
|
+
color1: '#ff5722',
|
|
13
|
+
color2: '#673ab7',
|
|
14
|
+
color3: '#2196f3',
|
|
15
|
+
speed: 0.4,
|
|
16
|
+
morphIntensity: 1,
|
|
17
|
+
grain: 0.03,
|
|
18
|
+
lightX: 0.5,
|
|
19
|
+
lightY: 0.5,
|
|
20
|
+
brightness: 1,
|
|
21
|
+
zoom: 1,
|
|
22
|
+
} as const
|
|
23
|
+
|
|
24
|
+
function setNumberUniform(uniform: FloatUniform, value: number | undefined): void {
|
|
25
|
+
if (value !== undefined) {
|
|
26
|
+
uniform.value = value
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function setColorUniform(uniform: TSLNode, value: string | undefined): void {
|
|
31
|
+
if (value !== undefined) {
|
|
32
|
+
uniform.value = new Color(value)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
11
36
|
export type ShaderGradientOptions = {
|
|
12
37
|
/** Primary color */
|
|
13
38
|
color1?: string
|
|
@@ -51,17 +76,17 @@ export function createShaderGradientUniforms(
|
|
|
51
76
|
options: ShaderGradientOptions = {}
|
|
52
77
|
): ShaderGradientUniforms {
|
|
53
78
|
const {
|
|
54
|
-
color1
|
|
55
|
-
color2
|
|
56
|
-
color3
|
|
57
|
-
speed
|
|
58
|
-
morphIntensity
|
|
59
|
-
grain
|
|
60
|
-
lightX
|
|
61
|
-
lightY
|
|
62
|
-
brightness
|
|
63
|
-
zoom
|
|
64
|
-
} = options
|
|
79
|
+
color1,
|
|
80
|
+
color2,
|
|
81
|
+
color3,
|
|
82
|
+
speed,
|
|
83
|
+
morphIntensity,
|
|
84
|
+
grain,
|
|
85
|
+
lightX,
|
|
86
|
+
lightY,
|
|
87
|
+
brightness,
|
|
88
|
+
zoom,
|
|
89
|
+
} = { ...DEFAULT_SHADER_GRADIENT_OPTIONS, ...options }
|
|
65
90
|
|
|
66
91
|
return {
|
|
67
92
|
color1: uniform(new Color(color1)),
|
|
@@ -139,14 +164,14 @@ export function updateShaderGradientUniforms(
|
|
|
139
164
|
uniforms: ShaderGradientUniforms,
|
|
140
165
|
options: Partial<ShaderGradientOptions>
|
|
141
166
|
): void {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
167
|
+
setColorUniform(uniforms.color1, options.color1)
|
|
168
|
+
setColorUniform(uniforms.color2, options.color2)
|
|
169
|
+
setColorUniform(uniforms.color3, options.color3)
|
|
170
|
+
setNumberUniform(uniforms.speed, options.speed)
|
|
171
|
+
setNumberUniform(uniforms.morphIntensity, options.morphIntensity)
|
|
172
|
+
setNumberUniform(uniforms.grain, options.grain)
|
|
173
|
+
setNumberUniform(uniforms.lightX, options.lightX)
|
|
174
|
+
setNumberUniform(uniforms.lightY, options.lightY)
|
|
175
|
+
setNumberUniform(uniforms.brightness, options.brightness)
|
|
176
|
+
setNumberUniform(uniforms.zoom, options.zoom)
|
|
152
177
|
}
|
|
@@ -14,11 +14,9 @@ export type TweenHandle = {
|
|
|
14
14
|
*/
|
|
15
15
|
export function tweenUniform(
|
|
16
16
|
uniformNode: { value: number },
|
|
17
|
-
from: number,
|
|
18
|
-
to: number,
|
|
19
|
-
duration: number,
|
|
20
|
-
easing?: (t: number) => number
|
|
17
|
+
...args: [from: number, to: number, duration: number, easing?: (t: number) => number]
|
|
21
18
|
): TweenHandle {
|
|
19
|
+
const [from, to, duration, easing] = args
|
|
22
20
|
let startTime: number | null = null
|
|
23
21
|
let cancelled = false
|
|
24
22
|
let resolvePromise: () => void
|
|
@@ -1,30 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
|
-
contrastOverride,
|
|
4
|
-
motionOverride,
|
|
5
|
-
transparencyOverride,
|
|
6
|
-
setContrastOverride,
|
|
7
|
-
setMotionOverride,
|
|
8
|
-
setTransparencyOverride,
|
|
9
|
-
effectiveHighContrast,
|
|
10
|
-
effectiveReducedMotion,
|
|
11
|
-
effectiveReducedTransparency,
|
|
12
|
-
} = useTheme()
|
|
2
|
+
import { useThemePreferenceModels } from '../../composables/useThemePreferenceModels'
|
|
13
3
|
|
|
14
|
-
const contrastModel =
|
|
15
|
-
|
|
16
|
-
set: (val: boolean) => setContrastOverride(val ? 'on' : 'system'),
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
const motionModel = computed({
|
|
20
|
-
get: () => effectiveReducedMotion.value,
|
|
21
|
-
set: (val: boolean) => setMotionOverride(val ? 'on' : 'system'),
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
const transparencyModel = computed({
|
|
25
|
-
get: () => effectiveReducedTransparency.value,
|
|
26
|
-
set: (val: boolean) => setTransparencyOverride(val ? 'on' : 'system'),
|
|
27
|
-
})
|
|
4
|
+
const { contrastOverride, motionOverride, transparencyOverride, contrastModel, motionModel, transparencyModel } =
|
|
5
|
+
useThemePreferenceModels()
|
|
28
6
|
</script>
|
|
29
7
|
|
|
30
8
|
<template>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { computed } from 'vue'
|
|
2
|
+
|
|
3
|
+
export function useThemePreferenceModels() {
|
|
4
|
+
const {
|
|
5
|
+
contrastOverride,
|
|
6
|
+
motionOverride,
|
|
7
|
+
transparencyOverride,
|
|
8
|
+
setContrastOverride,
|
|
9
|
+
setMotionOverride,
|
|
10
|
+
setTransparencyOverride,
|
|
11
|
+
effectiveHighContrast,
|
|
12
|
+
effectiveReducedMotion,
|
|
13
|
+
effectiveReducedTransparency,
|
|
14
|
+
} = useTheme()
|
|
15
|
+
|
|
16
|
+
const contrastModel = computed({
|
|
17
|
+
get: () => effectiveHighContrast.value,
|
|
18
|
+
set: (value: boolean) => setContrastOverride(value ? 'on' : 'system'),
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const motionModel = computed({
|
|
22
|
+
get: () => effectiveReducedMotion.value,
|
|
23
|
+
set: (value: boolean) => setMotionOverride(value ? 'on' : 'system'),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const transparencyModel = computed({
|
|
27
|
+
get: () => effectiveReducedTransparency.value,
|
|
28
|
+
set: (value: boolean) => setTransparencyOverride(value ? 'on' : 'system'),
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
contrastOverride,
|
|
33
|
+
motionOverride,
|
|
34
|
+
transparencyOverride,
|
|
35
|
+
contrastModel,
|
|
36
|
+
motionModel,
|
|
37
|
+
transparencyModel,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
type DefaultColors = typeof twColors
|
|
1
|
+
import { buildAccentCSS } from '../utils/accent-css'
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* Nitro render hook — prevents FOUC for all theme preferences by injecting:
|
|
@@ -24,95 +22,6 @@ type DefaultColors = typeof twColors
|
|
|
24
22
|
* set the same value again — this is idempotent and safe.
|
|
25
23
|
*/
|
|
26
24
|
|
|
27
|
-
const ACCENTS = [
|
|
28
|
-
'red',
|
|
29
|
-
'orange',
|
|
30
|
-
'amber',
|
|
31
|
-
'yellow',
|
|
32
|
-
'lime',
|
|
33
|
-
'green',
|
|
34
|
-
'emerald',
|
|
35
|
-
'teal',
|
|
36
|
-
'cyan',
|
|
37
|
-
'sky',
|
|
38
|
-
'blue',
|
|
39
|
-
'indigo',
|
|
40
|
-
'violet',
|
|
41
|
-
'purple',
|
|
42
|
-
'fuchsia',
|
|
43
|
-
'pink',
|
|
44
|
-
'rose',
|
|
45
|
-
] as const
|
|
46
|
-
|
|
47
|
-
type AccentName = (typeof ACCENTS)[number]
|
|
48
|
-
|
|
49
|
-
const SHADES = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] as const
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Coordinated three-colour palettes. Each accent colour selects a
|
|
53
|
-
* secondary and info (accent) colour that sit in the same temperature
|
|
54
|
-
* zone and feel cohesive together.
|
|
55
|
-
*
|
|
56
|
-
* primary = the user-selected accent
|
|
57
|
-
* secondary = a related colour in the same hue family
|
|
58
|
-
* info = a third complementary accent
|
|
59
|
-
*/
|
|
60
|
-
const ACCENT_PALETTES: Record<AccentName, { secondary: AccentName; info: AccentName }> = {
|
|
61
|
-
red: { secondary: 'rose', info: 'orange' },
|
|
62
|
-
orange: { secondary: 'amber', info: 'red' },
|
|
63
|
-
amber: { secondary: 'orange', info: 'yellow' },
|
|
64
|
-
yellow: { secondary: 'lime', info: 'amber' },
|
|
65
|
-
lime: { secondary: 'green', info: 'yellow' },
|
|
66
|
-
green: { secondary: 'teal', info: 'emerald' },
|
|
67
|
-
emerald: { secondary: 'green', info: 'teal' },
|
|
68
|
-
teal: { secondary: 'cyan', info: 'emerald' },
|
|
69
|
-
cyan: { secondary: 'sky', info: 'teal' },
|
|
70
|
-
sky: { secondary: 'blue', info: 'cyan' },
|
|
71
|
-
blue: { secondary: 'indigo', info: 'sky' },
|
|
72
|
-
indigo: { secondary: 'violet', info: 'blue' },
|
|
73
|
-
violet: { secondary: 'purple', info: 'indigo' },
|
|
74
|
-
purple: { secondary: 'fuchsia', info: 'violet' },
|
|
75
|
-
fuchsia: { secondary: 'pink', info: 'purple' },
|
|
76
|
-
pink: { secondary: 'rose', info: 'fuchsia' },
|
|
77
|
-
rose: { secondary: 'pink', info: 'red' },
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function getShades(name: AccentName): Record<number, string> | null {
|
|
81
|
-
const p = (twColors as DefaultColors)[name]
|
|
82
|
-
return !p || typeof p === 'string' ? null : (p as Record<number, string>)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Pre-compute all CSS rules at startup (once)
|
|
86
|
-
function buildAccentCSS(): string {
|
|
87
|
-
let css = ''
|
|
88
|
-
for (const accent of ACCENTS) {
|
|
89
|
-
const primary = getShades(accent)
|
|
90
|
-
if (!primary) continue
|
|
91
|
-
|
|
92
|
-
const { secondary: secName, info: infoName } = ACCENT_PALETTES[accent]
|
|
93
|
-
const secondary = getShades(secName)
|
|
94
|
-
const info = getShades(infoName)
|
|
95
|
-
|
|
96
|
-
let vars = ''
|
|
97
|
-
for (const s of SHADES) {
|
|
98
|
-
vars += `--ui-color-primary-${s}:${primary[s]};`
|
|
99
|
-
}
|
|
100
|
-
if (secondary) {
|
|
101
|
-
for (const s of SHADES) {
|
|
102
|
-
vars += `--ui-color-secondary-${s}:${secondary[s]};`
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (info) {
|
|
106
|
-
for (const s of SHADES) {
|
|
107
|
-
vars += `--ui-color-info-${s}:${info[s]};`
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
css += `html[data-theme-colour="${accent}"]{${vars}}`
|
|
112
|
-
}
|
|
113
|
-
return css
|
|
114
|
-
}
|
|
115
|
-
|
|
116
25
|
const accentCSS = buildAccentCSS()
|
|
117
26
|
|
|
118
27
|
// Blocking init script — restores data-* attributes from localStorage before first paint.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import twColors from 'tailwindcss/colors'
|
|
2
|
+
|
|
3
|
+
type DefaultColors = typeof twColors
|
|
4
|
+
|
|
5
|
+
const ACCENTS = [
|
|
6
|
+
'red',
|
|
7
|
+
'orange',
|
|
8
|
+
'amber',
|
|
9
|
+
'yellow',
|
|
10
|
+
'lime',
|
|
11
|
+
'green',
|
|
12
|
+
'emerald',
|
|
13
|
+
'teal',
|
|
14
|
+
'cyan',
|
|
15
|
+
'sky',
|
|
16
|
+
'blue',
|
|
17
|
+
'indigo',
|
|
18
|
+
'violet',
|
|
19
|
+
'purple',
|
|
20
|
+
'fuchsia',
|
|
21
|
+
'pink',
|
|
22
|
+
'rose',
|
|
23
|
+
] as const
|
|
24
|
+
|
|
25
|
+
type AccentName = (typeof ACCENTS)[number]
|
|
26
|
+
|
|
27
|
+
const SHADES = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] as const
|
|
28
|
+
|
|
29
|
+
const ACCENT_PALETTES: Record<AccentName, { secondary: AccentName; info: AccentName }> = {
|
|
30
|
+
red: { secondary: 'rose', info: 'orange' },
|
|
31
|
+
orange: { secondary: 'amber', info: 'red' },
|
|
32
|
+
amber: { secondary: 'orange', info: 'yellow' },
|
|
33
|
+
yellow: { secondary: 'lime', info: 'amber' },
|
|
34
|
+
lime: { secondary: 'green', info: 'yellow' },
|
|
35
|
+
green: { secondary: 'teal', info: 'emerald' },
|
|
36
|
+
emerald: { secondary: 'green', info: 'teal' },
|
|
37
|
+
teal: { secondary: 'cyan', info: 'emerald' },
|
|
38
|
+
cyan: { secondary: 'sky', info: 'teal' },
|
|
39
|
+
sky: { secondary: 'blue', info: 'cyan' },
|
|
40
|
+
blue: { secondary: 'indigo', info: 'sky' },
|
|
41
|
+
indigo: { secondary: 'violet', info: 'blue' },
|
|
42
|
+
violet: { secondary: 'purple', info: 'indigo' },
|
|
43
|
+
purple: { secondary: 'fuchsia', info: 'violet' },
|
|
44
|
+
fuchsia: { secondary: 'pink', info: 'purple' },
|
|
45
|
+
pink: { secondary: 'rose', info: 'fuchsia' },
|
|
46
|
+
rose: { secondary: 'pink', info: 'red' },
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resolveAccentShades(name: AccentName): Record<number, string> | null {
|
|
50
|
+
const palette = (twColors as DefaultColors)[name]
|
|
51
|
+
return !palette || typeof palette === 'string' ? null : (palette as Record<number, string>)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function buildColourVariables(prefix: string, colours: Record<number, string>) {
|
|
55
|
+
return SHADES.map((shade) => `--ui-color-${prefix}-${shade}:${colours[shade]};`).join('')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildAccentRule(accent: AccentName) {
|
|
59
|
+
const primary = resolveAccentShades(accent)
|
|
60
|
+
if (!primary) return ''
|
|
61
|
+
|
|
62
|
+
const { secondary: secondaryName, info: infoName } = ACCENT_PALETTES[accent]
|
|
63
|
+
const secondary = resolveAccentShades(secondaryName)
|
|
64
|
+
const info = resolveAccentShades(infoName)
|
|
65
|
+
|
|
66
|
+
let vars = buildColourVariables('primary', primary)
|
|
67
|
+
if (secondary) vars += buildColourVariables('secondary', secondary)
|
|
68
|
+
if (info) vars += buildColourVariables('info', info)
|
|
69
|
+
|
|
70
|
+
return `html[data-theme-colour="${accent}"]{${vars}}`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function buildAccentCSS(): string {
|
|
74
|
+
return ACCENTS.map(buildAccentRule).join('')
|
|
75
|
+
}
|
|
@@ -24,13 +24,9 @@ function normalizeAxis(
|
|
|
24
24
|
) {
|
|
25
25
|
const { prefix = '', fallback = '', numericFormatter } = options
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return `[${value}]`
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return value ?? fallback
|
|
27
|
+
return typeof value === 'number'
|
|
28
|
+
? numericFormatter?.(value) ?? (prefix ? `${prefix}-[${value}]` : `[${value}]`)
|
|
29
|
+
: value ?? fallback
|
|
34
30
|
}
|
|
35
31
|
|
|
36
32
|
function getSizeClass(size: FontSize | undefined): string {
|
|
@@ -2,6 +2,7 @@ import type { ComputedRef, CSSProperties, MaybeRefOrGetter } from 'vue'
|
|
|
2
2
|
import { toValue } from 'vue'
|
|
3
3
|
|
|
4
4
|
import type { BlobBlur, BlobConfig } from '../types/accent'
|
|
5
|
+
import { resolveUiColorToken } from '../utils/colorTokens'
|
|
5
6
|
|
|
6
7
|
const BLUR_PX_MAP: Record<string, number> = {
|
|
7
8
|
none: 0,
|
|
@@ -18,14 +19,6 @@ function resolveBlurPx(blur: BlobBlur = '3xl'): number {
|
|
|
18
19
|
return BLUR_PX_MAP[blur] ?? 64
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
function resolveColor(config: BlobConfig): string {
|
|
22
|
-
const { color = 'primary', shade = 500, customColor } = config
|
|
23
|
-
if (color === 'custom') return customColor ?? 'transparent'
|
|
24
|
-
if (color === 'white') return '#ffffff'
|
|
25
|
-
if (color === 'black') return '#000000'
|
|
26
|
-
return `var(--ui-color-${color}-${shade})`
|
|
27
|
-
}
|
|
28
|
-
|
|
29
22
|
export function useAccentBlob(config: MaybeRefOrGetter<BlobConfig>): {
|
|
30
23
|
style: ComputedRef<CSSProperties>
|
|
31
24
|
} {
|
|
@@ -42,7 +35,7 @@ export function useAccentBlob(config: MaybeRefOrGetter<BlobConfig>): {
|
|
|
42
35
|
transform: 'translate(-50%, -50%)',
|
|
43
36
|
width: size,
|
|
44
37
|
height: size,
|
|
45
|
-
backgroundColor:
|
|
38
|
+
backgroundColor: resolveUiColorToken(resolved),
|
|
46
39
|
opacity,
|
|
47
40
|
borderRadius: '9999px',
|
|
48
41
|
filter: blurPx > 0 ? `blur(${blurPx}px)` : undefined,
|