kmcom-nuxt-layers 2.7.4 → 3.0.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/FALLOW-COMPLEXITY-DUPLICATION-AUDIT.md +0 -1
- package/docs/LAYOUT.md +34 -61
- package/docs/MIGRATION.md +1 -1
- package/docs/UPAGE-GRID-INTEGRATION.md +6 -6
- package/docs/layout-shift-causes.md +1 -1
- package/docs/superpowers/plans/2026-09-03-layout-layer-fluid-rebuild.md +1850 -0
- package/docs/superpowers/plans/2026-09-03-vue-element-system.md +1019 -0
- package/docs/superpowers/plans/2026-09-04-grid-layer.md +977 -0
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-0-1-design.md +533 -0
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-2-design.md +154 -0
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-3-design.md +109 -0
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-4-design.md +124 -0
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-5-design.md +121 -0
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-6-design.md +77 -0
- package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-7-design.md +73 -0
- package/docs/superpowers/specs/2026-08-31-swiss-grid-layer-design.md +190 -0
- package/docs/superpowers/specs/2026-09-03-layout-layer-fluid-rebuild-design.md +312 -0
- package/docs/superpowers/specs/2026-09-03-vue-element-system-design.md +342 -0
- package/docs/superpowers/specs/2026-09-04-grid-layer-design.md +236 -0
- package/layers/content/app/app.config.ts +10 -2
- package/layers/content/app/components/Blog/ItemPage.vue +21 -0
- package/layers/content/app/components/Blog/Page.vue +25 -0
- package/layers/content/app/components/Gallery/ImagePage.vue +5 -1
- package/layers/content/app/components/Gallery/Page.vue +4 -1
- package/layers/content/app/components/Gallery/SlugPage.vue +5 -1
- package/layers/content/app/components/Portfolio/ItemPage.vue +4 -1
- package/layers/content/app/components/Portfolio/List.vue +14 -2
- package/layers/content/app/components/Portfolio/Page.vue +4 -1
- package/layers/content/app/composables/useBlogPosts.ts +6 -19
- package/layers/content/app/composables/useCollectionItems.ts +63 -0
- package/layers/content/app/composables/useGalleryItems.ts +1 -16
- package/layers/content/app/types/content.ts +9 -0
- package/layers/content/package.json +3 -0
- package/layers/core/app/app.config.ts +34 -17
- package/layers/core/app/app.vue +1 -0
- package/layers/core/app/assets/css/core.css +10 -0
- package/layers/core/app/assets/css/tokens/breakpoints.css +13 -0
- package/layers/core/app/assets/css/tokens/container.css +36 -0
- package/layers/core/app/assets/css/tokens/spacing-fluid.css +25 -0
- package/layers/core/app/components/AppContainer.vue +32 -0
- package/layers/core/app/components/Element.test.ts +90 -0
- package/layers/core/app/components/Element.vue +78 -0
- package/layers/core/app/components/ElementTw.test.ts +64 -0
- package/layers/core/app/components/ElementTw.vue +86 -0
- package/layers/core/app/components/LoadingScreen.vue +0 -15
- package/layers/core/app/composables/useElementGrid.test.ts +33 -0
- package/layers/core/app/composables/useElementGrid.ts +29 -0
- package/layers/core/app/composables/useElementInteraction.test.ts +27 -0
- package/layers/core/app/composables/useElementInteraction.ts +17 -0
- package/layers/core/app/composables/useElementLayout.test.ts +41 -0
- package/layers/core/app/composables/useElementLayout.ts +26 -0
- package/layers/core/app/composables/useElementSizing.test.ts +39 -0
- package/layers/core/app/composables/useElementSizing.ts +25 -0
- package/layers/core/app/composables/useElementStyle.ts +65 -0
- package/layers/core/app/composables/useElementSurface.test.ts +33 -0
- package/layers/core/app/composables/useElementSurface.ts +21 -0
- package/layers/core/app/composables/useErrorLog.ts +6 -4
- package/layers/core/app/composables/useLayoutAttrs.test.ts +47 -0
- package/layers/core/app/composables/useLayoutAttrs.ts +94 -0
- package/layers/core/app/composables/useLoading.ts +3 -1
- package/layers/core/app/error.vue +27 -57
- package/layers/core/app/pages/[...slug].vue +1 -29
- package/layers/core/app/plugins/icon-fetch-fix.ts +12 -0
- package/layers/core/app/plugins/loading.client.ts +0 -6
- package/layers/core/app/types/element.ts +25 -0
- package/layers/core/app/types/index.ts +0 -1
- package/layers/core/app/types/notFound.ts +24 -0
- package/layers/core/app/types/responsive.ts +15 -0
- package/layers/core/app/types/responsive.type-test.ts +22 -0
- package/layers/core/app/types/runtime-config.ts +1 -17
- package/layers/core/app/types/tokens.ts +29 -0
- package/layers/core/app/utils/index.ts +4 -2
- package/layers/core/app/utils/scrollGuardClamp.test.ts +36 -0
- package/layers/core/app/utils/scrollGuardClamp.ts +22 -0
- package/layers/core/app/utils/styleToTailwindClasses.test.ts +31 -0
- package/layers/core/app/utils/styleToTailwindClasses.ts +16 -0
- package/layers/core/nuxt.config.ts +15 -11
- package/layers/core/package.json +5 -1
- package/layers/{auth → data/auth}/nuxt.config.ts +1 -1
- package/layers/{auth → data/auth}/package.json +1 -1
- package/layers/{baseline → data/auth}/tsconfig.json +1 -1
- package/layers/{database → data/database}/nuxt.config.ts +1 -1
- package/layers/{database → data/database}/package.json +1 -1
- package/layers/{database → data/database}/tsconfig.json +1 -1
- package/layers/{forms/app/types/app-config.d.ts → data/forms/app/app.config.ts} +4 -0
- package/layers/{forms → data/forms}/package.json +1 -1
- package/layers/{forms → data/forms}/server/api/forms/status.get.ts +3 -1
- package/layers/{animations → data/forms}/tsconfig.json +1 -1
- package/layers/{mailer → data/mailer}/nuxt.config.ts +11 -1
- package/layers/{mailer → data/mailer}/package.json +1 -1
- package/layers/{auth → data/mailer}/tsconfig.json +1 -1
- package/layers/data/metadata/app/composables/useMetadataItem.ts +18 -0
- package/layers/data/metadata/app/composables/useMetadataSearch.ts +37 -0
- package/layers/{metadata → data/metadata}/nuxt.config.ts +1 -1
- package/layers/{metadata → data/metadata}/package.json +1 -1
- package/layers/{metadata → data/metadata}/providers/comicvine/nuxt.config.ts +1 -1
- package/layers/{metadata → data/metadata}/providers/comicvine/package.json +1 -1
- package/layers/data/metadata/providers/comicvine/tsconfig.json +7 -0
- package/layers/{metadata → data/metadata}/providers/google-books/nuxt.config.ts +1 -1
- package/layers/{metadata → data/metadata}/providers/google-books/package.json +1 -1
- package/layers/{metadata → data/metadata}/providers/google-books/server/utils/metadata-google-books/client.ts +10 -2
- package/layers/data/metadata/providers/google-books/tsconfig.json +7 -0
- package/layers/{metadata → data/metadata}/providers/openlibrary/package.json +1 -1
- package/layers/{metadata → data/metadata}/providers/openlibrary/server/utils/metadata-openlibrary/client.ts +23 -11
- package/layers/data/metadata/providers/openlibrary/tsconfig.json +7 -0
- package/layers/{metadata → data/metadata}/providers/themoviedb/nuxt.config.ts +1 -1
- package/layers/{metadata → data/metadata}/providers/themoviedb/package.json +1 -1
- package/layers/{metadata → data/metadata}/providers/themoviedb/tsconfig.json +1 -1
- package/layers/{metadata → data/metadata}/server/utils/metadata/search.ts +11 -4
- package/layers/data/metadata/tsconfig.json +7 -0
- package/layers/{feeds → delivery/feeds}/app/components/Feeds/Index.vue +6 -2
- package/layers/{feeds → delivery/feeds}/app/components/Feeds/RouteCard.vue +2 -0
- package/layers/{feeds → delivery/feeds}/app/plugins/feed-head.ts +6 -2
- package/layers/{feeds → delivery/feeds}/app/utils/feed-catalog.ts +8 -6
- package/layers/{feeds → delivery/feeds}/nuxt.config.ts +1 -1
- package/layers/{feeds → delivery/feeds}/package.json +1 -1
- package/layers/{feeds → delivery/feeds}/server/utils/feed-config.ts +1 -1
- package/layers/{feeds → delivery/feeds}/server/utils/feed-service.ts +2 -1
- package/layers/delivery/feeds/tsconfig.json +7 -0
- package/layers/{scripts → delivery/scripts}/app/composables/useAnalytics.ts +3 -1
- package/layers/{scripts → delivery/scripts}/app/composables/useGtm.ts +8 -2
- package/layers/{scripts → delivery/scripts}/app/composables/useScriptsConsent.ts +4 -1
- package/layers/{scripts → delivery/scripts}/app/utils/scriptClients.ts +19 -0
- package/layers/{scripts → delivery/scripts}/nuxt.config.ts +7 -2
- package/layers/{scripts → delivery/scripts}/package.json +3 -2
- package/layers/delivery/scripts/tsconfig.json +7 -0
- package/layers/delivery/seo/app/composables/useSeoConfig.ts +8 -0
- package/layers/{seo → delivery/seo}/app/utils/seoConfig.test.ts +2 -2
- package/layers/{seo → delivery/seo}/app/utils/seoConfig.ts +2 -2
- package/layers/{seo → delivery/seo}/nuxt.config.ts +1 -1
- package/layers/{seo → delivery/seo}/package.json +1 -1
- package/layers/delivery/seo/tsconfig.json +7 -0
- package/layers/design-system/theming/app/app.config.ts +35 -0
- package/layers/design-system/theming/app/assets/css/tokens.css +26 -0
- package/layers/{theme → design-system/theming}/app/components/ThemePicker/AccentButton.vue +3 -22
- package/layers/{theme → design-system/theming}/app/components/ThemePicker/Colors.vue +3 -4
- package/layers/{theme → design-system/theming}/app/composables/useAccentColor.ts +5 -2
- package/layers/{theme → design-system/theming}/app/composables/useThemeContrast.ts +1 -1
- package/layers/{theme → design-system/theming}/app/composables/useThemeMotion.ts +1 -1
- package/layers/{theme → design-system/theming}/app/composables/useThemeTransparency.ts +1 -1
- package/layers/{theme → design-system/theming}/app/plugins/theme.client.ts +2 -1
- package/layers/design-system/theming/app/types/designSystem.ts +102 -0
- package/layers/design-system/theming/app/types/semanticColor.ts +20 -0
- package/layers/design-system/theming/app/types/theme.ts +12 -0
- package/layers/design-system/theming/app/utils/accentPalette.ts +28 -0
- package/layers/design-system/theming/app/utils/themeAdapter.ts +20 -0
- package/layers/design-system/theming/nuxt.config.ts +49 -0
- package/layers/{ui → design-system/theming}/package.json +5 -6
- package/layers/{theme → design-system/theming}/server/plugins/theme-fouc.ts +2 -0
- package/layers/{theme → design-system/theming}/server/utils/accent-css.ts +2 -2
- package/layers/{theme → design-system/theming}/tsconfig.json +1 -1
- package/layers/{typography → design-system/typography}/app/assets/css/typography.css +13 -10
- package/layers/design-system/typography/app/components/Typography/CodeBlock.vue +65 -0
- package/layers/{typography → design-system/typography}/app/components/Typography/Headline.vue +16 -31
- package/layers/{typography/app/components/Typography/HeadlineScreen.vue → design-system/typography/app/components/Typography/QuoteBlock.vue} +27 -29
- package/layers/design-system/typography/app/components/Typography/RepeatText.vue +147 -0
- package/layers/{typography → design-system/typography}/app/components/Typography/index.vue +6 -4
- package/layers/design-system/typography/app/composables/repeatFill.ts +51 -0
- package/layers/{typography → design-system/typography}/app/composables/typography.ts +4 -0
- package/layers/{typography → design-system/typography}/app/types/typography.ts +8 -0
- package/layers/{typography → design-system/typography}/nuxt.config.ts +1 -3
- package/layers/design-system/typography/tsconfig.json +7 -0
- package/layers/{visual → design-system/visual}/app/app.config.ts +2 -2
- package/layers/{visual → design-system/visual}/app/components/Accent/Blob.vue +1 -1
- package/layers/{visual → design-system/visual}/app/components/Accent/Scene.vue +2 -3
- package/layers/{visual → design-system/visual}/app/components/Gradient/Background.vue +2 -2
- package/layers/{visual → design-system/visual}/app/components/Gradient/Text.vue +2 -2
- package/layers/{visual → design-system/visual}/app/composables/gradient.ts +1 -1
- package/layers/design-system/visual/app/types/breakpoints.ts +67 -0
- package/layers/{visual → design-system/visual}/app/types/media.ts +0 -11
- package/layers/{visual → design-system/visual}/nuxt.config.ts +1 -1
- package/layers/design-system/visual/tsconfig.json +7 -0
- package/layers/{animations → motion/animations}/app/components/Motion/Marquee.vue +0 -7
- package/layers/{animations → motion/animations}/app/components/Motion/Staggered.vue +4 -1
- package/layers/{animations → motion/animations}/nuxt.config.ts +1 -2
- package/layers/motion/animations/tsconfig.json +7 -0
- package/layers/motion/{package.json → motion/package.json} +1 -1
- package/layers/motion/{tsconfig.json → motion/tsconfig.json} +1 -1
- package/layers/motion/page-transitions/app/assets/css/page-transitions.css +30 -0
- package/layers/{page-transitions → motion/page-transitions}/app/composables/usePageTransition.ts +14 -2
- package/layers/{page-transitions → motion/page-transitions}/app/plugins/page-transitions.client.ts +5 -2
- package/layers/{page-transitions → motion/page-transitions}/nuxt.config.ts +3 -1
- package/layers/motion/page-transitions/tsconfig.json +7 -0
- package/layers/motion/scroll/app/app.config.ts +13 -0
- package/layers/{scroll → motion/scroll}/app/composables/useSmoothScroll.ts +2 -1
- package/layers/{scroll → motion/scroll}/app/plugins/locomotive-scroll.client.ts +2 -1
- package/layers/{scroll → motion/scroll}/nuxt.config.ts +1 -1
- package/layers/motion/scroll/tsconfig.json +7 -0
- package/layers/{transitions → motion/transitions}/nuxt.config.ts +1 -1
- package/layers/motion/transitions/tsconfig.json +7 -0
- package/layers/{canvas → render/canvas}/app/composables/useRendererCapabilities.ts +0 -8
- package/layers/{canvas → render/canvas}/app/composables/useShaderTime.ts +0 -12
- package/layers/{canvas → render/canvas}/nuxt.config.ts +1 -4
- package/layers/{canvas → render/canvas}/package.json +3 -2
- package/layers/{canvas → render/canvas}/tsconfig.json +1 -1
- package/layers/render/shader/app/app.config.ts +21 -0
- package/layers/{shader → render/shader}/app/shaders/layers/paperShading.ts +1 -1
- package/layers/{shader → render/shader}/package.json +1 -1
- package/layers/render/shader/tsconfig.json +7 -0
- package/layers/structure/grid/app/app.config.ts +92 -0
- package/layers/structure/grid/app/assets/css/grid.css +38 -0
- package/layers/structure/grid/app/composables/useGridTracks.test.ts +94 -0
- package/layers/structure/grid/app/composables/useGridTracks.ts +74 -0
- package/layers/structure/grid/app/types/tracks.test.ts +32 -0
- package/layers/structure/grid/app/types/tracks.ts +18 -0
- package/layers/structure/grid/app/utils/placement.test.ts +19 -0
- package/layers/structure/grid/app/utils/placement.ts +15 -0
- package/layers/structure/grid/nuxt.config.ts +22 -0
- package/layers/{theme → structure/grid}/package.json +3 -3
- package/layers/structure/layout/app/app.config.ts +116 -0
- package/layers/{layout → structure/layout}/app/assets/css/layout/grids.css +11 -6
- package/layers/structure/layout/app/assets/css/layout.css +1 -0
- package/layers/structure/layout/app/components/HStack.vue +41 -0
- package/layers/{layout → structure/layout}/app/components/Layout/Grid/Debug.vue +5 -3
- package/layers/{layout → structure/layout}/app/components/Layout/Grid/Item.vue +4 -10
- package/layers/{layout → structure/layout}/app/components/Layout/Main.vue +3 -3
- package/layers/{layout → structure/layout}/app/components/Layout/Page/Header.vue +1 -1
- package/layers/{layout → structure/layout}/app/components/Layout/Page/index.vue +3 -3
- package/layers/{layout → structure/layout}/app/components/Layout/Section/index.vue +1 -1
- package/layers/structure/layout/app/components/Spacer.vue +21 -0
- package/layers/structure/layout/app/components/VStack.vue +37 -0
- package/layers/structure/layout/app/components/ZStack.vue +28 -0
- package/layers/structure/layout/app/layouts/default.vue +5 -0
- package/layers/{layout → structure/layout}/app/types/layouts.ts +2 -3
- package/layers/{layout → structure/layout}/app/utils/gridPlacementStyle.test.ts +7 -42
- package/layers/{layout → structure/layout}/app/utils/gridPlacementStyle.ts +4 -18
- package/layers/{layout → structure/layout}/nuxt.config.ts +1 -1
- package/layers/{layout → structure/layout}/package.json +1 -1
- package/layers/structure/layout/tsconfig.json +7 -0
- package/layers/{navigation → structure/navigation}/app/app.config.ts +4 -6
- package/layers/structure/navigation/app/components/Mast/Header.vue +11 -0
- package/layers/{navigation → structure/navigation}/app/components/Mast/Nav.vue +1 -1
- package/layers/structure/navigation/app/composables/mastNav.ts +15 -0
- package/layers/{navigation → structure/navigation}/app/utils/createModal.ts +22 -3
- package/layers/{navigation → structure/navigation}/nuxt.config.ts +1 -1
- package/layers/structure/navigation/tsconfig.json +7 -0
- package/layers/{routing → structure/routing}/app/app.config.ts +11 -2
- package/layers/{routing → structure/routing}/nuxt.config.ts +1 -1
- package/layers/{routing → structure/routing}/package.json +1 -1
- package/layers/structure/routing/tsconfig.json +7 -0
- package/package.json +56 -56
- package/docs/fallow-refactor/layers-ui.md +0 -27
- package/layers/animations/app/types/animations.ts +0 -8
- package/layers/animations/app/types/index.ts +0 -1
- package/layers/baseline/nuxt.config.ts +0 -24
- package/layers/baseline/package.json +0 -14
- package/layers/content/app/components/Blog/Card.vue +0 -43
- package/layers/content/app/components/Gallery/AmbientImage.vue +0 -44
- package/layers/content/app/components/Gallery/Card.vue +0 -43
- package/layers/content/app/components/NuxtContent/Renderer.vue +0 -14
- package/layers/content/app/components/Portfolio/Card.vue +0 -43
- package/layers/content/app/composables/createPortfolioComposables.ts +0 -50
- package/layers/content/app/composables/useBlogPost.ts +0 -3
- package/layers/content/app/composables/useGalleryItem.ts +0 -3
- package/layers/content/app/composables/usePortfolioItem.ts +0 -3
- package/layers/content/app/composables/usePortfolioItems.ts +0 -26
- package/layers/content/app/types/app-config.d.ts +0 -14
- package/layers/core/app/assets/css-backup.zip +0 -0
- package/layers/core/app/composables/useEnv.ts +0 -23
- package/layers/core/app/composables/useScrollGuard.ts +0 -243
- package/layers/core/app/plugins/feature-detection.client.ts +0 -34
- package/layers/core/app/plugins/init.ts +0 -192
- package/layers/core/app/plugins/scroll-guard.client.ts +0 -56
- package/layers/core/app/types/app-config.d.ts +0 -63
- package/layers/core/app/types/scroll-guard.ts +0 -25
- package/layers/core/app/utils/helpers.test.ts +0 -55
- package/layers/core/app/utils/helpers.ts +0 -248
- package/layers/core/app/utils/regex.test.ts +0 -31
- package/layers/feeds/tsconfig.json +0 -7
- package/layers/forms/app/app.config.ts +0 -3
- package/layers/forms/tsconfig.json +0 -7
- package/layers/layout/app/app.config.ts +0 -180
- package/layers/layout/app/assets/css/layout/modes/fluid.css +0 -109
- package/layers/layout/app/assets/css/layout.css +0 -2
- package/layers/layout/app/components/Layout/Container.vue +0 -46
- package/layers/layout/app/components/Layout/Page/Container.vue +0 -103
- package/layers/layout/app/components/Layout/Section/Grid.vue +0 -46
- package/layers/layout/app/components/Layout/Section/Sidebar.vue +0 -84
- package/layers/layout/app/components/Layout/Section/Stack.vue +0 -63
- package/layers/layout/app/components/Layout/Section/Title.vue +0 -33
- package/layers/layout/app/composables/GridPlacement.ts +0 -31
- package/layers/layout/app/types/app-config.d.ts +0 -14
- package/layers/layout/tsconfig.json +0 -7
- package/layers/mailer/server/types.d.ts +0 -11
- package/layers/mailer/tsconfig.json +0 -7
- package/layers/metadata/app/composables/useMetadataItem.ts +0 -15
- package/layers/metadata/app/composables/useMetadataSearch.ts +0 -29
- package/layers/metadata/providers/comicvine/tsconfig.json +0 -7
- package/layers/metadata/providers/google-books/tsconfig.json +0 -7
- package/layers/metadata/providers/openlibrary/tsconfig.json +0 -7
- package/layers/metadata/tsconfig.json +0 -7
- package/layers/motion/app/components/Motion/index.vue +0 -16
- package/layers/navigation/app/components/Mast/Header.vue +0 -18
- package/layers/navigation/app/components/Mast/Main.vue +0 -7
- package/layers/navigation/app/composables/mastNav.ts +0 -26
- package/layers/navigation/app/utils/regex.ts +0 -6
- package/layers/navigation/tsconfig.json +0 -7
- package/layers/page-transitions/tsconfig.json +0 -7
- package/layers/routing/app/types/app-config.d.ts +0 -7
- package/layers/routing/tsconfig.json +0 -7
- package/layers/scripts/app/types/index.ts +0 -1
- package/layers/scripts/app/types/scripts.ts +0 -14
- package/layers/scripts/tsconfig.json +0 -7
- package/layers/scroll/app/app.config.ts +0 -31
- package/layers/scroll/tsconfig.json +0 -7
- package/layers/seo/app/composables/useSeoConfig.ts +0 -7
- package/layers/seo/tsconfig.json +0 -7
- package/layers/shader/app/app.config.ts +0 -9
- package/layers/shader/app/types/app-config.d.ts +0 -7
- package/layers/shader/tsconfig.json +0 -7
- package/layers/starter/app/app.config.ts +0 -10
- package/layers/starter/app/components/StarterDesignSystem.vue +0 -1913
- package/layers/starter/app/components/StarterHome.vue +0 -441
- package/layers/starter/nuxt.config.ts +0 -15
- package/layers/starter/package.json +0 -10
- package/layers/theme/app/app.config.ts +0 -3
- package/layers/theme/app/types/app-config.d.ts +0 -10
- package/layers/theme/app/types/theme.ts +0 -23
- package/layers/theme/nuxt.config.ts +0 -74
- package/layers/transitions/tsconfig.json +0 -7
- package/layers/typography/app/components/Typography/CodeBlock.vue +0 -41
- package/layers/typography/app/components/Typography/QuoteBlock.vue +0 -23
- package/layers/typography/tsconfig.json +0 -7
- package/layers/ui/app/assets/css/ui.css +0 -4
- package/layers/ui/app/layouts/default.vue +0 -8
- package/layers/ui/nuxt.config.ts +0 -22
- package/layers/ui/tsconfig.json +0 -7
- package/layers/visual/app/types/breakpoints.ts +0 -179
- package/layers/visual/tsconfig.json +0 -7
- /package/layers/{auth → data/auth}/app/middleware/auth.ts +0 -0
- /package/layers/{auth → data/auth}/server/routes/auth/github.get.ts +0 -0
- /package/layers/{database → data/database}/server/utils/drizzle.ts +0 -0
- /package/layers/{database → data/database}/server/utils/sql.ts +0 -0
- /package/layers/{forms → data/forms}/app/components/Form/Contact.vue +0 -0
- /package/layers/{forms → data/forms}/app/components/Form/Field.vue +0 -0
- /package/layers/{forms → data/forms}/app/composables/useFormSchema.ts +0 -0
- /package/layers/{forms → data/forms}/app/composables/useFormsFetch.ts +0 -0
- /package/layers/{forms → data/forms}/app/config/fields.ts +0 -0
- /package/layers/{forms → data/forms}/app/types/fields.ts +0 -0
- /package/layers/{forms → data/forms}/app/types/index.ts +0 -0
- /package/layers/{forms → data/forms}/app/utils/contact-schema.test.ts +0 -0
- /package/layers/{forms → data/forms}/app/utils/contact-schema.ts +0 -0
- /package/layers/{forms → data/forms}/app/utils/fieldProps.test.ts +0 -0
- /package/layers/{forms → data/forms}/app/utils/fieldProps.ts +0 -0
- /package/layers/{forms → data/forms}/nuxt.config.ts +0 -0
- /package/layers/{forms → data/forms}/server/api/contact.post.ts +0 -0
- /package/layers/{mailer → data/mailer}/app/types/index.ts +0 -0
- /package/layers/{mailer → data/mailer}/app/types/mailer.ts +0 -0
- /package/layers/{mailer → data/mailer}/server/utils/config.ts +0 -0
- /package/layers/{mailer → data/mailer}/server/utils/email.ts +0 -0
- /package/layers/{mailer → data/mailer}/server/utils/hooks.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/comicvine/server/plugins/comicvine-register.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/comicvine/server/utils/metadata-comicvine/client.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/comicvine/server/utils/metadata-comicvine/normalise.test.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/comicvine/server/utils/metadata-comicvine/normalise.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/comicvine/server/utils/metadata-comicvine/provider.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/comicvine/server/utils/metadata-comicvine/types.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/google-books/server/plugins/google-books-register.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/google-books/server/utils/metadata-google-books/client.test.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/google-books/server/utils/metadata-google-books/normalise.test.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/google-books/server/utils/metadata-google-books/normalise.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/google-books/server/utils/metadata-google-books/provider.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/google-books/server/utils/metadata-google-books/types.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/openlibrary/nuxt.config.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/openlibrary/server/plugins/openlibrary-register.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/openlibrary/server/utils/metadata-openlibrary/client.test.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/openlibrary/server/utils/metadata-openlibrary/normalise.test.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/openlibrary/server/utils/metadata-openlibrary/normalise.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/openlibrary/server/utils/metadata-openlibrary/provider.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/openlibrary/server/utils/metadata-openlibrary/types.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/themoviedb/server/plugins/tmdb-register.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/themoviedb/server/utils/metadata-tmdb/client.test.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/themoviedb/server/utils/metadata-tmdb/client.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/themoviedb/server/utils/metadata-tmdb/normalise.test.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/themoviedb/server/utils/metadata-tmdb/normalise.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/themoviedb/server/utils/metadata-tmdb/provider.ts +0 -0
- /package/layers/{metadata → data/metadata}/providers/themoviedb/server/utils/metadata-tmdb/types.ts +0 -0
- /package/layers/{metadata → data/metadata}/server/api/metadata/lookup.get.ts +0 -0
- /package/layers/{metadata → data/metadata}/server/api/metadata/providers.get.ts +0 -0
- /package/layers/{metadata → data/metadata}/server/api/metadata/search.get.ts +0 -0
- /package/layers/{metadata → data/metadata}/server/api/metadata/status.get.ts +0 -0
- /package/layers/{metadata → data/metadata}/server/api/metadata/sync.post.ts +0 -0
- /package/layers/{metadata → data/metadata}/server/utils/metadata/cache.test.ts +0 -0
- /package/layers/{metadata → data/metadata}/server/utils/metadata/cache.ts +0 -0
- /package/layers/{metadata → data/metadata}/server/utils/metadata/errors.ts +0 -0
- /package/layers/{metadata → data/metadata}/server/utils/metadata/provider-registry.test.ts +0 -0
- /package/layers/{metadata → data/metadata}/server/utils/metadata/provider-registry.ts +0 -0
- /package/layers/{metadata → data/metadata}/server/utils/metadata/search.test.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/app/app.config.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/app/assets/css/feeds.css +0 -0
- /package/layers/{feeds → delivery/feeds}/public/feed/style.css +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/[collection]/atom/all.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/[collection]/atom.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/[collection]/json/all.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/[collection]/json.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/[collection]/rss/all.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/[collection]/rss.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/atom/all.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/atom.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/demo.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/index.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/json/all.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/json.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/rss/all.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/rss.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/routes/feed/style.xsl.get.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/utils/cache.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/utils/content-adapter.test.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/utils/content-adapter.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/utils/feed-author.test.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/utils/feed-author.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/utils/feed-config.test.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/utils/feed-xml.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/utils/formats/atom.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/utils/formats/json.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/utils/formats/rss.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/utils/formats.test.ts +0 -0
- /package/layers/{feeds → delivery/feeds}/server/utils/types.ts +0 -0
- /package/layers/{scripts → delivery/scripts}/app/app.config.ts +0 -0
- /package/layers/{baseline → delivery/scripts}/app/components/BaselineStatus.vue +0 -0
- /package/layers/{scripts → delivery/scripts}/app/composables/useScriptLoader.ts +0 -0
- /package/layers/{scripts → delivery/scripts}/app/composables/useYoutubeEmbed.ts +0 -0
- /package/layers/{baseline → delivery/scripts}/app/plugins/baseline-status.client.ts +0 -0
- /package/layers/{seo → delivery/seo}/app/app.config.ts +0 -0
- /package/layers/{theme → design-system/theming}/app/assets/css/theme.css +0 -0
- /package/layers/{theme → design-system/theming}/app/components/ThemePicker/Menu.vue +0 -0
- /package/layers/{theme → design-system/theming}/app/components/ThemePicker/MenuButton.vue +0 -0
- /package/layers/{theme → design-system/theming}/app/composables/useTheme.ts +0 -0
- /package/layers/{theme → design-system/theming}/app/composables/useThemePreferenceModels.ts +0 -0
- /package/layers/{theme → design-system/theming}/app/composables/useThemePreferences.ts +0 -0
- /package/layers/{theme → design-system/theming}/app/types/index.ts +0 -0
- /package/layers/{theme → design-system/theming}/server/utils/accent-css.test.ts +0 -0
- /package/layers/{theme → design-system/theming}/server/utils/fouc-config.test.ts +0 -0
- /package/layers/{theme → design-system/theming}/server/utils/fouc-config.ts +0 -0
- /package/layers/{typography → design-system/typography}/app/components/Typography/TextStroke.vue +0 -0
- /package/layers/{typography → design-system/typography}/app/composables/color.ts +0 -0
- /package/layers/{typography → design-system/typography}/app/types/colors.ts +0 -0
- /package/layers/{typography → design-system/typography}/app/types/index.ts +0 -0
- /package/layers/{typography → design-system/typography}/package.json +0 -0
- /package/layers/{visual → design-system/visual}/app/assets/css/visual.css +0 -0
- /package/layers/{visual → design-system/visual}/app/components/Base/Modal.vue +0 -0
- /package/layers/{visual → design-system/visual}/app/components/Media/Picture.vue +0 -0
- /package/layers/{visual → design-system/visual}/app/components/Progress/Bar.vue +0 -0
- /package/layers/{visual → design-system/visual}/app/components/Progress/Circular.vue +0 -0
- /package/layers/{visual → design-system/visual}/app/components/Tint/Overlay.vue +0 -0
- /package/layers/{visual → design-system/visual}/app/composables/accent.ts +0 -0
- /package/layers/{visual → design-system/visual}/app/composables/picture.ts +0 -0
- /package/layers/{visual → design-system/visual}/app/composables/tint.ts +0 -0
- /package/layers/{visual → design-system/visual}/app/types/accent.ts +0 -0
- /package/layers/{visual → design-system/visual}/app/types/gradient.ts +0 -0
- /package/layers/{visual → design-system/visual}/app/types/index.ts +0 -0
- /package/layers/{visual → design-system/visual}/app/types/tint.ts +0 -0
- /package/layers/{visual → design-system/visual}/app/utils/colorTokens.test.ts +0 -0
- /package/layers/{visual → design-system/visual}/app/utils/colorTokens.ts +0 -0
- /package/layers/{visual → design-system/visual}/app/utils/gradientStyle.test.ts +0 -0
- /package/layers/{visual → design-system/visual}/app/utils/gradientStyle.ts +0 -0
- /package/layers/{visual → design-system/visual}/app/utils/responsiveSizes.test.ts +0 -0
- /package/layers/{visual → design-system/visual}/app/utils/responsiveSizes.ts +0 -0
- /package/layers/{visual → design-system/visual}/package.json +0 -0
- /package/layers/{animations → motion/animations}/app/components/Motion/CountUp.vue +0 -0
- /package/layers/{animations → motion/animations}/app/components/Motion/Cursor.vue +0 -0
- /package/layers/{animations → motion/animations}/app/components/Motion/Magnetic.vue +0 -0
- /package/layers/{animations → motion/animations}/app/components/Motion/MarqueeText.vue +0 -0
- /package/layers/{animations → motion/animations}/app/components/Motion/TextReveal.vue +0 -0
- /package/layers/{animations → motion/animations}/app/components/Motion/Tilt.vue +0 -0
- /package/layers/{animations → motion/animations}/app/components/Motion/VelocityEffect.vue +0 -0
- /package/layers/{animations → motion/animations}/app/composables/useCountUp.ts +0 -0
- /package/layers/{animations → motion/animations}/app/composables/useCursorFollower.ts +0 -0
- /package/layers/{animations → motion/animations}/app/composables/useMagneticElement.ts +0 -0
- /package/layers/{animations → motion/animations}/app/composables/useMarqueeCopies.ts +0 -0
- /package/layers/{animations → motion/animations}/app/composables/useMarqueeVelocity.ts +0 -0
- /package/layers/{animations → motion/animations}/app/composables/useTiltEffect.ts +0 -0
- /package/layers/{animations → motion/animations}/app/utils/pointerMotion.ts +0 -0
- /package/layers/{animations → motion/animations}/package.json +0 -0
- /package/layers/motion/{nuxt.config.ts → motion/nuxt.config.ts} +0 -0
- /package/layers/{page-transitions → motion/page-transitions}/app/app.config.ts +0 -0
- /package/layers/{page-transitions → motion/page-transitions}/package.json +0 -0
- /package/layers/{scroll → motion/scroll}/app/components/Motion/HorizontalScroll.vue +0 -0
- /package/layers/{scroll → motion/scroll}/app/components/Motion/Parallax.vue +0 -0
- /package/layers/{scroll → motion/scroll}/app/components/Motion/PinnedSection.vue +0 -0
- /package/layers/{scroll → motion/scroll}/app/components/Motion/ScrollLink.vue +0 -0
- /package/layers/{scroll → motion/scroll}/app/components/Motion/ScrollProgress.vue +0 -0
- /package/layers/{scroll → motion/scroll}/app/components/Motion/ScrollScene.vue +0 -0
- /package/layers/{scroll → motion/scroll}/app/components/Motion/ScrollStats.vue +0 -0
- /package/layers/{scroll → motion/scroll}/app/components/Motion/ScrollStep.vue +0 -0
- /package/layers/{scroll → motion/scroll}/app/composables/useGsap.ts +0 -0
- /package/layers/{scroll → motion/scroll}/app/composables/useScrollSteps.ts +0 -0
- /package/layers/{scroll → motion/scroll}/app/composables/useSectionProgress.ts +0 -0
- /package/layers/{scroll → motion/scroll}/app/types/index.ts +0 -0
- /package/layers/{scroll → motion/scroll}/app/types/scroll.ts +0 -0
- /package/layers/{scroll → motion/scroll}/app/utils/scroll.ts +0 -0
- /package/layers/{scroll → motion/scroll}/package.json +0 -0
- /package/layers/{transitions → motion/transitions}/app/assets/css/transitions.css +0 -0
- /package/layers/{transitions → motion/transitions}/app/components/Motion/Transition.vue +0 -0
- /package/layers/{transitions → motion/transitions}/package.json +0 -0
- /package/layers/{canvas → render/canvas}/app/components/ShaderCanvas.vue +0 -0
- /package/layers/{canvas → render/canvas}/app/types/index.ts +0 -0
- /package/layers/{canvas → render/canvas}/app/types/renderer.ts +0 -0
- /package/layers/{shader → render/shader}/app/assets/css/shader.css +0 -0
- /package/layers/{shader → render/shader}/app/components/Effect/Bloom.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Effect/ChromaticAberration.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Effect/PostProcessing.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Material/AmbientAurora.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Material/AmbientFlow.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Material/AmbientGradientMesh.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Material/AmbientNebula.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Material/AmbientOcean.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Material/Fresnel.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Material/Gradient.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Material/Image.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Material/Node.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Material/Noise.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Mesh/Plane.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Node/Color.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Node/Fresnel.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Node/Gradient.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Node/Mix.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Node/Noise.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ACESTonemap.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/AddBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/AgedFilm.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Aurora.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/BilinearGradient.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/BillowNoise.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/BrightnessContrast.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/CellularNoise.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ChannelMixer.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ChebyshevNoiseField.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Checkerboard.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ChromaticAberration.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ChromaticScreenWaves.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Circle.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Clouds.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ColorBurnBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ColorDodgeBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ColourRamp.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ComplexPlaneField.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ConicGradient.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/CosinePalette.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/CoverageAlpha.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Cross.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/CurlNoise.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/DarkenBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/DayNightCycle.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Desaturate.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/DiagonalGradient.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/DiamondGradient.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/DifferenceBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/DivideBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/DomainWarpedNoise.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Dots.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/DuoTone.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ExclusionBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ExponentialFog.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Exposure.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/FBMNoise.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/FilmBurn.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/FilmGrain.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/FisheyeRay.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Flame.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/FocalGradient.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Gamma.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/GodRays.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/GradientNoise.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Grain.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Grid.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Halation.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Halftone.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/HardLightBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Haze.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Hexagon.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Hue.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Invert.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/LensFlare.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/LightenBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/LinearGradient.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/LinearGradient4.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/LinearToSRGB.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Marble.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/MixBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/MonochromeTint.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/MultiplyBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/NoisyGradient.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/NoisyGradientBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/OverlayBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/PaperTexture.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Polygon.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Posterise.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/RadialGradient.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/RayAutoOrbit.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/RayMouseOrbit.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/RayRotateX.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/RayRotateY.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/RayRotateZ.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/RayTiltBasis.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/RaymarchTunnel.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Rectangle.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ReinhardTonemap.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/RidgedNoise.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Ring.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/RingField.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/RisographGrain.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/RotatedGradientBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/SDFColourMask.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/SDFRadialMask.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/SRGBToLinear.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Saturation.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Scanlines.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ScreenBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ShaderDebugger.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/SimplexNoise.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/SkyAtmosphere.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/SoftLightBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/SolidColour.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/SplitTone.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Star.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Starfield.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Stripes.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/SubtractBlend.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/TanhTonemap.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Threshold.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Tint.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Triangle.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVAspectCorrect.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVBreath.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVBulge.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVClamp.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVColumnOffset.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVFlipX.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVFlipXY.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVFlipY.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVFractBand.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVMousePull.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVNoiseRotate.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVNoiseWarp.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVOrbit.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVParallax.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVPixelate.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVPulse.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVRipple.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVRotate.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVScale.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVScroll.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVScrollX.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVScrollY.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVShear.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVSineWarpXY.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVSwapAxes.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVTile.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVTwirl.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/UVWarp.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/VHSBleed.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/ValueNoise.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Vibrance.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Vignette.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/VoronoiEdges.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Water.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/WaveBendLayer.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/WaveColourLayer.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/WhiteBalance.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Pipeline/Wood.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Preset/Aurora.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Preset/Flow.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Preset/GradientMesh.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Preset/Nebula.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Preset/Ocean.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Preset/ThemeAurora.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Preset/ThemeBubble.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Preset/ThemeFlow.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Preset/ThemeGradient.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Preset/ThemeLavaLamp.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Preset/ThemePlasma.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Preset/ThemeWave.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Shader/Background.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Shader/Debug.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Shader/Host.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Shader/Material.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Shader/Pipeline.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Shader/PipelineContext.vue +0 -0
- /package/layers/{shader → render/shader}/app/components/Shader/Runtime.client.vue +0 -0
- /package/layers/{shader → render/shader}/app/composables/useAmbientMaterials.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useCSSColourUniform.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useCSSFloatUniform.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useMousePosition.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useShader.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useShaderColor.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useShaderFloat.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useShaderGraph.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useShaderMixBlend.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useShaderPerf.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useShaderPipeline.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useShaderRuntime.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useShaderVec2.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useSunDirectionUniform.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useTSLNodes.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useThemeColors.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useThemePreset.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useUniformWatchers.ts +0 -0
- /package/layers/{shader → render/shader}/app/composables/useUniforms.ts +0 -0
- /package/layers/{shader → render/shader}/app/plugins/shader.client.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/blend.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/complex.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/effects.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/grain.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/index.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/lighting.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/math.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/nodes.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/noise.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/noiseHelpers.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/palette.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/patterns.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/sdf.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/shapes.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/tonemapping.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/common/uv.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/createMaterial.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/index.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/layers/aurora.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/layers/index.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/layers/meshGradient.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/layers/shaderGradient.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/layers/stripe.ts +0 -0
- /package/layers/{shader → render/shader}/app/shaders/types.ts +0 -0
- /package/layers/{shader → render/shader}/app/types/index.ts +0 -0
- /package/layers/{shader → render/shader}/app/types/materials.ts +0 -0
- /package/layers/{shader → render/shader}/app/types/renderer.ts +0 -0
- /package/layers/{shader → render/shader}/app/types/tsl.ts +0 -0
- /package/layers/{shader → render/shader}/app/types/uniforms.ts +0 -0
- /package/layers/{shader → render/shader}/app/utils/tsl/animation.ts +0 -0
- /package/layers/{shader → render/shader}/app/utils/tsl/color.ts +0 -0
- /package/layers/{shader → render/shader}/app/utils/tsl/index.ts +0 -0
- /package/layers/{shader → render/shader}/app/utils/tsl/math.ts +0 -0
- /package/layers/{shader → render/shader}/app/utils/tsl/noise.ts +0 -0
- /package/layers/{shader → render/shader}/app/utils/tsl/oklch.ts +0 -0
- /package/layers/{shader → render/shader}/app/utils/tsl/patterns.ts +0 -0
- /package/layers/{shader → render/shader}/app/utils/tsl/tween.ts +0 -0
- /package/layers/{shader → render/shader}/app/utils/tsl/uv.ts +0 -0
- /package/layers/{shader → render/shader}/nuxt.config.ts +0 -0
- /package/layers/{layout → structure/layout}/app/components/Layout/Section/Gallery.vue +0 -0
- /package/layers/{layout → structure/layout}/app/components/Layout/Section/Hero.vue +0 -0
- /package/layers/{layout → structure/layout}/app/components/Layout/Section/Split.vue +0 -0
- /package/layers/{layout → structure/layout}/app/composables/useGridConfig.ts +0 -0
- /package/layers/{layout → structure/layout}/app/types/index.ts +0 -0
- /package/layers/{layout → structure/layout}/tailwind.config.js +0 -0
- /package/layers/{navigation → structure/navigation}/app/assets/css/navigation.css +0 -0
- /package/layers/{navigation → structure/navigation}/app/components/Links/Group.vue +0 -0
- /package/layers/{navigation → structure/navigation}/app/components/Links/Named.vue +0 -0
- /package/layers/{navigation → structure/navigation}/app/components/Mast/Footer.vue +0 -0
- /package/layers/{navigation → structure/navigation}/app/components/Mast/NavModal.vue +0 -0
- /package/layers/{navigation → structure/navigation}/app/components/Site/Subtitle.vue +0 -0
- /package/layers/{navigation → structure/navigation}/app/components/Site/Title.vue +0 -0
- /package/layers/{navigation → structure/navigation}/app/composables/toast.ts +0 -0
- /package/layers/{navigation → structure/navigation}/app/composables/useSite.ts +0 -0
- /package/layers/{navigation → structure/navigation}/app/types/index.ts +0 -0
- /package/layers/{navigation → structure/navigation}/app/types/nav.ts +0 -0
- /package/layers/{core → structure/navigation}/app/utils/regex.ts +0 -0
- /package/layers/{navigation → structure/navigation}/app/utils/site.test.ts +0 -0
- /package/layers/{navigation → structure/navigation}/app/utils/site.ts +0 -0
- /package/layers/{navigation → structure/navigation}/package.json +0 -0
- /package/layers/{routing → structure/routing}/app/composables/useFeatureFlags.ts +0 -0
- /package/layers/{routing → structure/routing}/app/composables/useMaintenance.ts +0 -0
- /package/layers/{routing → structure/routing}/app/composables/useRoutingConfig.ts +0 -0
- /package/layers/{routing → structure/routing}/app/middleware/01.maintenance.global.ts +0 -0
- /package/layers/{routing → structure/routing}/app/middleware/02.governance.global.ts +0 -0
- /package/layers/{routing → structure/routing}/app/plugins/feature-flags.client.ts +0 -0
- /package/layers/{routing → structure/routing}/app/plugins/scroll-routing.client.ts +0 -0
- /package/layers/{routing → structure/routing}/app/types/index.ts +0 -0
- /package/layers/{routing → structure/routing}/app/types/route-meta.d.ts +0 -0
- /package/layers/{routing → structure/routing}/app/types/routing.ts +0 -0
- /package/layers/{routing → structure/routing}/app/utils/resolveRoute.test.ts +0 -0
- /package/layers/{routing → structure/routing}/app/utils/resolveRoute.ts +0 -0
- /package/layers/{routing → structure/routing}/server/api/feature-flags.get.ts +0 -0
|
@@ -0,0 +1,977 @@
|
|
|
1
|
+
# Grid Layer Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Add a new `grid` layer (Tier 2 — Structure) providing font-size-derived vertical rhythm plus data-driven, arbitrary-width column composition (named presets and custom track arrays), with a dev-only visual builder for constructing custom compositions.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Two independent, composable concerns. (1) Rhythm — CSS custom properties on `.grid-root`, derived unconditionally from `--fs`, reimplementing `LAYOUT-SYSTEM.md`'s derivation chain (steps 1–6 only; column derivation is replaced by (2)). (2) Column composition — a `TrackSegment[]` data shape, resolved from a named preset registry (`app.config.ts`) or passed inline, converted to a `grid-template-columns` string by a pure function. Named CSS grid lines are the primary addressing mechanism (they survive `subgrid` boundaries); numeric index/span is a fallback util only, no component ships for it in this phase.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Nuxt 4 layer conventions (`app/` srcDir, `#layers/grid` alias), TypeScript, Vitest (`layers/**/*.test.ts` auto-discovered by root `vitest.config.mts`), Vue 3 `<script setup lang="ts">` for the playground builder page only (no components ship in the layer itself).
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-09-04-grid-layer-design.md`
|
|
12
|
+
|
|
13
|
+
## Global Constraints
|
|
14
|
+
|
|
15
|
+
- New layer only — do not modify `layers/structure/layout/**` (its `LayoutMain`/`LayoutGridItem`/`Section`/`Hero`/`Split`/`Gallery`/`useGridConfig` are explicitly out of scope; reworking them to consume this layer is a future pass).
|
|
16
|
+
- Do not carry forward the `layout-fluid-rebuild` worktree's uncommitted `app.config.ts`/`types/layouts.ts` changes — this layer reimplements the rhythm math fresh, independent of that worktree.
|
|
17
|
+
- Rhythm CSS constraints (from `LAYOUT-SYSTEM.md`, carried forward unconditionally): every `clamp()` keeps a `rem`/`rlh` term (WCAG 1.4.4 zoom safety); `svh`/`svi` only, never `dvh`/`vw`/`vh`; `rlh` not `lh`; `container-type` never on `body`.
|
|
18
|
+
- No component ships in the `grid` layer itself in this phase — only CSS, a composable, and pure utils. The only Vue component is the playground's dev-only builder page.
|
|
19
|
+
- No Bento-grid preset, no row-track presets beyond the rhythm floor, no save-to-registry automation from the builder UI — all explicitly out of scope per spec.
|
|
20
|
+
- Package name: `kmcom-layer-grid`. Layer path: `layers/structure/grid`. Alias: `#layers/grid` (root), `#layers/grid/types` (types dir).
|
|
21
|
+
- Style: no semicolons, single quotes, ES5 trailing commas, max 100 char lines, `<script setup lang="ts">` block order script → template → style (per `.claude/rules/typescript.md` and `.claude/rules/vue-components.md`).
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## File Structure
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
layers/structure/grid/
|
|
29
|
+
├── app/
|
|
30
|
+
│ ├── assets/css/
|
|
31
|
+
│ │ └── grid.css # Task 2 — rhythm custom properties, .grid-root
|
|
32
|
+
│ ├── composables/
|
|
33
|
+
│ │ └── useGridTracks.ts # Task 3 — resolve() + toColumnsCss(), composable wrapper
|
|
34
|
+
│ ├── types/
|
|
35
|
+
│ │ └── tracks.ts # Task 1 — TrackSegment, TrackConfig, GridPreset
|
|
36
|
+
│ ├── utils/
|
|
37
|
+
│ │ ├── placement.ts # Task 4 — placementFromLines, placementFromIndex
|
|
38
|
+
│ │ └── placement.test.ts # Task 4
|
|
39
|
+
│ └── app.config.ts # Task 5 — gridLayer.presets registry (swiss, editorial)
|
|
40
|
+
├── nuxt.config.ts # Task 1 — $meta.name: 'grid', extends core, alias, css
|
|
41
|
+
├── package.json # Task 1 — kmcom-layer-grid
|
|
42
|
+
└── CLAUDE.md # Task 1 — layer reference doc
|
|
43
|
+
|
|
44
|
+
layers/structure/grid/app/composables/useGridTracks.test.ts # Task 3
|
|
45
|
+
|
|
46
|
+
apps/playground/nuxt.config.ts # Task 6 — MODIFY: register grid in layer resolver
|
|
47
|
+
apps/playground/app/pages/grid-builder.vue # Task 7 — dev-only visual builder page
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Boundary rationale:** `types/tracks.ts` has zero runtime dependents outside this layer's own composable/utils, so it's the natural first task (nothing else compiles without it). `useGridTracks.ts` and `utils/placement.ts` are independent pure-function modules — neither imports the other — so they're separate tasks or could run in parallel. `app.config.ts` depends on `types/tracks.ts` (for `GridPreset`) but not on the composable, so it's its own task. The playground builder page is last because it's the only consumer that needs the full composable + utils surface working end to end.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
### Task 1: Layer scaffold + track types
|
|
55
|
+
|
|
56
|
+
**Files:**
|
|
57
|
+
|
|
58
|
+
- Create: `layers/structure/grid/nuxt.config.ts`
|
|
59
|
+
- Create: `layers/structure/grid/package.json`
|
|
60
|
+
- Create: `layers/structure/grid/CLAUDE.md`
|
|
61
|
+
- Create: `layers/structure/grid/app/types/tracks.ts`
|
|
62
|
+
- Test: `layers/structure/grid/app/types/tracks.test.ts`
|
|
63
|
+
|
|
64
|
+
**Interfaces:**
|
|
65
|
+
|
|
66
|
+
- Produces: `TrackSegment` (`{ size: number; lineStart?: string | string[]; lineEnd?: string | string[] }`), `TrackConfig` (`{ columns: TrackSegment[]; minTrackWidth?: string }`), `GridPreset` (`TrackConfig & { name: string }`) — all subsequent tasks import from `#layers/grid/types/tracks` (or relative `../types/tracks` within the layer).
|
|
67
|
+
|
|
68
|
+
- [ ] **Step 1: Create the layer directories**
|
|
69
|
+
|
|
70
|
+
Run: `mkdir -p layers/structure/grid/app/types layers/structure/grid/app/assets/css layers/structure/grid/app/composables layers/structure/grid/app/utils`
|
|
71
|
+
|
|
72
|
+
- [ ] **Step 2: Write `app/types/tracks.ts`**
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
export type TrackSegment = {
|
|
76
|
+
/** Relative size, same unit as CSS `fr`. 1 = one equal column. */
|
|
77
|
+
size: number
|
|
78
|
+
/** Optional named line(s) at the start of this segment, e.g. 'feature-start'. */
|
|
79
|
+
lineStart?: string | string[]
|
|
80
|
+
/** Optional named line(s) at the end of this segment. */
|
|
81
|
+
lineEnd?: string | string[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type TrackConfig = {
|
|
85
|
+
columns: TrackSegment[]
|
|
86
|
+
/** Track floor — reuses --measure-min unless overridden. */
|
|
87
|
+
minTrackWidth?: string
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type GridPreset = TrackConfig & {
|
|
91
|
+
name: string
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- [ ] **Step 3: Write the failing test for the type shapes**
|
|
96
|
+
|
|
97
|
+
Types have no runtime behavior, so the test is a compile-time shape check: it fails if the file
|
|
98
|
+
doesn't exist yet, and catches accidental breaking changes to the shape later (e.g. renaming
|
|
99
|
+
`lineStart`).
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
// layers/structure/grid/app/types/tracks.test.ts
|
|
103
|
+
import { describe, expect, it } from 'vitest'
|
|
104
|
+
|
|
105
|
+
import type { GridPreset, TrackConfig, TrackSegment } from './tracks'
|
|
106
|
+
|
|
107
|
+
describe('track types', () => {
|
|
108
|
+
it('accepts a minimal equal-width segment', () => {
|
|
109
|
+
const segment: TrackSegment = { size: 1 }
|
|
110
|
+
expect(segment.size).toBe(1)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('accepts a segment with named lines', () => {
|
|
114
|
+
const segment: TrackSegment = {
|
|
115
|
+
size: 2,
|
|
116
|
+
lineStart: 'feature-start',
|
|
117
|
+
lineEnd: ['feature-end', 'aside-start'],
|
|
118
|
+
}
|
|
119
|
+
expect(segment.lineEnd).toEqual(['feature-end', 'aside-start'])
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('accepts a TrackConfig with optional minTrackWidth', () => {
|
|
123
|
+
const config: TrackConfig = {
|
|
124
|
+
columns: [{ size: 1 }, { size: 2 }],
|
|
125
|
+
minTrackWidth: '20rem',
|
|
126
|
+
}
|
|
127
|
+
expect(config.columns).toHaveLength(2)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('a GridPreset is a named TrackConfig', () => {
|
|
131
|
+
const preset: GridPreset = { name: 'swiss', columns: [{ size: 1 }] }
|
|
132
|
+
expect(preset.name).toBe('swiss')
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- [ ] **Step 4: Run test to verify it fails**
|
|
138
|
+
|
|
139
|
+
Run: `pnpm vitest run layers/structure/grid/app/types/tracks.test.ts`
|
|
140
|
+
Expected: FAIL — cannot find module `./tracks` (file doesn't exist yet until Step 2 lands; if
|
|
141
|
+
Step 2 already ran, run this before Step 2 instead, or trust the type-checker catches shape
|
|
142
|
+
regressions on the next `pnpm typecheck` run — either ordering is fine for a types-only file).
|
|
143
|
+
|
|
144
|
+
- [ ] **Step 5: Run test to verify it passes**
|
|
145
|
+
|
|
146
|
+
Run: `pnpm vitest run layers/structure/grid/app/types/tracks.test.ts`
|
|
147
|
+
Expected: PASS (4 tests)
|
|
148
|
+
|
|
149
|
+
- [ ] **Step 6: Write `nuxt.config.ts`**
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
153
|
+
export default defineNuxtConfig({
|
|
154
|
+
$meta: {
|
|
155
|
+
name: 'grid',
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
extends: ['../../core'],
|
|
159
|
+
|
|
160
|
+
alias: {
|
|
161
|
+
'#layers/grid': import.meta.dirname,
|
|
162
|
+
'#layers/grid/types': `${import.meta.dirname}/app/types`,
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
css: ['#layers/grid/app/assets/css/grid.css'],
|
|
166
|
+
|
|
167
|
+
compatibilityDate: '2026-01-20',
|
|
168
|
+
|
|
169
|
+
typescript: {
|
|
170
|
+
typeCheck: false,
|
|
171
|
+
strict: true,
|
|
172
|
+
},
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- [ ] **Step 7: Write `package.json`**
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"name": "kmcom-layer-grid",
|
|
181
|
+
"type": "module",
|
|
182
|
+
"version": "0.0.1",
|
|
183
|
+
"main": "./nuxt.config.ts",
|
|
184
|
+
"scripts": {
|
|
185
|
+
"typecheck": "vue-tsc --noEmit -p ../../../tsconfig.typecheck.json",
|
|
186
|
+
"dev": "nuxi dev .playground",
|
|
187
|
+
"dev:prepare": "nuxt prepare .playground",
|
|
188
|
+
"build": "nuxt build .playground",
|
|
189
|
+
"generate": "nuxt generate .playground",
|
|
190
|
+
"preview": "nuxt preview .playground",
|
|
191
|
+
"lint": "eslint ."
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
- [ ] **Step 8: Write `CLAUDE.md`**
|
|
197
|
+
|
|
198
|
+
```markdown
|
|
199
|
+
# Grid Layer
|
|
200
|
+
|
|
201
|
+
Font-size-derived vertical rhythm + data-driven column composition for Nuxt 4 applications.
|
|
202
|
+
Named presets (`swiss`, `editorial`) or custom `TrackSegment[]` arrays resolve to
|
|
203
|
+
`grid-template-columns` strings via `useGridTracks()`. See
|
|
204
|
+
`docs/superpowers/specs/2026-09-04-grid-layer-design.md` for the full design.
|
|
205
|
+
|
|
206
|
+
## Layer Structure
|
|
207
|
+
|
|
208
|
+
\`\`\`
|
|
209
|
+
layers/structure/grid/
|
|
210
|
+
├── app/
|
|
211
|
+
│ ├── assets/css/grid.css # .grid-root rhythm custom properties
|
|
212
|
+
│ ├── composables/useGridTracks.ts # resolve() + toColumnsCss()
|
|
213
|
+
│ ├── types/tracks.ts # TrackSegment, TrackConfig, GridPreset
|
|
214
|
+
│ ├── utils/placement.ts # placementFromLines, placementFromIndex
|
|
215
|
+
│ └── app.config.ts # gridLayer.presets registry
|
|
216
|
+
├── nuxt.config.ts
|
|
217
|
+
└── package.json
|
|
218
|
+
\`\`\`
|
|
219
|
+
|
|
220
|
+
## Quick Reference
|
|
221
|
+
|
|
222
|
+
\`\`\`ts
|
|
223
|
+
const { resolve, toColumnsCss } = useGridTracks()
|
|
224
|
+
|
|
225
|
+
const config = resolve('swiss') // named preset from app.config
|
|
226
|
+
const custom = resolve({ columns: [{ size: 1 }, { size: 2 }] }) // passthrough
|
|
227
|
+
|
|
228
|
+
const css = toColumnsCss(config)
|
|
229
|
+
// → 'minmax(var(--measure-min), 1fr) minmax(var(--measure-min), 1fr) ...'
|
|
230
|
+
\`\`\`
|
|
231
|
+
|
|
232
|
+
\`\`\`ts
|
|
233
|
+
import { placementFromIndex, placementFromLines } from '#layers/grid/app/utils/placement'
|
|
234
|
+
|
|
235
|
+
placementFromLines('feature-start', 'aside-end') // → 'feature-start / aside-end'
|
|
236
|
+
placementFromIndex(2, 3) // → '2 / span 3'
|
|
237
|
+
placementFromIndex(2, 'full') // → '2 / -1'
|
|
238
|
+
\`\`\`
|
|
239
|
+
|
|
240
|
+
## Out of scope (this layer, this phase)
|
|
241
|
+
|
|
242
|
+
- No `LayoutGridItem`-equivalent component — that's `layout`'s territory, deferred.
|
|
243
|
+
- No row-track presets beyond the rhythm-derived floor.
|
|
244
|
+
- No Bento-grid preset.
|
|
245
|
+
- `layers/structure/layout/**` is untouched — reworking it to consume this layer is a future pass.
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
- [ ] **Step 9: Commit**
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
git add layers/structure/grid/nuxt.config.ts layers/structure/grid/package.json \
|
|
252
|
+
layers/structure/grid/CLAUDE.md layers/structure/grid/app/types/tracks.ts \
|
|
253
|
+
layers/structure/grid/app/types/tracks.test.ts
|
|
254
|
+
git commit -m "feat(grid): scaffold new grid layer, add track types"
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
### Task 2: Rhythm CSS
|
|
260
|
+
|
|
261
|
+
**Files:**
|
|
262
|
+
|
|
263
|
+
- Create: `layers/structure/grid/app/assets/css/grid.css`
|
|
264
|
+
|
|
265
|
+
**Interfaces:**
|
|
266
|
+
|
|
267
|
+
- Consumes: nothing (pure CSS, no TS dependency).
|
|
268
|
+
- Produces: `.grid-root` class with `--unit`, `--measure-min`, `--edge-min`, `--lines`, `--lpf`,
|
|
269
|
+
`--fields` custom properties, consumed visually by Task 7's builder page (no TS import — CSS
|
|
270
|
+
classes are used by `class="grid-root"` in markup).
|
|
271
|
+
|
|
272
|
+
- [ ] **Step 1: Write `app/assets/css/grid.css`**
|
|
273
|
+
|
|
274
|
+
Reimplements `LAYOUT-SYSTEM.md`'s derivation chain, steps 1–6 (step 7, column derivation via
|
|
275
|
+
`auto-fill`, is intentionally NOT included — column composition comes from `toColumnsCss()`
|
|
276
|
+
instead, applied per-consumer via inline `style="grid-template-columns: ..."`).
|
|
277
|
+
|
|
278
|
+
```css
|
|
279
|
+
/* Grid layer rhythm — derives all vertical spacing from --fs. See
|
|
280
|
+
docs/superpowers/specs/2026-09-04-grid-layer-design.md and root LAYOUT-SYSTEM.md
|
|
281
|
+
for the full derivation rationale. Column composition is NOT here — it comes from
|
|
282
|
+
useGridTracks().toColumnsCss(), applied inline per grid-root instance. */
|
|
283
|
+
|
|
284
|
+
@property --fs {
|
|
285
|
+
syntax: '<length>';
|
|
286
|
+
inherits: true;
|
|
287
|
+
initial-value: 1rem;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
:root {
|
|
291
|
+
/* rem term keeps this WCAG 1.4.4 zoom-safe; svi never vw (viewport-bar-collapse safe) */
|
|
292
|
+
--fs: clamp(1rem, 0.95rem + 0.25svi, 1.125rem);
|
|
293
|
+
font-size: var(--fs);
|
|
294
|
+
/* rounded, not raw, so grid boundaries land on-pixel */
|
|
295
|
+
line-height: round(nearest, var(--fs) * 1.5, 1px);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.grid-root {
|
|
299
|
+
/* rlh not lh: always resolves against root, unaffected by a nested element's own
|
|
300
|
+
line-height */
|
|
301
|
+
--unit: 1rlh;
|
|
302
|
+
--measure-min: 22rem;
|
|
303
|
+
/* margin floor > 0, steep svi slope so it shrinks fast on narrow screens */
|
|
304
|
+
--edge-min: clamp(1rlh, 8svi, 3rlh);
|
|
305
|
+
--avail-b: calc(100svh - 2 * var(--edge-min));
|
|
306
|
+
--lines: calc(round(down, var(--avail-b), var(--unit)) / var(--unit));
|
|
307
|
+
--lpf: clamp(4, round(down, calc(100svh / var(--unit) / 4.5), 1), 8);
|
|
308
|
+
--fields: max(1, round(down, calc((var(--lines) + 1) / (var(--lpf) + 1)), 1));
|
|
309
|
+
|
|
310
|
+
display: grid;
|
|
311
|
+
grid-auto-rows: minmax(calc(var(--lpf) * var(--unit)), max-content);
|
|
312
|
+
gap: var(--unit);
|
|
313
|
+
container-name: grid-root;
|
|
314
|
+
container-type: inline-size;
|
|
315
|
+
padding-inline: var(--edge-min);
|
|
316
|
+
padding-block: var(--edge-min);
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
- [ ] **Step 2: Verify it's wired into the layer's `nuxt.config.ts`**
|
|
321
|
+
|
|
322
|
+
Task 1 Step 6 already added `css: ['#layers/grid/app/assets/css/grid.css']` — confirm the path
|
|
323
|
+
matches this file's actual location:
|
|
324
|
+
|
|
325
|
+
Run: `grep -n "grid.css" layers/structure/grid/nuxt.config.ts`
|
|
326
|
+
Expected: `css: ['#layers/grid/app/assets/css/grid.css'],`
|
|
327
|
+
|
|
328
|
+
- [ ] **Step 3: Commit**
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
git add layers/structure/grid/app/assets/css/grid.css
|
|
332
|
+
git commit -m "feat(grid): add rhythm CSS derived from --fs"
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
(Manual/visual verification of this CSS happens in Task 7, once a page actually renders
|
|
336
|
+
`.grid-root` — CSS custom-property derivation chains can't be meaningfully unit-tested in
|
|
337
|
+
Vitest, and a computed-style assertion would need a real browser per `LAYOUT-SYSTEM.md`
|
|
338
|
+
Pitfall 1's own lesson: `pnpm build`/static checks don't catch computed-value failures.)
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
### Task 3: `useGridTracks()` — track resolution and CSS generation
|
|
343
|
+
|
|
344
|
+
**Files:**
|
|
345
|
+
|
|
346
|
+
- Create: `layers/structure/grid/app/composables/useGridTracks.ts`
|
|
347
|
+
- Test: `layers/structure/grid/app/composables/useGridTracks.test.ts`
|
|
348
|
+
|
|
349
|
+
**Interfaces:**
|
|
350
|
+
|
|
351
|
+
- Consumes: `TrackSegment`, `TrackConfig`, `GridPreset` from `../types/tracks` (Task 1).
|
|
352
|
+
- Produces: `resolve(input: string | TrackConfig, presets?: Record<string, GridPreset>): TrackConfig`
|
|
353
|
+
and `toColumnsCss(config: TrackConfig): string` — both exported as standalone pure functions
|
|
354
|
+
(unit-testable without Nuxt context) — plus the `useGridTracks()` composable that wraps them
|
|
355
|
+
against `useAppConfig().gridLayer.presets`. Task 5 (`app.config.ts`) and Task 7 (builder page)
|
|
356
|
+
both call `useGridTracks()`; Task 6 does not.
|
|
357
|
+
|
|
358
|
+
- [ ] **Step 1: Write the failing tests**
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
// layers/structure/grid/app/composables/useGridTracks.test.ts
|
|
362
|
+
import { describe, expect, it } from 'vitest'
|
|
363
|
+
|
|
364
|
+
import type { GridPreset, TrackConfig } from '../types/tracks'
|
|
365
|
+
import { resolve, toColumnsCss } from './useGridTracks'
|
|
366
|
+
|
|
367
|
+
describe('toColumnsCss', () => {
|
|
368
|
+
it('renders equal segments with no line names', () => {
|
|
369
|
+
const config: TrackConfig = { columns: [{ size: 1 }, { size: 1 }, { size: 1 }] }
|
|
370
|
+
expect(toColumnsCss(config)).toBe(
|
|
371
|
+
'minmax(var(--measure-min), 1fr) minmax(var(--measure-min), 1fr) ' +
|
|
372
|
+
'minmax(var(--measure-min), 1fr)'
|
|
373
|
+
)
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
it('renders mixed sizes (8 equal + double + triple)', () => {
|
|
377
|
+
const config: TrackConfig = {
|
|
378
|
+
columns: [
|
|
379
|
+
{ size: 1 },
|
|
380
|
+
{ size: 1 },
|
|
381
|
+
{ size: 1 },
|
|
382
|
+
{ size: 1 },
|
|
383
|
+
{ size: 1 },
|
|
384
|
+
{ size: 1 },
|
|
385
|
+
{ size: 1 },
|
|
386
|
+
{ size: 2 },
|
|
387
|
+
{ size: 3 },
|
|
388
|
+
],
|
|
389
|
+
}
|
|
390
|
+
const css = toColumnsCss(config)
|
|
391
|
+
expect(css.split(' ').filter((s) => s.startsWith('minmax'))).toHaveLength(9)
|
|
392
|
+
expect(css).toContain('minmax(var(--measure-min), 2fr)')
|
|
393
|
+
expect(css).toContain('minmax(var(--measure-min), 3fr)')
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
it('brackets a leading lineStart and trailing lineEnd', () => {
|
|
397
|
+
const config: TrackConfig = {
|
|
398
|
+
columns: [{ size: 1, lineStart: 'a-start', lineEnd: 'a-end' }],
|
|
399
|
+
}
|
|
400
|
+
expect(toColumnsCss(config)).toBe('[a-start] minmax(var(--measure-min), 1fr) [a-end]')
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
it('merges an adjacent lineEnd and lineStart into one bracket', () => {
|
|
404
|
+
const config: TrackConfig = {
|
|
405
|
+
columns: [
|
|
406
|
+
{ size: 2, lineStart: 'feature-start', lineEnd: 'feature-end' },
|
|
407
|
+
{ size: 1, lineStart: 'aside-start', lineEnd: 'aside-end' },
|
|
408
|
+
],
|
|
409
|
+
}
|
|
410
|
+
expect(toColumnsCss(config)).toBe(
|
|
411
|
+
'[feature-start] minmax(var(--measure-min), 2fr) ' +
|
|
412
|
+
'[feature-end aside-start] minmax(var(--measure-min), 1fr) [aside-end]'
|
|
413
|
+
)
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
it('supports a segment with only lineStart set', () => {
|
|
417
|
+
const config: TrackConfig = {
|
|
418
|
+
columns: [{ size: 1, lineStart: 'only-start' }, { size: 1 }],
|
|
419
|
+
}
|
|
420
|
+
expect(toColumnsCss(config)).toBe(
|
|
421
|
+
'[only-start] minmax(var(--measure-min), 1fr) minmax(var(--measure-min), 1fr)'
|
|
422
|
+
)
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
it('accepts an array of line names', () => {
|
|
426
|
+
const config: TrackConfig = {
|
|
427
|
+
columns: [{ size: 1, lineStart: ['a', 'b'] }],
|
|
428
|
+
}
|
|
429
|
+
expect(toColumnsCss(config)).toBe('[a b] minmax(var(--measure-min), 1fr)')
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
it('uses minTrackWidth override instead of the --measure-min default', () => {
|
|
433
|
+
const config: TrackConfig = { columns: [{ size: 1 }], minTrackWidth: '20rem' }
|
|
434
|
+
expect(toColumnsCss(config)).toBe('minmax(20rem, 1fr)')
|
|
435
|
+
})
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
describe('resolve', () => {
|
|
439
|
+
const presets: Record<string, GridPreset> = {
|
|
440
|
+
swiss: { name: 'swiss', columns: [{ size: 1 }] },
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
it('looks up a named preset from the registry', () => {
|
|
444
|
+
expect(resolve('swiss', presets)).toBe(presets.swiss)
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
it('throws for an unknown preset name', () => {
|
|
448
|
+
expect(() => resolve('nope', presets)).toThrow('Unknown grid preset: "nope"')
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
it('passes a TrackConfig object through unchanged', () => {
|
|
452
|
+
const custom: TrackConfig = { columns: [{ size: 1 }, { size: 2 }] }
|
|
453
|
+
expect(resolve(custom, presets)).toBe(custom)
|
|
454
|
+
})
|
|
455
|
+
})
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
- [ ] **Step 2: Run tests to verify they fail**
|
|
459
|
+
|
|
460
|
+
Run: `pnpm vitest run layers/structure/grid/app/composables/useGridTracks.test.ts`
|
|
461
|
+
Expected: FAIL — cannot find module `./useGridTracks`
|
|
462
|
+
|
|
463
|
+
- [ ] **Step 3: Write `app/composables/useGridTracks.ts`**
|
|
464
|
+
|
|
465
|
+
```ts
|
|
466
|
+
import type { GridPreset, TrackConfig } from '../types/tracks'
|
|
467
|
+
|
|
468
|
+
type GridLayerAppConfig = {
|
|
469
|
+
gridLayer?: {
|
|
470
|
+
presets?: Record<string, GridPreset>
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function normalizeNames(names: string | string[] | undefined): string[] {
|
|
475
|
+
if (!names) return []
|
|
476
|
+
return Array.isArray(names) ? names : [names]
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function toBracket(names: string[]): string {
|
|
480
|
+
return names.length > 0 ? `[${names.join(' ')}]` : ''
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Builds a `grid-template-columns` value from a TrackConfig. Each segment becomes a
|
|
485
|
+
* `minmax(floor, Nfr)` track; adjacent lineEnd/lineStart names merge into one bracket,
|
|
486
|
+
* per standard named-grid-line syntax.
|
|
487
|
+
*/
|
|
488
|
+
export function toColumnsCss(config: TrackConfig): string {
|
|
489
|
+
const minWidth = config.minTrackWidth ?? 'var(--measure-min)'
|
|
490
|
+
const segments = config.columns
|
|
491
|
+
|
|
492
|
+
const parts: string[] = []
|
|
493
|
+
|
|
494
|
+
segments.forEach((segment, index) => {
|
|
495
|
+
const previous = index > 0 ? segments[index - 1] : undefined
|
|
496
|
+
const leadNames = [...normalizeNames(previous?.lineEnd), ...normalizeNames(segment.lineStart)]
|
|
497
|
+
const leadBracket = toBracket(leadNames)
|
|
498
|
+
if (leadBracket) parts.push(leadBracket)
|
|
499
|
+
parts.push(`minmax(${minWidth}, ${segment.size}fr)`)
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
const lastSegment = segments[segments.length - 1]
|
|
503
|
+
const trailBracket = toBracket(normalizeNames(lastSegment?.lineEnd))
|
|
504
|
+
if (trailBracket) parts.push(trailBracket)
|
|
505
|
+
|
|
506
|
+
return parts.join(' ')
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Resolves a preset name against a registry, or passes a TrackConfig object through
|
|
511
|
+
* unchanged. Exported standalone so it's testable without Nuxt's useAppConfig context.
|
|
512
|
+
*/
|
|
513
|
+
export function resolve(
|
|
514
|
+
input: string | TrackConfig,
|
|
515
|
+
presets?: Record<string, GridPreset>
|
|
516
|
+
): TrackConfig {
|
|
517
|
+
if (typeof input === 'string') {
|
|
518
|
+
const found = presets?.[input]
|
|
519
|
+
if (!found) {
|
|
520
|
+
throw new Error(`Unknown grid preset: "${input}"`)
|
|
521
|
+
}
|
|
522
|
+
return found
|
|
523
|
+
}
|
|
524
|
+
return input
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export function useGridTracks() {
|
|
528
|
+
const appConfig = useAppConfig() as GridLayerAppConfig
|
|
529
|
+
const presets = computed(() => appConfig.gridLayer?.presets)
|
|
530
|
+
|
|
531
|
+
return {
|
|
532
|
+
resolve: (input: string | TrackConfig) => resolve(input, presets.value),
|
|
533
|
+
toColumnsCss,
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
- [ ] **Step 4: Run tests to verify they pass**
|
|
539
|
+
|
|
540
|
+
Run: `pnpm vitest run layers/structure/grid/app/composables/useGridTracks.test.ts`
|
|
541
|
+
Expected: PASS (10 tests)
|
|
542
|
+
|
|
543
|
+
- [ ] **Step 5: Commit**
|
|
544
|
+
|
|
545
|
+
```bash
|
|
546
|
+
git add layers/structure/grid/app/composables/useGridTracks.ts \
|
|
547
|
+
layers/structure/grid/app/composables/useGridTracks.test.ts
|
|
548
|
+
git commit -m "feat(grid): add useGridTracks composable (resolve, toColumnsCss)"
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
### Task 4: Placement utils
|
|
554
|
+
|
|
555
|
+
**Files:**
|
|
556
|
+
|
|
557
|
+
- Create: `layers/structure/grid/app/utils/placement.ts`
|
|
558
|
+
- Test: `layers/structure/grid/app/utils/placement.test.ts`
|
|
559
|
+
|
|
560
|
+
**Interfaces:**
|
|
561
|
+
|
|
562
|
+
- Consumes: nothing (pure string functions, no type imports needed).
|
|
563
|
+
- Produces: `placementFromLines(startLine: string, endLine: string): string`,
|
|
564
|
+
`placementFromIndex(colStart: number, colSpan: number | 'full'): string` — used by Task 7's
|
|
565
|
+
builder page preview cells (named-line path) and available as the numeric fallback for future
|
|
566
|
+
`layout` migration (not consumed anywhere else in this plan).
|
|
567
|
+
|
|
568
|
+
- [ ] **Step 1: Write the failing tests**
|
|
569
|
+
|
|
570
|
+
```ts
|
|
571
|
+
// layers/structure/grid/app/utils/placement.test.ts
|
|
572
|
+
import { describe, expect, it } from 'vitest'
|
|
573
|
+
|
|
574
|
+
import { placementFromIndex, placementFromLines } from './placement'
|
|
575
|
+
|
|
576
|
+
describe('placementFromLines', () => {
|
|
577
|
+
it('joins two named lines with a slash', () => {
|
|
578
|
+
expect(placementFromLines('feature-start', 'aside-end')).toBe('feature-start / aside-end')
|
|
579
|
+
})
|
|
580
|
+
})
|
|
581
|
+
|
|
582
|
+
describe('placementFromIndex', () => {
|
|
583
|
+
it('builds a numeric span from colStart and colSpan', () => {
|
|
584
|
+
expect(placementFromIndex(2, 3)).toBe('2 / span 3')
|
|
585
|
+
})
|
|
586
|
+
|
|
587
|
+
it('spans to the last line when colSpan is "full"', () => {
|
|
588
|
+
expect(placementFromIndex(4, 'full')).toBe('4 / -1')
|
|
589
|
+
})
|
|
590
|
+
})
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
- [ ] **Step 2: Run tests to verify they fail**
|
|
594
|
+
|
|
595
|
+
Run: `pnpm vitest run layers/structure/grid/app/utils/placement.test.ts`
|
|
596
|
+
Expected: FAIL — cannot find module `./placement`
|
|
597
|
+
|
|
598
|
+
- [ ] **Step 3: Write `app/utils/placement.ts`**
|
|
599
|
+
|
|
600
|
+
```ts
|
|
601
|
+
/** Grid-column value addressing a named-line range. Primary placement method — named
|
|
602
|
+
* lines survive CSS subgrid boundaries, unlike grid-template-areas. */
|
|
603
|
+
export function placementFromLines(startLine: string, endLine: string): string {
|
|
604
|
+
return `${startLine} / ${endLine}`
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/** Numeric fallback for auto-flowing content with no named slot to target. `'full'`
|
|
608
|
+
* spans to the grid's last line (-1), matching the shape of layout's current
|
|
609
|
+
* GridItem colSpan prop for an additive future migration. */
|
|
610
|
+
export function placementFromIndex(colStart: number, colSpan: number | 'full'): string {
|
|
611
|
+
if (colSpan === 'full') {
|
|
612
|
+
return `${colStart} / -1`
|
|
613
|
+
}
|
|
614
|
+
return `${colStart} / span ${colSpan}`
|
|
615
|
+
}
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
- [ ] **Step 4: Run tests to verify they pass**
|
|
619
|
+
|
|
620
|
+
Run: `pnpm vitest run layers/structure/grid/app/utils/placement.test.ts`
|
|
621
|
+
Expected: PASS (3 tests)
|
|
622
|
+
|
|
623
|
+
- [ ] **Step 5: Commit**
|
|
624
|
+
|
|
625
|
+
```bash
|
|
626
|
+
git add layers/structure/grid/app/utils/placement.ts \
|
|
627
|
+
layers/structure/grid/app/utils/placement.test.ts
|
|
628
|
+
git commit -m "feat(grid): add placement utils (named-line and numeric fallback)"
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
### Task 5: Preset registry (`app.config.ts`)
|
|
634
|
+
|
|
635
|
+
**Files:**
|
|
636
|
+
|
|
637
|
+
- Create: `layers/structure/grid/app/app.config.ts`
|
|
638
|
+
|
|
639
|
+
**Interfaces:**
|
|
640
|
+
|
|
641
|
+
- Consumes: `GridPreset` from `./types/tracks` (Task 1).
|
|
642
|
+
- Produces: `gridLayer.presets.swiss` and `gridLayer.presets.editorial`, read by
|
|
643
|
+
`useGridTracks()` (Task 3) via `useAppConfig()`. Task 7's builder page uses `resolve('swiss')`
|
|
644
|
+
and `resolve('editorial')` to render the two known-shape previews mentioned in the spec's
|
|
645
|
+
testing section.
|
|
646
|
+
|
|
647
|
+
- [ ] **Step 1: Write `app/app.config.ts`**
|
|
648
|
+
|
|
649
|
+
No unit test for this file — it's static data plus a type augmentation, and its correctness is
|
|
650
|
+
exercised end to end by Task 7 (visually rendering both presets) per the spec's own testing
|
|
651
|
+
plan, which explicitly scopes preset verification to that visual check rather than a unit test.
|
|
652
|
+
|
|
653
|
+
```ts
|
|
654
|
+
import type { GridPreset } from './types/tracks'
|
|
655
|
+
|
|
656
|
+
const SWISS_COLUMNS = Array.from({ length: 18 }, () => ({ size: 1 }))
|
|
657
|
+
|
|
658
|
+
export default defineAppConfig({
|
|
659
|
+
gridLayer: {
|
|
660
|
+
presets: {
|
|
661
|
+
swiss: {
|
|
662
|
+
name: 'swiss',
|
|
663
|
+
columns: SWISS_COLUMNS,
|
|
664
|
+
},
|
|
665
|
+
editorial: {
|
|
666
|
+
name: 'editorial',
|
|
667
|
+
columns: [
|
|
668
|
+
{ size: 2, lineStart: 'feature-start', lineEnd: 'feature-end' },
|
|
669
|
+
{ size: 1, lineStart: 'aside-start', lineEnd: 'aside-end' },
|
|
670
|
+
],
|
|
671
|
+
},
|
|
672
|
+
},
|
|
673
|
+
},
|
|
674
|
+
})
|
|
675
|
+
|
|
676
|
+
declare module '@nuxt/schema' {
|
|
677
|
+
interface AppConfigInput {
|
|
678
|
+
gridLayer?: {
|
|
679
|
+
presets?: Record<string, GridPreset>
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
- [ ] **Step 2: Verify the shape matches the tested `toColumnsCss` behavior**
|
|
686
|
+
|
|
687
|
+
Run: `pnpm vitest run layers/structure/grid/app/composables/useGridTracks.test.ts`
|
|
688
|
+
Expected: PASS (still 10 tests — this step is a regression check, not new coverage; the
|
|
689
|
+
"mixed sizes" and "merges an adjacent lineEnd and lineStart" test cases in Task 3 already cover
|
|
690
|
+
the exact shapes used here for `swiss` and `editorial`)
|
|
691
|
+
|
|
692
|
+
- [ ] **Step 3: Commit**
|
|
693
|
+
|
|
694
|
+
```bash
|
|
695
|
+
git add layers/structure/grid/app/app.config.ts
|
|
696
|
+
git commit -m "feat(grid): add swiss and editorial preset registry"
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
---
|
|
700
|
+
|
|
701
|
+
### Task 6: Register `grid` in the playground layer resolver
|
|
702
|
+
|
|
703
|
+
**Files:**
|
|
704
|
+
|
|
705
|
+
- Modify: `apps/playground/nuxt.config.ts:2-95`
|
|
706
|
+
|
|
707
|
+
**Interfaces:**
|
|
708
|
+
|
|
709
|
+
- Consumes: nothing new — this task only wires the layer path into the existing
|
|
710
|
+
`AVAILABLE_LAYERS`/`LAYER_PATHS`/`LAYER_DEPENDENCIES` resolver so
|
|
711
|
+
`PLAYGROUND_LAYERS=core,grid pnpm dev` (Task 7's manual verification) can load it.
|
|
712
|
+
- Produces: nothing new for later tasks to consume — this is pure registration.
|
|
713
|
+
|
|
714
|
+
- [ ] **Step 1: Add `'grid'` to `AVAILABLE_LAYERS`**
|
|
715
|
+
|
|
716
|
+
In `apps/playground/nuxt.config.ts`, add `'grid'` to the `AVAILABLE_LAYERS` array (alongside
|
|
717
|
+
`'layout'`, since it's the same tier):
|
|
718
|
+
|
|
719
|
+
```ts
|
|
720
|
+
const AVAILABLE_LAYERS = [
|
|
721
|
+
'core',
|
|
722
|
+
'seo',
|
|
723
|
+
'scripts',
|
|
724
|
+
'element',
|
|
725
|
+
'typography',
|
|
726
|
+
'navigation',
|
|
727
|
+
'visual',
|
|
728
|
+
'layout',
|
|
729
|
+
'grid',
|
|
730
|
+
'scroll',
|
|
731
|
+
// ...unchanged rest
|
|
732
|
+
] as const
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
- [ ] **Step 2: Add the path to `LAYER_PATHS`**
|
|
736
|
+
|
|
737
|
+
```ts
|
|
738
|
+
const LAYER_PATHS: Record<LayerName, string> = {
|
|
739
|
+
// ...unchanged entries
|
|
740
|
+
layout: '../../layers/structure/layout',
|
|
741
|
+
grid: '../../layers/structure/grid',
|
|
742
|
+
// ...unchanged rest
|
|
743
|
+
}
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
- [ ] **Step 3: Add the dependency entry to `LAYER_DEPENDENCIES`**
|
|
747
|
+
|
|
748
|
+
`grid` depends only on `core`, same as `layout`:
|
|
749
|
+
|
|
750
|
+
```ts
|
|
751
|
+
const LAYER_DEPENDENCIES: Record<LayerName, LayerName[]> = {
|
|
752
|
+
// ...unchanged entries
|
|
753
|
+
layout: ['core'],
|
|
754
|
+
grid: ['core'],
|
|
755
|
+
// ...unchanged rest
|
|
756
|
+
}
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
- [ ] **Step 4: Verify the registration typechecks**
|
|
760
|
+
|
|
761
|
+
Run: `pnpm --filter kmcom-playground typecheck`
|
|
762
|
+
Expected: no new errors (the `LayerName` union, `Record` types, and `resolveExtendedLayers()`
|
|
763
|
+
all derive from `AVAILABLE_LAYERS`, so a missing `LAYER_PATHS`/`LAYER_DEPENDENCIES` entry for
|
|
764
|
+
`'grid'` would be a TS error here — this is the automated check standing in for a runtime test,
|
|
765
|
+
since layer registration has no unit-testable behavior of its own)
|
|
766
|
+
|
|
767
|
+
- [ ] **Step 5: Verify the layer loads at dev time**
|
|
768
|
+
|
|
769
|
+
Run: `PLAYGROUND_LAYERS=core,grid pnpm dev` (from `apps/playground/`), then stop it once the
|
|
770
|
+
dev server prints its ready message with no errors — full page verification happens in Task 7
|
|
771
|
+
once there's a page to load.
|
|
772
|
+
Expected: dev server starts cleanly, no "layer not found" or CSS resolution errors in the
|
|
773
|
+
console.
|
|
774
|
+
|
|
775
|
+
- [ ] **Step 6: Commit**
|
|
776
|
+
|
|
777
|
+
```bash
|
|
778
|
+
git add apps/playground/nuxt.config.ts
|
|
779
|
+
git commit -m "feat(grid): register grid layer in playground resolver"
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
---
|
|
783
|
+
|
|
784
|
+
### Task 7: Playground visual builder (`/grid-builder`)
|
|
785
|
+
|
|
786
|
+
**Files:**
|
|
787
|
+
|
|
788
|
+
- Create: `apps/playground/app/pages/grid-builder.vue`
|
|
789
|
+
|
|
790
|
+
**Interfaces:**
|
|
791
|
+
|
|
792
|
+
- Consumes: `useGridTracks()` (Task 3, `resolve`/`toColumnsCss`), `TrackSegment`/`TrackConfig`
|
|
793
|
+
types (Task 1), `.grid-root` CSS class (Task 2), `gridLayer.presets` (Task 5), the `grid`
|
|
794
|
+
layer registration (Task 6).
|
|
795
|
+
- Produces: nothing further downstream — this is the terminal consumer and the spec's
|
|
796
|
+
manual/visual verification point.
|
|
797
|
+
|
|
798
|
+
- [ ] **Step 1: Write `apps/playground/app/pages/grid-builder.vue`**
|
|
799
|
+
|
|
800
|
+
Dev-only page: add/remove/resize `TrackSegment`s (no drag library — up/down reorder buttons,
|
|
801
|
+
per YAGNI, since drag-and-drop is a UX nicety the spec doesn't require), a live `.grid-root`
|
|
802
|
+
preview with placeholder cells, and a copyable TS output panel. No persistence, no
|
|
803
|
+
save-to-registry — copy/paste is the hand-off, matching the spec's scope.
|
|
804
|
+
|
|
805
|
+
```vue
|
|
806
|
+
<script setup lang="ts">
|
|
807
|
+
import type { TrackConfig, TrackSegment } from '#layers/grid/types/tracks'
|
|
808
|
+
|
|
809
|
+
definePageMeta({ layout: false })
|
|
810
|
+
|
|
811
|
+
const { resolve, toColumnsCss } = useGridTracks()
|
|
812
|
+
|
|
813
|
+
const activePresetName = ref<'swiss' | 'editorial' | 'custom'>('custom')
|
|
814
|
+
const customSegments = ref<TrackSegment[]>([{ size: 1 }, { size: 1 }, { size: 2 }])
|
|
815
|
+
|
|
816
|
+
const activeConfig = computed<TrackConfig>(() => {
|
|
817
|
+
if (activePresetName.value === 'custom') {
|
|
818
|
+
return { columns: customSegments.value }
|
|
819
|
+
}
|
|
820
|
+
return resolve(activePresetName.value)
|
|
821
|
+
})
|
|
822
|
+
|
|
823
|
+
const columnsCss = computed(() => toColumnsCss(activeConfig.value))
|
|
824
|
+
|
|
825
|
+
const isCustom = computed(() => activePresetName.value === 'custom')
|
|
826
|
+
|
|
827
|
+
function addSegment() {
|
|
828
|
+
customSegments.value.push({ size: 1 })
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function removeSegment(index: number) {
|
|
832
|
+
customSegments.value.splice(index, 1)
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function moveSegment(index: number, direction: -1 | 1) {
|
|
836
|
+
const target = index + direction
|
|
837
|
+
if (target < 0 || target >= customSegments.value.length) return
|
|
838
|
+
const segments = customSegments.value
|
|
839
|
+
;[segments[index], segments[target]] = [segments[target], segments[index]]
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function updateSize(index: number, value: number) {
|
|
843
|
+
customSegments.value[index].size = Math.max(1, value)
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
function updateLineStart(index: number, value: string) {
|
|
847
|
+
customSegments.value[index].lineStart = value || undefined
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
function updateLineEnd(index: number, value: string) {
|
|
851
|
+
customSegments.value[index].lineEnd = value || undefined
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
const outputCode = computed(() => {
|
|
855
|
+
const lines = customSegments.value.map((segment) => {
|
|
856
|
+
const parts = [`size: ${segment.size}`]
|
|
857
|
+
if (segment.lineStart) parts.push(`lineStart: '${segment.lineStart}'`)
|
|
858
|
+
if (segment.lineEnd) parts.push(`lineEnd: '${segment.lineEnd}'`)
|
|
859
|
+
return ` { ${parts.join(', ')} },`
|
|
860
|
+
})
|
|
861
|
+
return `{\n columns: [\n${lines.join('\n')}\n ],\n}`
|
|
862
|
+
})
|
|
863
|
+
</script>
|
|
864
|
+
|
|
865
|
+
<template>
|
|
866
|
+
<div style="padding: 2rem; display: grid; gap: 2rem">
|
|
867
|
+
<h1>Grid builder</h1>
|
|
868
|
+
|
|
869
|
+
<div style="display: flex; gap: 0.5rem">
|
|
870
|
+
<button type="button" @click="activePresetName = 'swiss'">Swiss (18 equal)</button>
|
|
871
|
+
<button type="button" @click="activePresetName = 'editorial'">Editorial</button>
|
|
872
|
+
<button type="button" @click="activePresetName = 'custom'">Custom</button>
|
|
873
|
+
</div>
|
|
874
|
+
|
|
875
|
+
<section v-if="isCustom" style="display: grid; gap: 0.5rem">
|
|
876
|
+
<div
|
|
877
|
+
v-for="(segment, index) in customSegments"
|
|
878
|
+
:key="index"
|
|
879
|
+
style="display: flex; gap: 0.5rem; align-items: center"
|
|
880
|
+
>
|
|
881
|
+
<span>#{{ index + 1 }}</span>
|
|
882
|
+
<label>
|
|
883
|
+
size
|
|
884
|
+
<input
|
|
885
|
+
type="number"
|
|
886
|
+
min="1"
|
|
887
|
+
:value="segment.size"
|
|
888
|
+
@input="updateSize(index, Number(($event.target as HTMLInputElement).value))"
|
|
889
|
+
/>
|
|
890
|
+
</label>
|
|
891
|
+
<label>
|
|
892
|
+
lineStart
|
|
893
|
+
<input
|
|
894
|
+
type="text"
|
|
895
|
+
:value="segment.lineStart ?? ''"
|
|
896
|
+
@input="updateLineStart(index, ($event.target as HTMLInputElement).value)"
|
|
897
|
+
/>
|
|
898
|
+
</label>
|
|
899
|
+
<label>
|
|
900
|
+
lineEnd
|
|
901
|
+
<input
|
|
902
|
+
type="text"
|
|
903
|
+
:value="segment.lineEnd ?? ''"
|
|
904
|
+
@input="updateLineEnd(index, ($event.target as HTMLInputElement).value)"
|
|
905
|
+
/>
|
|
906
|
+
</label>
|
|
907
|
+
<button type="button" @click="moveSegment(index, -1)">↑</button>
|
|
908
|
+
<button type="button" @click="moveSegment(index, 1)">↓</button>
|
|
909
|
+
<button type="button" @click="removeSegment(index)">Remove</button>
|
|
910
|
+
</div>
|
|
911
|
+
<button type="button" @click="addSegment">Add segment</button>
|
|
912
|
+
</section>
|
|
913
|
+
|
|
914
|
+
<section>
|
|
915
|
+
<h2>Preview</h2>
|
|
916
|
+
<div class="grid-root" :style="{ gridTemplateColumns: columnsCss }">
|
|
917
|
+
<div
|
|
918
|
+
v-for="(segment, index) in activeConfig.columns"
|
|
919
|
+
:key="index"
|
|
920
|
+
style="border: 1px solid currentColor; padding: 0.5rem; text-align: center"
|
|
921
|
+
>
|
|
922
|
+
{{ segment.size }}fr
|
|
923
|
+
</div>
|
|
924
|
+
</div>
|
|
925
|
+
</section>
|
|
926
|
+
|
|
927
|
+
<section v-if="isCustom">
|
|
928
|
+
<h2>Output — paste into app.config.ts as a new preset</h2>
|
|
929
|
+
<pre><code>{{ outputCode }}</code></pre>
|
|
930
|
+
</section>
|
|
931
|
+
</div>
|
|
932
|
+
</template>
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
- [ ] **Step 2: Run typecheck**
|
|
936
|
+
|
|
937
|
+
Run: `pnpm --filter kmcom-playground typecheck`
|
|
938
|
+
Expected: no errors
|
|
939
|
+
|
|
940
|
+
- [ ] **Step 3: Manual visual verification — the three known shapes from the spec's testing plan**
|
|
941
|
+
|
|
942
|
+
Run: `PLAYGROUND_LAYERS=core,grid pnpm dev` (from `apps/playground/`), open
|
|
943
|
+
`http://localhost:3000/grid-builder`.
|
|
944
|
+
|
|
945
|
+
Expected, checked by eye:
|
|
946
|
+
|
|
947
|
+
1. Click "Swiss (18 equal)" — 18 equal-width preview cells render, all the same width.
|
|
948
|
+
2. Click "Editorial" — 2 preview cells render, the first roughly twice the width of the second.
|
|
949
|
+
3. Click "Custom", adjust segment sizes/add a segment (e.g. the 8-equal + double + triple shape
|
|
950
|
+
from the spec) — proportions in the preview visibly match the configured sizes, and the
|
|
951
|
+
output panel shows valid-looking TS matching the current segments.
|
|
952
|
+
|
|
953
|
+
- [ ] **Step 4: Commit**
|
|
954
|
+
|
|
955
|
+
```bash
|
|
956
|
+
git add apps/playground/app/pages/grid-builder.vue
|
|
957
|
+
git commit -m "feat(grid): add playground visual grid builder"
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
---
|
|
961
|
+
|
|
962
|
+
## Plan Self-Review Notes
|
|
963
|
+
|
|
964
|
+
- **Spec coverage:** rhythm CSS (Task 2), track types (Task 1), presets registry (Task 5),
|
|
965
|
+
`useGridTracks()`/`resolve`/`toColumnsCss` (Task 3), placement utils (Task 4), visual builder
|
|
966
|
+
(Task 7) — all spec sections have a task. Out-of-scope items (layout rework, row presets,
|
|
967
|
+
save-to-registry, Bento preset) are called out in Global Constraints and not implemented.
|
|
968
|
+
- **`toColumnsCss` example correction:** the spec's inline example string
|
|
969
|
+
(`'minmax(min-content, [feature-start] 2fr [feature-end aside-start] 1fr [aside-end])'`) is
|
|
970
|
+
not valid `grid-template-columns` syntax (a single `minmax()` can't wrap multiple tracks).
|
|
971
|
+
This plan implements the spec's own prose description instead — "each segment's
|
|
972
|
+
lineStart/lineEnd become bracketed line names positioned around its fr value, per standard
|
|
973
|
+
named-grid-line syntax" — which Task 3's tests encode as one `minmax(floor, Nfr)` per segment
|
|
974
|
+
with brackets between them. Flagged here rather than silently deviating.
|
|
975
|
+
- **Type consistency:** `TrackSegment`/`TrackConfig`/`GridPreset` (Task 1) are used identically
|
|
976
|
+
in Task 3's function signatures, Task 5's `app.config.ts`, and Task 7's builder page — no
|
|
977
|
+
renames across tasks.
|