kmcom-nuxt-layers 2.6.3 → 2.7.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/docs/ARCHITECTURE.md +21 -17
- package/docs/FEEDS.md +42 -34
- package/docs/IMPROVE-AUDIT-README.md +9 -10
- package/docs/IMPROVE-AUDIT-RESULTS.md +16 -17
- package/docs/IMPROVE-DEEP-AUDIT-RESULTS.md +29 -29
- package/docs/LAYERS-FIXES.md +1 -0
- package/docs/LAYOUT.md +91 -86
- package/docs/MIGRATION.md +93 -60
- package/docs/UPAGE-GRID-INTEGRATION.md +12 -10
- package/docs/USAGE.md +29 -38
- package/layers/animations/app/utils/pointerMotion.ts +1 -4
- package/layers/baseline/app/components/BaselineStatus.vue +13 -13
- package/layers/baseline/app/plugins/baseline-status.client.ts +4 -1
- package/layers/canvas/app/composables/useRendererCapabilities.ts +2 -2
- package/layers/content/app/components/Blog/Card.vue +1 -0
- package/layers/content/app/components/Gallery/Card.vue +1 -1
- package/layers/content/app/components/Gallery/Detail.vue +8 -1
- package/layers/content/app/components/Portfolio/Card.vue +1 -0
- package/layers/content/app/components/Portfolio/ColorPalette.vue +8 -1
- package/layers/content/app/components/Portfolio/Typography.vue +12 -2
- package/layers/content/app/composables/useGalleryItems.ts +3 -2
- package/layers/content/app/composables/usePortfolioItems.ts +3 -2
- package/layers/core/app/components/DiagnosticsPage.vue +15 -3
- package/layers/core/app/utils/featureClasses.test.ts +1 -3
- package/layers/core/app/utils/featureClasses.ts +1 -3
- package/layers/core/app/utils/helpers.test.ts +6 -2
- package/layers/core/nuxt.config.ts +2 -0
- package/layers/feeds/app/assets/css/feeds.css +3 -2
- package/layers/feeds/app/components/Feeds/Index.vue +36 -6
- package/layers/feeds/app/components/Feeds/RouteCard.vue +1 -3
- package/layers/feeds/app/plugins/feed-head.ts +2 -2
- package/layers/feeds/package.json +1 -1
- package/layers/feeds/public/feed/style.css +9 -9
- package/layers/feeds/server/utils/content-adapter.test.ts +3 -4
- package/layers/feeds/server/utils/content-adapter.ts +1 -1
- package/layers/feeds/server/utils/feed-author.test.ts +3 -3
- package/layers/feeds/server/utils/feed-config.test.ts +28 -6
- package/layers/feeds/server/utils/feed-config.ts +1 -4
- package/layers/feeds/server/utils/feed-service.ts +1 -1
- package/layers/feeds/server/utils/formats/rss.ts +1 -1
- package/layers/feeds/server/utils/formats.test.ts +1 -1
- package/layers/forms/app/utils/fieldProps.ts +1 -2
- package/layers/forms/server/api/contact.post.ts +4 -1
- package/layers/layout/app/assets/css/layout/grids.css +12 -3
- package/layers/layout/app/assets/css/layout/modes/fluid.css +9 -0
- package/layers/layout/app/components/Layout/Section/Sidebar.vue +11 -4
- package/layers/layout/app/utils/gridPlacementStyle.test.ts +3 -9
- package/layers/layout/app/utils/gridPlacementStyle.ts +10 -7
- package/layers/mailer/server/utils/email.ts +4 -3
- package/layers/mailer/server/utils/hooks.ts +1 -1
- package/layers/metadata/app/composables/useMetadataItem.ts +6 -2
- package/layers/metadata/app/composables/useMetadataSearch.ts +6 -2
- package/layers/metadata/providers/comicvine/server/utils/metadata-comicvine/client.ts +5 -5
- package/layers/metadata/providers/comicvine/server/utils/metadata-comicvine/provider.ts +11 -3
- package/layers/metadata/providers/google-books/server/utils/metadata-google-books/client.ts +9 -6
- package/layers/metadata/providers/google-books/server/utils/metadata-google-books/provider.ts +6 -3
- package/layers/metadata/providers/openlibrary/server/utils/metadata-openlibrary/normalise.test.ts +3 -1
- package/layers/metadata/providers/openlibrary/server/utils/metadata-openlibrary/provider.ts +4 -2
- package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/client.test.ts +1 -3
- package/layers/metadata/providers/themoviedb/server/utils/metadata-tmdb/provider.ts +3 -1
- package/layers/metadata/server/api/metadata/status.get.ts +13 -18
- package/layers/metadata/server/utils/metadata/provider-registry.test.ts +1 -1
- package/layers/metadata/server/utils/metadata/search.test.ts +18 -18
- package/layers/metadata/server/utils/metadata/search.ts +16 -8
- package/layers/navigation/app/utils/site.test.ts +1 -3
- package/layers/scroll/app/components/Motion/PinnedSection.vue +11 -1
- package/layers/scroll/app/composables/useSectionProgress.ts +1 -1
- package/layers/scroll/app/plugins/locomotive-scroll.client.ts +6 -0
- package/layers/scroll/app/utils/scroll.ts +5 -1
- package/layers/seo/app/utils/seoConfig.test.ts +1 -1
- package/layers/shader/app/assets/css/shader.css +1 -1
- package/layers/shader/app/components/Material/AmbientAurora.client.vue +16 -4
- package/layers/shader/app/components/Material/AmbientFlow.client.vue +21 -5
- package/layers/shader/app/components/Material/AmbientGradientMesh.client.vue +21 -5
- package/layers/shader/app/components/Material/AmbientNebula.client.vue +21 -5
- package/layers/shader/app/components/Material/AmbientOcean.client.vue +16 -4
- package/layers/shader/app/components/Material/Gradient.client.vue +1 -1
- package/layers/shader/app/components/Material/Image.client.vue +1 -1
- package/layers/shader/app/components/Material/Noise.client.vue +11 -3
- package/layers/shader/app/composables/useAmbientMaterials.ts +0 -1
- package/layers/shader/app/shaders/common/blend.ts +5 -1
- package/layers/shader/app/shaders/common/grain.ts +5 -1
- package/layers/shader/app/shaders/common/lighting.ts +1 -6
- package/layers/shader/app/shaders/common/noise.ts +3 -1
- package/layers/shader/app/shaders/common/patterns.ts +1 -1
- package/layers/shader/app/shaders/common/shapes.ts +5 -1
- package/layers/shader/app/shaders/common/uv.ts +19 -2
- package/layers/shader/app/shaders/createMaterial.ts +11 -6
- package/layers/shader/app/shaders/layers/shaderGradient.ts +2 -12
- package/layers/shader/app/shaders/types.ts +1 -1
- package/layers/shader/app/types/uniforms.ts +1 -0
- package/layers/shader/app/utils/tsl/animation.ts +34 -36
- package/layers/starter/app/components/StarterDesignSystem.vue +120 -120
- package/layers/starter/app/components/StarterHome.vue +233 -208
- package/layers/theme/app/assets/css/theme.css +1 -1
- package/layers/theme/server/utils/accent-css.test.ts +17 -2
- package/layers/theme/server/utils/fouc-config.test.ts +7 -3
- package/layers/theme/server/utils/fouc-config.ts +24 -12
- package/layers/transitions/app/assets/css/transitions.css +6 -0
- package/layers/typography/app/assets/css/typography.css +313 -0
- package/layers/typography/app/components/Typography/Headline.vue +23 -14
- package/layers/typography/app/components/Typography/HeadlineScreen.vue +5 -12
- package/layers/typography/app/components/Typography/index.vue +4 -0
- package/layers/typography/app/composables/typography.ts +45 -5
- package/layers/typography/app/types/typography.ts +36 -7
- package/layers/typography/nuxt.config.ts +2 -0
- package/layers/visual/app/app.config.ts +1 -1
- package/layers/visual/app/assets/css/visual.css +1 -1
- package/layers/visual/app/composables/accent.ts +1 -2
- package/layers/visual/app/composables/tint.ts +1 -2
- package/layers/visual/app/types/media.ts +2 -2
- package/layers/visual/app/utils/gradientStyle.test.ts +1 -2
- package/layers/visual/app/utils/responsiveSizes.ts +0 -1
- package/package.json +33 -13
|
@@ -43,6 +43,7 @@ Every direct child that doesn't explicitly span rows becomes ~8.5vh tall. `<UPag
|
|
|
43
43
|
The mobile breakpoint already uses this. Apply it at all breakpoints.
|
|
44
44
|
|
|
45
45
|
**What changes:**
|
|
46
|
+
|
|
46
47
|
- `<LayoutSection>` spanning 12 rows = at least `12 × 8.5vh ≈ 100vh` (min preserved)
|
|
47
48
|
- `<UPage>` in a single row = auto-sized to its content height
|
|
48
49
|
- Column alignment is fully preserved
|
|
@@ -69,6 +70,7 @@ ui: {
|
|
|
69
70
|
With `display: contents`, `<UPage>` generates no box — its children (`UPageHero`, `UPageSection`, etc.) become direct grid items in `mastmain`.
|
|
70
71
|
|
|
71
72
|
**What changes:**
|
|
73
|
+
|
|
72
74
|
- `<UPage>` styling is effectively gone (use with care)
|
|
73
75
|
- Each child component becomes a grid item and needs its own column/row spanning
|
|
74
76
|
- Requires configuring `UPageSection`, `UPageHero`, etc. similarly
|
|
@@ -84,10 +86,10 @@ A new component that gives pages an explicit choice between Swiss Grid and norma
|
|
|
84
86
|
```vue
|
|
85
87
|
<!-- layers/layout/app/components/Layout/Page/index.vue -->
|
|
86
88
|
<script setup lang="ts">
|
|
87
|
-
interface Props {
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
const { mode = 'swiss' } = defineProps<Props>()
|
|
89
|
+
interface Props {
|
|
90
|
+
mode?: 'swiss' | 'flow'
|
|
91
|
+
}
|
|
92
|
+
const { mode = 'swiss' } = defineProps<Props>()
|
|
91
93
|
</script>
|
|
92
94
|
|
|
93
95
|
<template>
|
|
@@ -178,9 +180,9 @@ Don't apply `<LayoutMain>` in the layouts at all. Let pages that want the Swiss
|
|
|
178
180
|
|
|
179
181
|
## CSS Grid Behaviour Reference
|
|
180
182
|
|
|
181
|
-
| Scenario
|
|
182
|
-
|
|
183
|
-
| `<LayoutSection>` (spans 12 rows) | ✅ Exactly 100vh
|
|
184
|
-
| `<UPage>` (1 auto row)
|
|
185
|
-
| `<LayoutGridItem rowStart="3">`
|
|
186
|
-
| `<LayoutGridItem rowSpan="6">`
|
|
183
|
+
| Scenario | `grid-auto-rows: 8.5vh` | `grid-auto-rows: minmax(8.5vh, auto)` |
|
|
184
|
+
| --------------------------------- | ----------------------- | ------------------------------------- |
|
|
185
|
+
| `<LayoutSection>` (spans 12 rows) | ✅ Exactly 100vh | ✅ At least 100vh, grows with content |
|
|
186
|
+
| `<UPage>` (1 auto row) | ❌ Collapsed to 8.5vh | ✅ Grows to content height |
|
|
187
|
+
| `<LayoutGridItem rowStart="3">` | ✅ Predictable position | ⚠️ Position shifts if rows above grew |
|
|
188
|
+
| `<LayoutGridItem rowSpan="6">` | ✅ 6 × 8.5vh = 51vh | ✅ At least 6 × 8.5vh |
|
package/docs/USAGE.md
CHANGED
|
@@ -15,10 +15,7 @@ Add layers to the `extends` array in your `nuxt.config.ts`. Each layer is refere
|
|
|
15
15
|
```ts
|
|
16
16
|
// nuxt.config.ts
|
|
17
17
|
export default defineNuxtConfig({
|
|
18
|
-
extends: [
|
|
19
|
-
'kmcom-nuxt-layers/layers/core',
|
|
20
|
-
'kmcom-nuxt-layers/layers/ui',
|
|
21
|
-
]
|
|
18
|
+
extends: ['kmcom-nuxt-layers/layers/core', 'kmcom-nuxt-layers/layers/ui'],
|
|
22
19
|
})
|
|
23
20
|
```
|
|
24
21
|
|
|
@@ -26,16 +23,16 @@ Nuxt resolves each layer via the `package.json` → `"main": "./nuxt.config.ts"`
|
|
|
26
23
|
|
|
27
24
|
## Available Layers
|
|
28
25
|
|
|
29
|
-
| Layer
|
|
30
|
-
|
|
31
|
-
| **core**
|
|
32
|
-
| **ui**
|
|
33
|
-
| **layout**
|
|
34
|
-
| **motion**
|
|
35
|
-
| **shader**
|
|
36
|
-
| **forms**
|
|
37
|
-
| **theme**
|
|
38
|
-
| **content** | `kmcom-nuxt-layers/layers/content` | core
|
|
26
|
+
| Layer | Extends Path | Depends On | Required Dependencies |
|
|
27
|
+
| ----------- | ---------------------------------- | ---------- | ------------------------------------------------------------------------------------- |
|
|
28
|
+
| **core** | `kmcom-nuxt-layers/layers/core` | — | `nuxt`, `@nuxt/ui`, `pinia`, `@vueuse/nuxt`, `@nuxtjs/device` |
|
|
29
|
+
| **ui** | `kmcom-nuxt-layers/layers/ui` | core | — |
|
|
30
|
+
| **layout** | `kmcom-nuxt-layers/layers/layout` | core | — |
|
|
31
|
+
| **motion** | `kmcom-nuxt-layers/layers/motion` | core | `gsap`, `locomotive-scroll` |
|
|
32
|
+
| **shader** | `kmcom-nuxt-layers/layers/shader` | core | `three`, `@tresjs/nuxt`, `@tresjs/core`, `@tresjs/cientos`, `@tresjs/post-processing` |
|
|
33
|
+
| **forms** | `kmcom-nuxt-layers/layers/forms` | core | `zod` |
|
|
34
|
+
| **theme** | `kmcom-nuxt-layers/layers/theme` | core | — |
|
|
35
|
+
| **content** | `kmcom-nuxt-layers/layers/content` | core | `@nuxt/content`, `better-sqlite3` |
|
|
39
36
|
|
|
40
37
|
## Layer Dependency Rules
|
|
41
38
|
|
|
@@ -67,10 +64,7 @@ npm install @nuxt/content better-sqlite3
|
|
|
67
64
|
|
|
68
65
|
```ts
|
|
69
66
|
export default defineNuxtConfig({
|
|
70
|
-
extends: [
|
|
71
|
-
'kmcom-nuxt-layers/layers/core',
|
|
72
|
-
'kmcom-nuxt-layers/layers/ui',
|
|
73
|
-
]
|
|
67
|
+
extends: ['kmcom-nuxt-layers/layers/core', 'kmcom-nuxt-layers/layers/ui'],
|
|
74
68
|
})
|
|
75
69
|
```
|
|
76
70
|
|
|
@@ -82,7 +76,7 @@ export default defineNuxtConfig({
|
|
|
82
76
|
'kmcom-nuxt-layers/layers/core',
|
|
83
77
|
'kmcom-nuxt-layers/layers/ui',
|
|
84
78
|
'kmcom-nuxt-layers/layers/layout',
|
|
85
|
-
]
|
|
79
|
+
],
|
|
86
80
|
})
|
|
87
81
|
```
|
|
88
82
|
|
|
@@ -94,10 +88,7 @@ npm install gsap locomotive-scroll
|
|
|
94
88
|
|
|
95
89
|
```ts
|
|
96
90
|
export default defineNuxtConfig({
|
|
97
|
-
extends: [
|
|
98
|
-
'kmcom-nuxt-layers/layers/core',
|
|
99
|
-
'kmcom-nuxt-layers/layers/motion',
|
|
100
|
-
]
|
|
91
|
+
extends: ['kmcom-nuxt-layers/layers/core', 'kmcom-nuxt-layers/layers/motion'],
|
|
101
92
|
})
|
|
102
93
|
```
|
|
103
94
|
|
|
@@ -118,7 +109,7 @@ export default defineNuxtConfig({
|
|
|
118
109
|
'kmcom-nuxt-layers/layers/forms',
|
|
119
110
|
'kmcom-nuxt-layers/layers/theme',
|
|
120
111
|
'kmcom-nuxt-layers/layers/content',
|
|
121
|
-
]
|
|
112
|
+
],
|
|
122
113
|
})
|
|
123
114
|
```
|
|
124
115
|
|
|
@@ -132,39 +123,39 @@ export default defineAppConfig({
|
|
|
132
123
|
// Core layer overrides
|
|
133
124
|
coreLayer: {
|
|
134
125
|
loading: {
|
|
135
|
-
enabled: false,
|
|
126
|
+
enabled: false, // Disable loading screen
|
|
136
127
|
},
|
|
137
128
|
scrollGuard: {
|
|
138
|
-
enabled: false,
|
|
129
|
+
enabled: false, // Disable scroll guard
|
|
139
130
|
},
|
|
140
131
|
},
|
|
141
132
|
|
|
142
133
|
// Motion layer overrides
|
|
143
134
|
motion: {
|
|
144
135
|
lenis: {
|
|
145
|
-
duration: 0.8,
|
|
146
|
-
smoothTouch: true,
|
|
136
|
+
duration: 0.8, // Faster smooth scroll
|
|
137
|
+
smoothTouch: true, // Enable on touch devices
|
|
147
138
|
},
|
|
148
139
|
},
|
|
149
140
|
|
|
150
141
|
// Theme layer overrides
|
|
151
142
|
themeLayer: {
|
|
152
|
-
defaultAccent: 'indigo',
|
|
153
|
-
accents: ['blue', 'indigo', 'violet', 'purple'],
|
|
143
|
+
defaultAccent: 'indigo', // Change default accent color
|
|
144
|
+
accents: ['blue', 'indigo', 'violet', 'purple'], // Limit palette
|
|
154
145
|
},
|
|
155
146
|
})
|
|
156
147
|
```
|
|
157
148
|
|
|
158
149
|
### Available Config Namespaces
|
|
159
150
|
|
|
160
|
-
| Namespace
|
|
161
|
-
|
|
162
|
-
| `coreLayer.loading`
|
|
163
|
-
| `coreLayer.scrollGuard` | core
|
|
164
|
-
| `coreLayer.errors`
|
|
165
|
-
| `coreLayer.notFound`
|
|
166
|
-
| `motion`
|
|
167
|
-
| `themeLayer`
|
|
151
|
+
| Namespace | Layer | Key Options |
|
|
152
|
+
| ----------------------- | ------ | -------------------------------------------------------------------------- |
|
|
153
|
+
| `coreLayer.loading` | core | `enabled`, `minDuration`, `maxDuration`, `background`, `textColor` |
|
|
154
|
+
| `coreLayer.scrollGuard` | core | `enabled`, `strict`, `excludeSelectors`, `debug` |
|
|
155
|
+
| `coreLayer.errors` | core | `logToConsole`, `logToExternal`, `externalUrl` |
|
|
156
|
+
| `coreLayer.notFound` | core | `icon`, `title`, `description`, `showHomeButton`, `actions`, `suggestions` |
|
|
157
|
+
| `motion` | motion | `gsapScrollTrigger`, `lenis.*` (duration, smoothWheel, smoothTouch, etc.) |
|
|
158
|
+
| `themeLayer` | theme | `accents`, `defaultAccent` |
|
|
168
159
|
|
|
169
160
|
## Optional Modules
|
|
170
161
|
|
|
@@ -12,10 +12,7 @@ export function resolvePointerSpring(
|
|
|
12
12
|
return next * (1 - damping)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function usePointerMotionFrame(
|
|
16
|
-
update: () => void,
|
|
17
|
-
cleanup?: () => void
|
|
18
|
-
) {
|
|
15
|
+
export function usePointerMotionFrame(update: () => void, cleanup?: () => void) {
|
|
19
16
|
const { gsap } = useGsap()
|
|
20
17
|
|
|
21
18
|
onMounted(() => {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { defineComponent, h } from 'vue'
|
|
2
|
+
import { defineComponent, h } from 'vue'
|
|
3
3
|
|
|
4
|
-
export default defineComponent({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
export default defineComponent({
|
|
5
|
+
name: 'BaselineStatus',
|
|
6
|
+
props: {
|
|
7
|
+
featureId: {
|
|
8
|
+
type: String,
|
|
9
|
+
required: true,
|
|
10
|
+
},
|
|
10
11
|
},
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
})
|
|
12
|
+
setup(props) {
|
|
13
|
+
if (import.meta.server) return () => null
|
|
14
|
+
return () => h('baseline-status', { featureId: props.featureId })
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
17
|
</script>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { WebGPURenderer } from 'three/webgpu'
|
|
2
|
-
import type { WebGLRenderer } from 'three'
|
|
3
1
|
import {
|
|
4
2
|
QUALITY_PRESETS,
|
|
5
3
|
type QualityLevel,
|
|
6
4
|
type QualitySettings,
|
|
7
5
|
type RendererCapabilities,
|
|
8
6
|
} from '#layers/canvas/types/renderer'
|
|
7
|
+
import type { WebGLRenderer } from 'three'
|
|
8
|
+
import type { WebGPURenderer } from 'three/webgpu'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Detect and expose renderer capabilities
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
<template #after-content="{ item }">
|
|
46
46
|
<template v-if="asGallery(item).images?.length">
|
|
47
47
|
<USeparator class="my-8" />
|
|
48
|
-
<div class="
|
|
48
|
+
<div class="gallery-grid gap-4">
|
|
49
49
|
<div
|
|
50
50
|
v-for="(img, idx) in asGallery(item).images"
|
|
51
51
|
:key="img.src"
|
|
@@ -88,3 +88,10 @@
|
|
|
88
88
|
</template>
|
|
89
89
|
</NuxtContentDetail>
|
|
90
90
|
</template>
|
|
91
|
+
|
|
92
|
+
<style scoped>
|
|
93
|
+
.gallery-grid {
|
|
94
|
+
display: grid;
|
|
95
|
+
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
|
|
96
|
+
}
|
|
97
|
+
</style>
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<template>
|
|
18
18
|
<div v-if="colors.length" class="space-y-6">
|
|
19
19
|
<h3 class="text-2xl font-semibold tracking-tight text-highlighted">Palette</h3>
|
|
20
|
-
<LinksGroup class="
|
|
20
|
+
<LinksGroup class="palette-grid gap-3">
|
|
21
21
|
<div v-for="color in colors" :key="color.value">
|
|
22
22
|
<div
|
|
23
23
|
class="aspect-2/3 flex flex-col justify-between p-4 rounded-lg border border-default/20 shadow-lg cursor-default"
|
|
@@ -33,3 +33,10 @@
|
|
|
33
33
|
</LinksGroup>
|
|
34
34
|
</div>
|
|
35
35
|
</template>
|
|
36
|
+
|
|
37
|
+
<style scoped>
|
|
38
|
+
.palette-grid {
|
|
39
|
+
display: grid;
|
|
40
|
+
grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
|
|
41
|
+
}
|
|
42
|
+
</style>
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
<div class="mt-8 flex flex-col gap-24">
|
|
13
13
|
<div v-for="font in typography" :key="font.name" class="flex flex-col gap-6">
|
|
14
14
|
<div class="flex items-baseline justify-between gap-4">
|
|
15
|
-
<p class="
|
|
15
|
+
<p class="font-name font-semibold text-highlighted tracking-tight truncate">
|
|
16
16
|
{{ font.name }}
|
|
17
17
|
</p>
|
|
18
18
|
<p v-if="font.usage" class="text-sm text-muted shrink-0">{{ font.usage }}</p>
|
|
19
19
|
</div>
|
|
20
|
-
<p class="
|
|
20
|
+
<p class="font-sample text-muted tracking-wide">ABCDEFGHabcdefgh 0123456789</p>
|
|
21
21
|
<div class="flex items-center gap-4">
|
|
22
22
|
<span class="text-xs font-mono text-muted uppercase tracking-widest opacity-50">
|
|
23
23
|
Weights
|
|
@@ -34,3 +34,13 @@
|
|
|
34
34
|
</div>
|
|
35
35
|
</div>
|
|
36
36
|
</template>
|
|
37
|
+
|
|
38
|
+
<style scoped>
|
|
39
|
+
.font-name {
|
|
40
|
+
font-size: clamp(2rem, 1.5rem + 2.5vw, 3.5rem);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.font-sample {
|
|
44
|
+
font-size: clamp(1.5rem, 1.25rem + 1.5vw, 2.5rem);
|
|
45
|
+
}
|
|
46
|
+
</style>
|
|
@@ -5,8 +5,9 @@ export function useGalleryItems(options: GalleryQueryOptions = {}) {
|
|
|
5
5
|
const key = `gallery-items:${(tags ?? []).slice().sort().join(',')}:${limit ?? ''}`
|
|
6
6
|
|
|
7
7
|
return useContentData(key, async () => {
|
|
8
|
-
let items = (await queryCollection('gallery').all())
|
|
9
|
-
|
|
8
|
+
let items = (await queryCollection('gallery').all()).sort((a, b) =>
|
|
9
|
+
(b.date ?? '').localeCompare(a.date ?? '')
|
|
10
|
+
)
|
|
10
11
|
|
|
11
12
|
if (tags?.length) {
|
|
12
13
|
items = items.filter((item) => Boolean(item.tags?.some((tag: string) => tags.includes(tag))))
|
|
@@ -5,8 +5,9 @@ export function usePortfolioItems(options: PortfolioQueryOptions = {}) {
|
|
|
5
5
|
const key = `portfolio-items:${featured ?? ''}:${(tags ?? []).slice().sort().join(',')}:${limit ?? ''}`
|
|
6
6
|
|
|
7
7
|
return useContentData(key, async () => {
|
|
8
|
-
let items = (await queryCollection('portfolio').all())
|
|
9
|
-
|
|
8
|
+
let items = (await queryCollection('portfolio').all()).sort(
|
|
9
|
+
(a, b) => (b.year ?? 0) - (a.year ?? 0)
|
|
10
|
+
)
|
|
10
11
|
|
|
11
12
|
if (featured !== undefined) {
|
|
12
13
|
items = items.filter((item) => item.featured === featured)
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
<template #header>
|
|
39
39
|
<h2 class="text-xl font-semibold">CSS Features</h2>
|
|
40
40
|
</template>
|
|
41
|
-
<div class="grid
|
|
41
|
+
<div class="diag-grid-9 gap-3">
|
|
42
42
|
<div class="flex items-center gap-2">
|
|
43
43
|
<span v-if="grid" class="text-green-500">✓</span>
|
|
44
44
|
<span v-else class="text-red-500">✗</span>
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
<template #header>
|
|
77
77
|
<h2 class="text-xl font-semibold">Image Formats</h2>
|
|
78
78
|
</template>
|
|
79
|
-
<div class="grid
|
|
79
|
+
<div class="diag-grid-8 gap-3">
|
|
80
80
|
<div class="flex items-center gap-2">
|
|
81
81
|
<span v-if="webp" class="text-green-500">✓</span>
|
|
82
82
|
<span v-else class="text-red-500">✗</span>
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
<template #header>
|
|
95
95
|
<h2 class="text-xl font-semibold">JavaScript APIs</h2>
|
|
96
96
|
</template>
|
|
97
|
-
<div class="grid
|
|
97
|
+
<div class="diag-grid-9 gap-3">
|
|
98
98
|
<div class="flex items-center gap-2">
|
|
99
99
|
<span v-if="intersectionObserver" class="text-green-500">✓</span>
|
|
100
100
|
<span v-else class="text-red-500">✗</span>
|
|
@@ -224,3 +224,15 @@
|
|
|
224
224
|
</UContainer>
|
|
225
225
|
</div>
|
|
226
226
|
</template>
|
|
227
|
+
|
|
228
|
+
<style scoped>
|
|
229
|
+
.diag-grid-9 {
|
|
230
|
+
display: grid;
|
|
231
|
+
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.diag-grid-8 {
|
|
235
|
+
display: grid;
|
|
236
|
+
grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
|
|
237
|
+
}
|
|
238
|
+
</style>
|
|
@@ -72,9 +72,7 @@ describe('getFeatureClassNames', () => {
|
|
|
72
72
|
})
|
|
73
73
|
|
|
74
74
|
it('only includes extra classes for the features that are truthy', () => {
|
|
75
|
-
const classes = getFeatureClassNames(
|
|
76
|
-
createFeatures({ intersectionObserver: true, avif: true })
|
|
77
|
-
)
|
|
75
|
+
const classes = getFeatureClassNames(createFeatures({ intersectionObserver: true, avif: true }))
|
|
78
76
|
|
|
79
77
|
expect(classes).toContain('has-intersection-observer')
|
|
80
78
|
expect(classes).toContain('supports-avif')
|
|
@@ -25,9 +25,7 @@ export function getFeatureClassNames(features: FeatureDetection): string[] {
|
|
|
25
25
|
features[key] ? supportedClass : unsupportedClass
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
-
classes.push(
|
|
29
|
-
...EXTRA_CLASS_MAP.flatMap(([key, className]) => (features[key] ? [className] : []))
|
|
30
|
-
)
|
|
28
|
+
classes.push(...EXTRA_CLASS_MAP.flatMap(([key, className]) => (features[key] ? [className] : [])))
|
|
31
29
|
|
|
32
30
|
return classes
|
|
33
31
|
}
|
|
@@ -38,11 +38,15 @@ describe('core helpers', () => {
|
|
|
38
38
|
],
|
|
39
39
|
docs: [{ kind: 'docs', id: 2 }],
|
|
40
40
|
})
|
|
41
|
-
expect(
|
|
41
|
+
expect(
|
|
42
|
+
pick({ title: 'Nuxt', description: 'Layers', slug: 'nuxt-layers' }, ['title', 'slug'])
|
|
43
|
+
).toEqual({
|
|
42
44
|
title: 'Nuxt',
|
|
43
45
|
slug: 'nuxt-layers',
|
|
44
46
|
})
|
|
45
|
-
expect(
|
|
47
|
+
expect(
|
|
48
|
+
omit({ title: 'Nuxt', description: 'Layers', slug: 'nuxt-layers' }, ['description'])
|
|
49
|
+
).toEqual({
|
|
46
50
|
title: 'Nuxt',
|
|
47
51
|
slug: 'nuxt-layers',
|
|
48
52
|
})
|
|
@@ -65,6 +65,8 @@ export default defineNuxtConfig({
|
|
|
65
65
|
meta: [
|
|
66
66
|
// <meta name="viewport" content="width=device-width, initial-scale=1">
|
|
67
67
|
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
|
68
|
+
// Opt page into browser's text-scale preference respecting default font size
|
|
69
|
+
{ name: 'text-scale', content: 'scale' },
|
|
68
70
|
],
|
|
69
71
|
|
|
70
72
|
noscript: [
|
|
@@ -35,9 +35,11 @@
|
|
|
35
35
|
.feed-badge-rss {
|
|
36
36
|
color: var(--color-orange-400, #fb923c);
|
|
37
37
|
}
|
|
38
|
+
|
|
38
39
|
.feed-badge-atom {
|
|
39
40
|
color: var(--color-purple-400, #c084fc);
|
|
40
41
|
}
|
|
42
|
+
|
|
41
43
|
.feed-badge-json {
|
|
42
44
|
color: var(--color-yellow-400, #facc15);
|
|
43
45
|
}
|
|
@@ -49,8 +51,7 @@
|
|
|
49
51
|
background-color: var(--color-gray-950, #030712);
|
|
50
52
|
padding: 1rem;
|
|
51
53
|
max-height: 24rem;
|
|
52
|
-
overflow
|
|
53
|
-
overflow-y: auto;
|
|
54
|
+
overflow: auto;
|
|
54
55
|
color: var(--color-gray-100, #f3f4f6);
|
|
55
56
|
font-size: 0.75rem;
|
|
56
57
|
line-height: 1.6;
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
<template>
|
|
18
18
|
<div
|
|
19
|
-
class="relative overflow-hidden rounded-4xl border border-amber-200/70 bg-[linear-gradient(180deg,rgba(255,251,244,0.96),rgba(243,236,223,0.95))]
|
|
19
|
+
class="feeds-shell relative overflow-hidden rounded-4xl border border-amber-200/70 bg-[linear-gradient(180deg,rgba(255,251,244,0.96),rgba(243,236,223,0.95))] shadow-[0_30px_100px_rgba(15,23,42,0.12)] dark:border-slate-800/80 dark:bg-[linear-gradient(180deg,rgba(2,6,23,0.96),rgba(3,7,18,0.98))]"
|
|
20
|
+
style="container-type: inline-size"
|
|
20
21
|
>
|
|
21
22
|
<div class="pointer-events-none absolute inset-0">
|
|
22
23
|
<div
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
</div>
|
|
32
33
|
|
|
33
34
|
<div class="relative space-y-8">
|
|
34
|
-
<div class="grid gap-6
|
|
35
|
+
<div class="feeds-split grid gap-6">
|
|
35
36
|
<div class="space-y-6">
|
|
36
37
|
<div
|
|
37
38
|
class="inline-flex items-center gap-2 rounded-full border border-amber-200/80 bg-white/80 px-3 py-1 text-[0.68rem] font-semibold uppercase tracking-[0.35em] text-amber-700 shadow-sm backdrop-blur dark:border-slate-700 dark:bg-slate-900/60 dark:text-amber-200"
|
|
@@ -42,18 +43,18 @@
|
|
|
42
43
|
|
|
43
44
|
<div class="space-y-3">
|
|
44
45
|
<h1
|
|
45
|
-
class="max-w-3xl
|
|
46
|
+
class="feeds-h1 max-w-3xl font-semibold tracking-tight text-slate-950 dark:text-slate-50"
|
|
46
47
|
>
|
|
47
48
|
Feed catalog for {{ catalog.site.title }}
|
|
48
49
|
</h1>
|
|
49
|
-
<p class="max-w-2xl
|
|
50
|
+
<p class="feeds-body max-w-2xl leading-7 text-slate-700 dark:text-slate-300">
|
|
50
51
|
Configured in <code class="font-mono text-[0.9em]">app.config.ts</code>, validated
|
|
51
52
|
against <code class="font-mono text-[0.9em]">content.config.ts</code>, and rendered as
|
|
52
53
|
a human-readable index for readers and site owners.
|
|
53
54
|
</p>
|
|
54
55
|
</div>
|
|
55
56
|
|
|
56
|
-
<div class="grid gap-3
|
|
57
|
+
<div class="feeds-cards-grid gap-3">
|
|
57
58
|
<div
|
|
58
59
|
class="rounded-2xl border border-slate-200/80 bg-white/75 p-4 shadow-sm backdrop-blur dark:border-slate-800 dark:bg-slate-900/45"
|
|
59
60
|
>
|
|
@@ -201,7 +202,7 @@
|
|
|
201
202
|
</UBadge>
|
|
202
203
|
</div>
|
|
203
204
|
|
|
204
|
-
<div class="grid gap-3 p-5
|
|
205
|
+
<div class="feeds-cards-grid gap-3 p-5">
|
|
205
206
|
<FeedsRouteCard v-for="route in group.routes" :key="route.path" :route compact />
|
|
206
207
|
</div>
|
|
207
208
|
</UCard>
|
|
@@ -227,3 +228,32 @@
|
|
|
227
228
|
</div>
|
|
228
229
|
</div>
|
|
229
230
|
</template>
|
|
231
|
+
|
|
232
|
+
<style scoped>
|
|
233
|
+
.feeds-shell {
|
|
234
|
+
padding: clamp(1.25rem, 1rem + 1vw, 1.5rem);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.feeds-h1 {
|
|
238
|
+
font-size: clamp(2.25rem, 1.75rem + 2.5vw, 3.75rem);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.feeds-body {
|
|
242
|
+
font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.feeds-cards-grid {
|
|
246
|
+
display: grid;
|
|
247
|
+
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.feeds-split {
|
|
251
|
+
grid-template-columns: 1fr;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
@container (width >= 48rem) {
|
|
255
|
+
.feeds-split {
|
|
256
|
+
grid-template-columns: minmax(0, 1.15fr) minmax(18rem, 0.85fr);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
</style>
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed } from 'vue'
|
|
3
|
-
|
|
4
2
|
import { type FeedRoute } from '../../utils/feed-catalog'
|
|
5
3
|
|
|
6
4
|
const { route, compact = false } = defineProps<{
|
|
@@ -50,7 +48,7 @@
|
|
|
50
48
|
<UCard
|
|
51
49
|
:to="route.path"
|
|
52
50
|
:ui="{ body: 'p-0' }"
|
|
53
|
-
class="group relative overflow-hidden border-slate-200/80 bg-white/90 shadow-none transition duration-200 hover:-translate-y-0.5 hover:border-slate-300 hover:shadow-md dark:border-slate-800 dark:bg-slate-950/55 dark:hover:border-slate-700"
|
|
51
|
+
class="group relative overflow-hidden border-slate-200/80 bg-white/90 shadow-none transition duration-200 hover:-translate-y-0.5 hover:border-slate-300 hover:shadow-md dark:border-slate-800 dark:bg-slate-950/55 dark:hover:border-slate-700 @container"
|
|
54
52
|
>
|
|
55
53
|
<div class="absolute inset-y-0 left-0 w-1" :class="routeTone.strip" />
|
|
56
54
|
<div :class="compact ? 'flex items-center gap-3 p-3' : 'flex items-center gap-3 p-4'">
|
|
@@ -27,7 +27,7 @@ export default defineNuxtPlugin({
|
|
|
27
27
|
const mainLinks = catalog.siteRoutes
|
|
28
28
|
.filter((route) => route.kind === 'format')
|
|
29
29
|
.map((route) => ({
|
|
30
|
-
rel: 'alternate',
|
|
30
|
+
rel: 'alternate' as const,
|
|
31
31
|
type: route.contentType ?? 'application/octet-stream',
|
|
32
32
|
title: `${siteTitle || 'Site'} (${route.label})`,
|
|
33
33
|
href: route.path,
|
|
@@ -38,7 +38,7 @@ export default defineNuxtPlugin({
|
|
|
38
38
|
const collectionLinks =
|
|
39
39
|
currentCollection && currentCollection.collection !== catalog.feed.defaultCollection
|
|
40
40
|
? currentCollection.routes.map((route) => ({
|
|
41
|
-
rel: 'alternate',
|
|
41
|
+
rel: 'alternate' as const,
|
|
42
42
|
type: route.contentType ?? 'application/octet-stream',
|
|
43
43
|
title: `${currentCollection.label} (${route.label})`,
|
|
44
44
|
href: route.path,
|