kmcom-nuxt-layers 2.7.5 → 4.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 +30 -1
- package/layers/core/app/assets/css/layout.css +0 -7
- 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/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 +2 -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 +67 -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 +52 -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 +9 -0
- 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 -1
- 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,1019 @@
|
|
|
1
|
+
# Vue Element System 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 polymorphic `Element` component to `layers/structure/layout` giving Vue templates a small, typed vocabulary (layout/grid/spacing/sizing/surface/interaction) instead of utility-class strings, reusing the layer's existing fluid-token and grid-placement engines.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Six small composables, each resolving one property group to a `Partial<CSSProperties>`, merged inside `Element.vue`'s single computed `style`. Grid item-placement (`colStart`/`colSpan`/`rowStart`/`rowSpan`) delegates directly to the existing `buildGridPlacementStyle` from `gridPlacementStyle.ts` — no new placement math. Spacing (`p`/`px`/`py`/`gap`/`align`/`justify`) delegates to core's `useLayoutAttrs`, extended with `m`/`mx`/`my`. Surface/Sizing/Interaction are new, minimal, raw-CSS-passthrough composables.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Vue 3 `<script setup>`, TypeScript, Vitest, Nuxt 4 layers (`#layers/*` aliases).
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-09-03-vue-element-system-design.md`
|
|
12
|
+
|
|
13
|
+
## Global Constraints
|
|
14
|
+
|
|
15
|
+
- No changes to `LayoutGridItem`, `gridPlacementStyle.ts` placement logic, or any fluid-rebuild-spec territory — read-only reuse.
|
|
16
|
+
- No semantic colour/token system — `bg`/`color`/`border`/`radius`/`shadow` are raw CSS passthrough (`CSSProperties[...]` typed), not token enums.
|
|
17
|
+
- Grid item-placement prop names match `LayoutGridItem`'s existing vocabulary: `colStart`/`colSpan`/`rowStart`/`rowSpan` — not the source doc's `span`/`start`/`end`.
|
|
18
|
+
- No semicolons, single quotes, ES5 trailing commas, max 100 char lines (`.claude/rules/typescript.md`).
|
|
19
|
+
- `<script setup lang="ts">`, script → template → style block order (`.claude/rules/vue-components.md`).
|
|
20
|
+
- `Element` is root-level, unprefixed component name, matching `HStack`/`VStack`/`ZStack`/`Spacer`.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## File Structure
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
layers/core/app/composables/useLayoutAttrs.ts (modify: add m/mx/my)
|
|
28
|
+
layers/core/app/composables/useLayoutAttrs.test.ts (modify: add margin tests, only if file exists — else create)
|
|
29
|
+
|
|
30
|
+
layers/structure/layout/app/composables/
|
|
31
|
+
├── useElementLayout.ts (new: display mode resolver)
|
|
32
|
+
├── useElementLayout.test.ts (new)
|
|
33
|
+
├── useElementGrid.ts (new: container-axis template + item-axis delegate)
|
|
34
|
+
├── useElementGrid.test.ts (new)
|
|
35
|
+
├── useElementSizing.ts (new: w/h/min/max/aspect passthrough)
|
|
36
|
+
├── useElementSizing.test.ts (new)
|
|
37
|
+
├── useElementSurface.ts (new: bg/color/border/radius/shadow passthrough)
|
|
38
|
+
├── useElementSurface.test.ts (new)
|
|
39
|
+
├── useElementInteraction.ts (new: cursor/select/pointer passthrough)
|
|
40
|
+
└── useElementInteraction.test.ts (new)
|
|
41
|
+
|
|
42
|
+
layers/structure/layout/app/types/element.ts (new: ElementProps union)
|
|
43
|
+
|
|
44
|
+
layers/structure/layout/app/components/Element.vue (new)
|
|
45
|
+
|
|
46
|
+
apps/playground/app/pages/element.vue (new: manual verification page)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Each composable owns exactly one property group, mirrors the existing `gridPlacementStyle.ts` / `useLayoutAttrs.ts` pattern (plain exported function, `Partial<CSSProperties>` return), and gets its own test file — matching the existing `gridPlacementStyle.test.ts` convention (`describe`/`it`, vitest, no mocking needed since these are pure functions).
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
### Task 1: Extend `useLayoutAttrs` with margin support
|
|
54
|
+
|
|
55
|
+
**Files:**
|
|
56
|
+
|
|
57
|
+
- Modify: `layers/core/app/composables/useLayoutAttrs.ts`
|
|
58
|
+
- Test: `layers/core/app/composables/useLayoutAttrs.test.ts` (create if it doesn't exist)
|
|
59
|
+
|
|
60
|
+
**Interfaces:**
|
|
61
|
+
|
|
62
|
+
- Consumes: nothing new — extends existing `LayoutAttrsInput`, `Spacing` type from `../types/tokens`, `ResponsiveValue` from `../types/responsive`.
|
|
63
|
+
- Produces: `LayoutAttrsInput` gains `m?`, `mx?`, `my?` fields (same shape as existing `p`/`px`/`py`). `useLayoutAttrs()`'s returned `style` now also sets `margin`/`marginInline`/`marginBlock` when those props are passed.
|
|
64
|
+
|
|
65
|
+
- [ ] **Step 1: Check for an existing test file**
|
|
66
|
+
|
|
67
|
+
Run: `ls layers/core/app/composables/useLayoutAttrs.test.ts 2>/dev/null && echo EXISTS || echo MISSING`
|
|
68
|
+
|
|
69
|
+
If `EXISTS`, read the file first and add new `describe` blocks to it in Step 2 rather than replacing it. If `MISSING`, Step 2 creates the whole file.
|
|
70
|
+
|
|
71
|
+
- [ ] **Step 2: Write the failing tests**
|
|
72
|
+
|
|
73
|
+
Create/extend `layers/core/app/composables/useLayoutAttrs.test.ts`:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { describe, expect, it } from 'vitest'
|
|
77
|
+
|
|
78
|
+
import { useLayoutAttrs } from './useLayoutAttrs'
|
|
79
|
+
|
|
80
|
+
describe('useLayoutAttrs margin support', () => {
|
|
81
|
+
it('sets margin from a static token', () => {
|
|
82
|
+
const { style } = useLayoutAttrs(() => ({ m: 'md' }))
|
|
83
|
+
expect(style.value.margin).toBe('var(--fluid-space-md)')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('sets marginInline from px-equivalent mx', () => {
|
|
87
|
+
const { style } = useLayoutAttrs(() => ({ mx: 'lg' }))
|
|
88
|
+
expect(style.value.marginInline).toBe('var(--fluid-space-lg)')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('sets marginBlock from my', () => {
|
|
92
|
+
const { style } = useLayoutAttrs(() => ({ my: 'sm' }))
|
|
93
|
+
expect(style.value.marginBlock).toBe('var(--fluid-space-sm)')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('resolves none to 0px for margin', () => {
|
|
97
|
+
const { style } = useLayoutAttrs(() => ({ m: 'none' }))
|
|
98
|
+
expect(style.value.margin).toBe('0px')
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('omits margin keys entirely when not passed', () => {
|
|
102
|
+
const { style } = useLayoutAttrs(() => ({ gap: 'md' }))
|
|
103
|
+
expect(style.value.margin).toBeUndefined()
|
|
104
|
+
expect(style.value.marginInline).toBeUndefined()
|
|
105
|
+
expect(style.value.marginBlock).toBeUndefined()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('does not regress existing gap/align/justify/padding behavior', () => {
|
|
109
|
+
const { style } = useLayoutAttrs(() => ({
|
|
110
|
+
gap: 'md',
|
|
111
|
+
align: 'center',
|
|
112
|
+
justify: 'between',
|
|
113
|
+
p: 'lg',
|
|
114
|
+
}))
|
|
115
|
+
expect(style.value).toEqual({
|
|
116
|
+
gap: 'var(--fluid-space-md)',
|
|
117
|
+
padding: 'var(--fluid-space-lg)',
|
|
118
|
+
alignItems: 'center',
|
|
119
|
+
justifyContent: 'space-between',
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- [ ] **Step 3: Run tests to verify they fail**
|
|
126
|
+
|
|
127
|
+
Run: `cd layers/core && npx vitest run app/composables/useLayoutAttrs.test.ts`
|
|
128
|
+
Expected: FAIL — `style.value.margin` is `undefined` for the `m: 'md'` case (property not implemented yet).
|
|
129
|
+
|
|
130
|
+
- [ ] **Step 4: Implement margin support**
|
|
131
|
+
|
|
132
|
+
Edit `layers/core/app/composables/useLayoutAttrs.ts`:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
export type LayoutAttrsInput = {
|
|
136
|
+
gap?: ResponsiveValue<Spacing> | Spacing | undefined
|
|
137
|
+
align?: ResponsiveValue<Align> | Align | undefined
|
|
138
|
+
justify?: ResponsiveValue<Justify> | Justify | undefined
|
|
139
|
+
p?: ResponsiveValue<Spacing> | Spacing | undefined
|
|
140
|
+
px?: ResponsiveValue<Spacing> | Spacing | undefined
|
|
141
|
+
py?: ResponsiveValue<Spacing> | Spacing | undefined
|
|
142
|
+
m?: ResponsiveValue<Spacing> | Spacing | undefined
|
|
143
|
+
mx?: ResponsiveValue<Spacing> | Spacing | undefined
|
|
144
|
+
my?: ResponsiveValue<Spacing> | Spacing | undefined
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Update the `applySpacing` call signature to accept the margin CSS props, then extend `useLayoutAttrs`'s `computed`:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
function applySpacing(
|
|
152
|
+
result: CSSProperties,
|
|
153
|
+
cssProp:
|
|
154
|
+
| 'gap'
|
|
155
|
+
| 'padding'
|
|
156
|
+
| 'paddingInline'
|
|
157
|
+
| 'paddingBlock'
|
|
158
|
+
| 'margin'
|
|
159
|
+
| 'marginInline'
|
|
160
|
+
| 'marginBlock',
|
|
161
|
+
value: ResponsiveValue<Spacing> | Spacing | undefined
|
|
162
|
+
): void {
|
|
163
|
+
const resolved = resolveDefault(value)
|
|
164
|
+
if (resolved) result[cssProp] = spacingVar(resolved)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function useLayoutAttrs(input: MaybeRefOrGetter<LayoutAttrsInput>): {
|
|
168
|
+
style: ComputedRef<CSSProperties>
|
|
169
|
+
} {
|
|
170
|
+
const style = computed((): CSSProperties => {
|
|
171
|
+
const { gap, align, justify, p, px, py, m, mx, my } = toValue(input)
|
|
172
|
+
const result: CSSProperties = {}
|
|
173
|
+
|
|
174
|
+
applySpacing(result, 'gap', gap)
|
|
175
|
+
applySpacing(result, 'padding', p)
|
|
176
|
+
applySpacing(result, 'paddingInline', px)
|
|
177
|
+
applySpacing(result, 'paddingBlock', py)
|
|
178
|
+
applySpacing(result, 'margin', m)
|
|
179
|
+
applySpacing(result, 'marginInline', mx)
|
|
180
|
+
applySpacing(result, 'marginBlock', my)
|
|
181
|
+
|
|
182
|
+
const resolvedAlign = resolveDefault(align)
|
|
183
|
+
if (resolvedAlign) result.alignItems = ALIGN_MAP[resolvedAlign]
|
|
184
|
+
|
|
185
|
+
const resolvedJustify = resolveDefault(justify)
|
|
186
|
+
if (resolvedJustify) result.justifyContent = JUSTIFY_MAP[resolvedJustify]
|
|
187
|
+
|
|
188
|
+
return result
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
return { style }
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
- [ ] **Step 5: Run tests to verify they pass**
|
|
196
|
+
|
|
197
|
+
Run: `cd layers/core && npx vitest run app/composables/useLayoutAttrs.test.ts`
|
|
198
|
+
Expected: PASS, all 6 new tests green.
|
|
199
|
+
|
|
200
|
+
- [ ] **Step 6: Run the full core test suite to check for regressions**
|
|
201
|
+
|
|
202
|
+
Run: `cd layers/core && npx vitest run`
|
|
203
|
+
Expected: PASS — no existing test touching `useLayoutAttrs` breaks.
|
|
204
|
+
|
|
205
|
+
- [ ] **Step 7: Commit**
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
git add layers/core/app/composables/useLayoutAttrs.ts layers/core/app/composables/useLayoutAttrs.test.ts
|
|
209
|
+
git commit -m "feat(core): add margin support to useLayoutAttrs
|
|
210
|
+
|
|
211
|
+
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
212
|
+
Claude-Session: https://claude.ai/code/session_017vMSCbNb6g9hyb9sBUsAQN"
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
### Task 2: `useElementLayout` — display mode composable
|
|
218
|
+
|
|
219
|
+
**Files:**
|
|
220
|
+
|
|
221
|
+
- Create: `layers/structure/layout/app/composables/useElementLayout.ts`
|
|
222
|
+
- Test: `layers/structure/layout/app/composables/useElementLayout.test.ts`
|
|
223
|
+
|
|
224
|
+
**Interfaces:**
|
|
225
|
+
|
|
226
|
+
- Consumes: nothing (pure function, no imports beyond Vue's `CSSProperties` type).
|
|
227
|
+
- Produces: `ElementLayoutProps` type (`{ block?: boolean; flex?: boolean; grid?: boolean; hidden?: boolean }`) and `useElementLayout(props: ElementLayoutProps): Partial<CSSProperties>`. Both are imported by Task 6 (`Element.vue`).
|
|
228
|
+
|
|
229
|
+
- [ ] **Step 1: Write the failing tests**
|
|
230
|
+
|
|
231
|
+
Create `layers/structure/layout/app/composables/useElementLayout.test.ts`:
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
import { describe, expect, it } from 'vitest'
|
|
235
|
+
|
|
236
|
+
import { useElementLayout } from './useElementLayout'
|
|
237
|
+
|
|
238
|
+
describe('useElementLayout', () => {
|
|
239
|
+
it('returns no display when no mode prop is set', () => {
|
|
240
|
+
expect(useElementLayout({})).toEqual({ display: undefined })
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('resolves block to display: block', () => {
|
|
244
|
+
expect(useElementLayout({ block: true })).toEqual({ display: 'block' })
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
it('resolves flex to display: flex', () => {
|
|
248
|
+
expect(useElementLayout({ flex: true })).toEqual({ display: 'flex' })
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it('resolves grid to display: grid', () => {
|
|
252
|
+
expect(useElementLayout({ grid: true })).toEqual({ display: 'grid' })
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
it('resolves hidden to display: none', () => {
|
|
256
|
+
expect(useElementLayout({ hidden: true })).toEqual({ display: 'none' })
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('prioritizes hidden over grid, flex, and block when multiple are set', () => {
|
|
260
|
+
expect(useElementLayout({ hidden: true, grid: true, flex: true, block: true })).toEqual({
|
|
261
|
+
display: 'none',
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('prioritizes grid over flex and block', () => {
|
|
266
|
+
expect(useElementLayout({ grid: true, flex: true, block: true })).toEqual({
|
|
267
|
+
display: 'grid',
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it('prioritizes flex over block', () => {
|
|
272
|
+
expect(useElementLayout({ flex: true, block: true })).toEqual({ display: 'flex' })
|
|
273
|
+
})
|
|
274
|
+
})
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
- [ ] **Step 2: Run tests to verify they fail**
|
|
278
|
+
|
|
279
|
+
Run: `cd layers/structure/layout && npx vitest run app/composables/useElementLayout.test.ts`
|
|
280
|
+
Expected: FAIL — module `./useElementLayout` not found.
|
|
281
|
+
|
|
282
|
+
- [ ] **Step 3: Write the implementation**
|
|
283
|
+
|
|
284
|
+
Create `layers/structure/layout/app/composables/useElementLayout.ts`:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
import type { CSSProperties } from 'vue'
|
|
288
|
+
|
|
289
|
+
export interface ElementLayoutProps {
|
|
290
|
+
block?: boolean
|
|
291
|
+
flex?: boolean
|
|
292
|
+
grid?: boolean
|
|
293
|
+
hidden?: boolean
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
type Mode = 'hidden' | 'grid' | 'flex' | 'block'
|
|
297
|
+
|
|
298
|
+
const DISPLAY_FOR: Record<Mode, CSSProperties['display']> = {
|
|
299
|
+
hidden: 'none',
|
|
300
|
+
grid: 'grid',
|
|
301
|
+
flex: 'flex',
|
|
302
|
+
block: 'block',
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export function useElementLayout(props: ElementLayoutProps): Partial<CSSProperties> {
|
|
306
|
+
const mode = (['hidden', 'grid', 'flex', 'block'] as const).find((m) => props[m])
|
|
307
|
+
return {
|
|
308
|
+
display: mode && DISPLAY_FOR[mode],
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
- [ ] **Step 4: Run tests to verify they pass**
|
|
314
|
+
|
|
315
|
+
Run: `cd layers/structure/layout && npx vitest run app/composables/useElementLayout.test.ts`
|
|
316
|
+
Expected: PASS, all 8 tests green.
|
|
317
|
+
|
|
318
|
+
- [ ] **Step 5: Commit**
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
git add layers/structure/layout/app/composables/useElementLayout.ts layers/structure/layout/app/composables/useElementLayout.test.ts
|
|
322
|
+
git commit -m "feat(layout): add useElementLayout display-mode composable
|
|
323
|
+
|
|
324
|
+
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
325
|
+
Claude-Session: https://claude.ai/code/session_017vMSCbNb6g9hyb9sBUsAQN"
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
### Task 3: `useElementSizing` — dimension composable
|
|
331
|
+
|
|
332
|
+
**Files:**
|
|
333
|
+
|
|
334
|
+
- Create: `layers/structure/layout/app/composables/useElementSizing.ts`
|
|
335
|
+
- Test: `layers/structure/layout/app/composables/useElementSizing.test.ts`
|
|
336
|
+
|
|
337
|
+
**Interfaces:**
|
|
338
|
+
|
|
339
|
+
- Consumes: nothing.
|
|
340
|
+
- Produces: `ElementSizingProps` type and `useElementSizing(props: ElementSizingProps): Partial<CSSProperties>`, imported by Task 6.
|
|
341
|
+
|
|
342
|
+
- [ ] **Step 1: Write the failing tests**
|
|
343
|
+
|
|
344
|
+
Create `layers/structure/layout/app/composables/useElementSizing.test.ts`:
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
import { describe, expect, it } from 'vitest'
|
|
348
|
+
|
|
349
|
+
import { useElementSizing } from './useElementSizing'
|
|
350
|
+
|
|
351
|
+
describe('useElementSizing', () => {
|
|
352
|
+
it('returns all-undefined style when no props are set', () => {
|
|
353
|
+
expect(useElementSizing({})).toEqual({
|
|
354
|
+
width: undefined,
|
|
355
|
+
height: undefined,
|
|
356
|
+
minWidth: undefined,
|
|
357
|
+
maxWidth: undefined,
|
|
358
|
+
minHeight: undefined,
|
|
359
|
+
maxHeight: undefined,
|
|
360
|
+
aspectRatio: undefined,
|
|
361
|
+
})
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
it('maps every prop to its CSS property 1:1', () => {
|
|
365
|
+
expect(
|
|
366
|
+
useElementSizing({
|
|
367
|
+
w: '80px',
|
|
368
|
+
h: '40px',
|
|
369
|
+
minW: '10rem',
|
|
370
|
+
maxW: '20rem',
|
|
371
|
+
minH: '5rem',
|
|
372
|
+
maxH: '15rem',
|
|
373
|
+
aspect: '16 / 9',
|
|
374
|
+
})
|
|
375
|
+
).toEqual({
|
|
376
|
+
width: '80px',
|
|
377
|
+
height: '40px',
|
|
378
|
+
minWidth: '10rem',
|
|
379
|
+
maxWidth: '20rem',
|
|
380
|
+
minHeight: '5rem',
|
|
381
|
+
maxHeight: '15rem',
|
|
382
|
+
aspectRatio: '16 / 9',
|
|
383
|
+
})
|
|
384
|
+
})
|
|
385
|
+
})
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
- [ ] **Step 2: Run tests to verify they fail**
|
|
389
|
+
|
|
390
|
+
Run: `cd layers/structure/layout && npx vitest run app/composables/useElementSizing.test.ts`
|
|
391
|
+
Expected: FAIL — module not found.
|
|
392
|
+
|
|
393
|
+
- [ ] **Step 3: Write the implementation**
|
|
394
|
+
|
|
395
|
+
Create `layers/structure/layout/app/composables/useElementSizing.ts`:
|
|
396
|
+
|
|
397
|
+
```ts
|
|
398
|
+
import type { CSSProperties } from 'vue'
|
|
399
|
+
|
|
400
|
+
export interface ElementSizingProps {
|
|
401
|
+
w?: CSSProperties['width']
|
|
402
|
+
h?: CSSProperties['height']
|
|
403
|
+
minW?: CSSProperties['minWidth']
|
|
404
|
+
maxW?: CSSProperties['maxWidth']
|
|
405
|
+
minH?: CSSProperties['minHeight']
|
|
406
|
+
maxH?: CSSProperties['maxHeight']
|
|
407
|
+
aspect?: CSSProperties['aspectRatio']
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export function useElementSizing(props: ElementSizingProps): Partial<CSSProperties> {
|
|
411
|
+
return {
|
|
412
|
+
width: props.w,
|
|
413
|
+
height: props.h,
|
|
414
|
+
minWidth: props.minW,
|
|
415
|
+
maxWidth: props.maxW,
|
|
416
|
+
minHeight: props.minH,
|
|
417
|
+
maxHeight: props.maxH,
|
|
418
|
+
aspectRatio: props.aspect,
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
- [ ] **Step 4: Run tests to verify they pass**
|
|
424
|
+
|
|
425
|
+
Run: `cd layers/structure/layout && npx vitest run app/composables/useElementSizing.test.ts`
|
|
426
|
+
Expected: PASS, both tests green.
|
|
427
|
+
|
|
428
|
+
- [ ] **Step 5: Commit**
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
git add layers/structure/layout/app/composables/useElementSizing.ts layers/structure/layout/app/composables/useElementSizing.test.ts
|
|
432
|
+
git commit -m "feat(layout): add useElementSizing composable
|
|
433
|
+
|
|
434
|
+
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
435
|
+
Claude-Session: https://claude.ai/code/session_017vMSCbNb6g9hyb9sBUsAQN"
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
### Task 4: `useElementSurface` and `useElementInteraction` — surface & interaction composables
|
|
441
|
+
|
|
442
|
+
**Files:**
|
|
443
|
+
|
|
444
|
+
- Create: `layers/structure/layout/app/composables/useElementSurface.ts`
|
|
445
|
+
- Create: `layers/structure/layout/app/composables/useElementInteraction.ts`
|
|
446
|
+
- Test: `layers/structure/layout/app/composables/useElementSurface.test.ts`
|
|
447
|
+
- Test: `layers/structure/layout/app/composables/useElementInteraction.test.ts`
|
|
448
|
+
|
|
449
|
+
**Interfaces:**
|
|
450
|
+
|
|
451
|
+
- Consumes: nothing.
|
|
452
|
+
- Produces: `ElementSurfaceProps`/`useElementSurface()` and `ElementInteractionProps`/`useElementInteraction()`, both imported by Task 6.
|
|
453
|
+
|
|
454
|
+
- [ ] **Step 1: Write the failing surface test**
|
|
455
|
+
|
|
456
|
+
Create `layers/structure/layout/app/composables/useElementSurface.test.ts`:
|
|
457
|
+
|
|
458
|
+
```ts
|
|
459
|
+
import { describe, expect, it } from 'vitest'
|
|
460
|
+
|
|
461
|
+
import { useElementSurface } from './useElementSurface'
|
|
462
|
+
|
|
463
|
+
describe('useElementSurface', () => {
|
|
464
|
+
it('returns all-undefined style when no props are set', () => {
|
|
465
|
+
expect(useElementSurface({})).toEqual({
|
|
466
|
+
background: undefined,
|
|
467
|
+
color: undefined,
|
|
468
|
+
border: undefined,
|
|
469
|
+
borderRadius: undefined,
|
|
470
|
+
boxShadow: undefined,
|
|
471
|
+
})
|
|
472
|
+
})
|
|
473
|
+
|
|
474
|
+
it('maps every prop to its CSS property 1:1', () => {
|
|
475
|
+
expect(
|
|
476
|
+
useElementSurface({
|
|
477
|
+
bg: 'var(--ui-bg-elevated)',
|
|
478
|
+
color: 'var(--ui-text)',
|
|
479
|
+
border: '1px solid var(--ui-border)',
|
|
480
|
+
radius: '0.5rem',
|
|
481
|
+
shadow: '0 4px 12px rgb(0 0 0 / 0.15)',
|
|
482
|
+
})
|
|
483
|
+
).toEqual({
|
|
484
|
+
background: 'var(--ui-bg-elevated)',
|
|
485
|
+
color: 'var(--ui-text)',
|
|
486
|
+
border: '1px solid var(--ui-border)',
|
|
487
|
+
borderRadius: '0.5rem',
|
|
488
|
+
boxShadow: '0 4px 12px rgb(0 0 0 / 0.15)',
|
|
489
|
+
})
|
|
490
|
+
})
|
|
491
|
+
})
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
- [ ] **Step 2: Write the failing interaction test**
|
|
495
|
+
|
|
496
|
+
Create `layers/structure/layout/app/composables/useElementInteraction.test.ts`:
|
|
497
|
+
|
|
498
|
+
```ts
|
|
499
|
+
import { describe, expect, it } from 'vitest'
|
|
500
|
+
|
|
501
|
+
import { useElementInteraction } from './useElementInteraction'
|
|
502
|
+
|
|
503
|
+
describe('useElementInteraction', () => {
|
|
504
|
+
it('returns all-undefined style when no props are set', () => {
|
|
505
|
+
expect(useElementInteraction({})).toEqual({
|
|
506
|
+
cursor: undefined,
|
|
507
|
+
userSelect: undefined,
|
|
508
|
+
pointerEvents: undefined,
|
|
509
|
+
})
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
it('maps every prop to its CSS property 1:1', () => {
|
|
513
|
+
expect(
|
|
514
|
+
useElementInteraction({
|
|
515
|
+
cursor: 'pointer',
|
|
516
|
+
select: 'none',
|
|
517
|
+
pointer: 'none',
|
|
518
|
+
})
|
|
519
|
+
).toEqual({
|
|
520
|
+
cursor: 'pointer',
|
|
521
|
+
userSelect: 'none',
|
|
522
|
+
pointerEvents: 'none',
|
|
523
|
+
})
|
|
524
|
+
})
|
|
525
|
+
})
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
- [ ] **Step 3: Run both tests to verify they fail**
|
|
529
|
+
|
|
530
|
+
Run: `cd layers/structure/layout && npx vitest run app/composables/useElementSurface.test.ts app/composables/useElementInteraction.test.ts`
|
|
531
|
+
Expected: FAIL — modules not found.
|
|
532
|
+
|
|
533
|
+
- [ ] **Step 4: Write the surface implementation**
|
|
534
|
+
|
|
535
|
+
Create `layers/structure/layout/app/composables/useElementSurface.ts`:
|
|
536
|
+
|
|
537
|
+
```ts
|
|
538
|
+
import type { CSSProperties } from 'vue'
|
|
539
|
+
|
|
540
|
+
export interface ElementSurfaceProps {
|
|
541
|
+
bg?: CSSProperties['background']
|
|
542
|
+
color?: CSSProperties['color']
|
|
543
|
+
border?: CSSProperties['border']
|
|
544
|
+
radius?: CSSProperties['borderRadius']
|
|
545
|
+
shadow?: CSSProperties['boxShadow']
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
export function useElementSurface(props: ElementSurfaceProps): Partial<CSSProperties> {
|
|
549
|
+
return {
|
|
550
|
+
background: props.bg,
|
|
551
|
+
color: props.color,
|
|
552
|
+
border: props.border,
|
|
553
|
+
borderRadius: props.radius,
|
|
554
|
+
boxShadow: props.shadow,
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
- [ ] **Step 5: Write the interaction implementation**
|
|
560
|
+
|
|
561
|
+
Create `layers/structure/layout/app/composables/useElementInteraction.ts`:
|
|
562
|
+
|
|
563
|
+
```ts
|
|
564
|
+
import type { CSSProperties } from 'vue'
|
|
565
|
+
|
|
566
|
+
export interface ElementInteractionProps {
|
|
567
|
+
cursor?: CSSProperties['cursor']
|
|
568
|
+
select?: CSSProperties['userSelect']
|
|
569
|
+
pointer?: CSSProperties['pointerEvents']
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
export function useElementInteraction(props: ElementInteractionProps): Partial<CSSProperties> {
|
|
573
|
+
return {
|
|
574
|
+
cursor: props.cursor,
|
|
575
|
+
userSelect: props.select,
|
|
576
|
+
pointerEvents: props.pointer,
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
- [ ] **Step 6: Run both tests to verify they pass**
|
|
582
|
+
|
|
583
|
+
Run: `cd layers/structure/layout && npx vitest run app/composables/useElementSurface.test.ts app/composables/useElementInteraction.test.ts`
|
|
584
|
+
Expected: PASS, all 4 tests green.
|
|
585
|
+
|
|
586
|
+
- [ ] **Step 7: Commit**
|
|
587
|
+
|
|
588
|
+
```bash
|
|
589
|
+
git add layers/structure/layout/app/composables/useElementSurface.ts layers/structure/layout/app/composables/useElementSurface.test.ts layers/structure/layout/app/composables/useElementInteraction.ts layers/structure/layout/app/composables/useElementInteraction.test.ts
|
|
590
|
+
git commit -m "feat(layout): add useElementSurface and useElementInteraction composables
|
|
591
|
+
|
|
592
|
+
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
593
|
+
Claude-Session: https://claude.ai/code/session_017vMSCbNb6g9hyb9sBUsAQN"
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
---
|
|
597
|
+
|
|
598
|
+
### Task 5: `useElementGrid` — container-axis template + item-axis delegate
|
|
599
|
+
|
|
600
|
+
**Files:**
|
|
601
|
+
|
|
602
|
+
- Create: `layers/structure/layout/app/composables/useElementGrid.ts`
|
|
603
|
+
- Test: `layers/structure/layout/app/composables/useElementGrid.test.ts`
|
|
604
|
+
|
|
605
|
+
**Interfaces:**
|
|
606
|
+
|
|
607
|
+
- Consumes: `buildGridPlacementStyle`, `GridPlacementInput` from `../utils/gridPlacementStyle` (existing, unmodified — see `layers/structure/layout/app/utils/gridPlacementStyle.ts:132`).
|
|
608
|
+
- Produces: `ElementGridProps` type and `useElementGrid(props: ElementGridProps): Partial<CSSProperties>`, imported by Task 6. `ElementGridProps` shape: `{ grid?: boolean; cols?: number | string; rows?: number | string; colStart?: number | ResponsiveValue<number>; colSpan?: number | 'full' | ResponsiveValue<number>; rowStart?: number | ResponsiveValue<number>; rowSpan?: number | ResponsiveValue<number> }`.
|
|
609
|
+
|
|
610
|
+
- [ ] **Step 1: Write the failing tests**
|
|
611
|
+
|
|
612
|
+
Create `layers/structure/layout/app/composables/useElementGrid.test.ts`:
|
|
613
|
+
|
|
614
|
+
```ts
|
|
615
|
+
import { describe, expect, it } from 'vitest'
|
|
616
|
+
|
|
617
|
+
import { useElementGrid } from './useElementGrid'
|
|
618
|
+
|
|
619
|
+
describe('useElementGrid container axis', () => {
|
|
620
|
+
it('returns no grid-template properties when grid is not set', () => {
|
|
621
|
+
const result = useElementGrid({ cols: 12 })
|
|
622
|
+
expect(result.gridTemplateColumns).toBeUndefined()
|
|
623
|
+
expect(result.gridTemplateRows).toBeUndefined()
|
|
624
|
+
})
|
|
625
|
+
|
|
626
|
+
it('converts a numeric cols to a repeat() track when grid is set', () => {
|
|
627
|
+
const result = useElementGrid({ grid: true, cols: 12 })
|
|
628
|
+
expect(result.gridTemplateColumns).toBe('repeat(12, 1fr)')
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
it('converts a numeric rows to a repeat() track when grid is set', () => {
|
|
632
|
+
const result = useElementGrid({ grid: true, rows: 4 })
|
|
633
|
+
expect(result.gridTemplateRows).toBe('repeat(4, 1fr)')
|
|
634
|
+
})
|
|
635
|
+
|
|
636
|
+
it('passes a string cols track through unchanged', () => {
|
|
637
|
+
const result = useElementGrid({ grid: true, cols: '1fr 2fr 1fr' })
|
|
638
|
+
expect(result.gridTemplateColumns).toBe('1fr 2fr 1fr')
|
|
639
|
+
})
|
|
640
|
+
|
|
641
|
+
it('sets display: grid when grid is true', () => {
|
|
642
|
+
const result = useElementGrid({ grid: true, cols: 12 })
|
|
643
|
+
expect(result.display).toBe('grid')
|
|
644
|
+
})
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
describe('useElementGrid item axis (delegates to buildGridPlacementStyle)', () => {
|
|
648
|
+
it('sets gridColumn span from colSpan alone', () => {
|
|
649
|
+
const result = useElementGrid({ colSpan: 8 })
|
|
650
|
+
expect(result.gridColumn).toBe('1 / -1')
|
|
651
|
+
})
|
|
652
|
+
|
|
653
|
+
it('sets --_ce custom property when colSpan is not full', () => {
|
|
654
|
+
const result = useElementGrid({ colStart: 3, colSpan: 6 })
|
|
655
|
+
expect(result['--_cs']).toBe('3')
|
|
656
|
+
expect(result['--_ce']).toBe('6')
|
|
657
|
+
})
|
|
658
|
+
|
|
659
|
+
it('produces identical output to buildGridPlacementStyle for the same input', async () => {
|
|
660
|
+
const { buildGridPlacementStyle } = await import('../utils/gridPlacementStyle')
|
|
661
|
+
const direct = buildGridPlacementStyle({ colStart: 2, colSpan: 4, rowSpan: 2 })
|
|
662
|
+
const viaElement = useElementGrid({ colStart: 2, colSpan: 4, rowSpan: 2 })
|
|
663
|
+
expect(viaElement).toMatchObject(direct)
|
|
664
|
+
})
|
|
665
|
+
})
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
- [ ] **Step 2: Run tests to verify they fail**
|
|
669
|
+
|
|
670
|
+
Run: `cd layers/structure/layout && npx vitest run app/composables/useElementGrid.test.ts`
|
|
671
|
+
Expected: FAIL — module not found.
|
|
672
|
+
|
|
673
|
+
- [ ] **Step 3: Write the implementation**
|
|
674
|
+
|
|
675
|
+
Create `layers/structure/layout/app/composables/useElementGrid.ts`:
|
|
676
|
+
|
|
677
|
+
```ts
|
|
678
|
+
import type { CSSProperties } from 'vue'
|
|
679
|
+
|
|
680
|
+
import type { ResponsiveValue } from '../types/layouts'
|
|
681
|
+
import { buildGridPlacementStyle } from '../utils/gridPlacementStyle'
|
|
682
|
+
|
|
683
|
+
export interface ElementGridProps {
|
|
684
|
+
grid?: boolean
|
|
685
|
+
cols?: number | string
|
|
686
|
+
rows?: number | string
|
|
687
|
+
colStart?: number | ResponsiveValue<number>
|
|
688
|
+
colSpan?: number | 'full' | ResponsiveValue<number>
|
|
689
|
+
rowStart?: number | ResponsiveValue<number>
|
|
690
|
+
rowSpan?: number | ResponsiveValue<number>
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function track(value: number | string | undefined): string | undefined {
|
|
694
|
+
return typeof value === 'number' ? `repeat(${value}, 1fr)` : value
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
export function useElementGrid(props: ElementGridProps): Partial<CSSProperties> {
|
|
698
|
+
const placement = buildGridPlacementStyle({
|
|
699
|
+
colStart: props.colStart,
|
|
700
|
+
colSpan: props.colSpan,
|
|
701
|
+
rowStart: props.rowStart,
|
|
702
|
+
rowSpan: props.rowSpan,
|
|
703
|
+
})
|
|
704
|
+
|
|
705
|
+
return {
|
|
706
|
+
...placement,
|
|
707
|
+
...(props.grid && {
|
|
708
|
+
display: 'grid',
|
|
709
|
+
gridTemplateColumns: track(props.cols),
|
|
710
|
+
gridTemplateRows: track(props.rows),
|
|
711
|
+
}),
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
- [ ] **Step 4: Confirm the `ResponsiveValue` import path**
|
|
717
|
+
|
|
718
|
+
Run: `grep -n "export type ResponsiveValue" layers/structure/layout/app/types/layouts.ts`
|
|
719
|
+
Expected: one match — confirms `../types/layouts` is the correct import path for the layer's own `ResponsiveValue` (distinct from `core`'s — see spec §13). If the grep finds no match, locate the actual export and adjust the import in Step 3 before proceeding.
|
|
720
|
+
|
|
721
|
+
- [ ] **Step 5: Run tests to verify they pass**
|
|
722
|
+
|
|
723
|
+
Run: `cd layers/structure/layout && npx vitest run app/composables/useElementGrid.test.ts`
|
|
724
|
+
Expected: PASS, all 8 tests green.
|
|
725
|
+
|
|
726
|
+
- [ ] **Step 6: Run the full layout test suite to check for regressions**
|
|
727
|
+
|
|
728
|
+
Run: `cd layers/structure/layout && npx vitest run`
|
|
729
|
+
Expected: PASS — `gridPlacementStyle.test.ts` and all others still green; confirms `buildGridPlacementStyle` itself was not touched.
|
|
730
|
+
|
|
731
|
+
- [ ] **Step 7: Commit**
|
|
732
|
+
|
|
733
|
+
```bash
|
|
734
|
+
git add layers/structure/layout/app/composables/useElementGrid.ts layers/structure/layout/app/composables/useElementGrid.test.ts
|
|
735
|
+
git commit -m "feat(layout): add useElementGrid composable, reusing buildGridPlacementStyle
|
|
736
|
+
|
|
737
|
+
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
738
|
+
Claude-Session: https://claude.ai/code/session_017vMSCbNb6g9hyb9sBUsAQN"
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
---
|
|
742
|
+
|
|
743
|
+
### Task 6: `Element.vue` + `ElementProps` type + playground verification page
|
|
744
|
+
|
|
745
|
+
**Files:**
|
|
746
|
+
|
|
747
|
+
- Create: `layers/structure/layout/app/types/element.ts`
|
|
748
|
+
- Create: `layers/structure/layout/app/components/Element.vue`
|
|
749
|
+
- Test: `layers/structure/layout/app/components/Element.test.ts`
|
|
750
|
+
- Create: `apps/playground/app/pages/element.vue`
|
|
751
|
+
|
|
752
|
+
**Interfaces:**
|
|
753
|
+
|
|
754
|
+
- Consumes: `ElementLayoutProps`/`useElementLayout` (Task 2), `ElementGridProps`/`useElementGrid` (Task 5), `ElementSizingProps`/`useElementSizing` (Task 3), `ElementSurfaceProps`/`useElementSurface` (Task 4), `ElementInteractionProps`/`useElementInteraction` (Task 4), `LayoutAttrsInput`/`useLayoutAttrs` (core, Task 1).
|
|
755
|
+
- Produces: `ElementProps` type (exported union) and the `<Element>` component itself — the final deliverable, consumed by application pages.
|
|
756
|
+
|
|
757
|
+
- [ ] **Step 1: Write the failing component test**
|
|
758
|
+
|
|
759
|
+
Create `layers/structure/layout/app/components/Element.test.ts`:
|
|
760
|
+
|
|
761
|
+
```ts
|
|
762
|
+
import { mount } from '@vue/test-utils'
|
|
763
|
+
import { describe, expect, it } from 'vitest'
|
|
764
|
+
|
|
765
|
+
import Element from './Element.vue'
|
|
766
|
+
|
|
767
|
+
describe('Element', () => {
|
|
768
|
+
it('renders as a div by default', () => {
|
|
769
|
+
const wrapper = mount(Element)
|
|
770
|
+
expect(wrapper.element.tagName).toBe('DIV')
|
|
771
|
+
})
|
|
772
|
+
|
|
773
|
+
it('renders as the tag passed via the as prop', () => {
|
|
774
|
+
const wrapper = mount(Element, { props: { as: 'section' } })
|
|
775
|
+
expect(wrapper.element.tagName).toBe('SECTION')
|
|
776
|
+
})
|
|
777
|
+
|
|
778
|
+
it('renders slot content', () => {
|
|
779
|
+
const wrapper = mount(Element, { slots: { default: 'hello' } })
|
|
780
|
+
expect(wrapper.text()).toBe('hello')
|
|
781
|
+
})
|
|
782
|
+
|
|
783
|
+
it('merges layout, sizing, and surface styles into one style attribute', () => {
|
|
784
|
+
const wrapper = mount(Element, {
|
|
785
|
+
props: {
|
|
786
|
+
flex: true,
|
|
787
|
+
w: '80px',
|
|
788
|
+
bg: 'var(--ui-bg-elevated)',
|
|
789
|
+
},
|
|
790
|
+
})
|
|
791
|
+
const style = wrapper.attributes('style') ?? ''
|
|
792
|
+
expect(style).toContain('display: flex')
|
|
793
|
+
expect(style).toContain('width: 80px')
|
|
794
|
+
expect(style).toContain('background: var(--ui-bg-elevated)')
|
|
795
|
+
})
|
|
796
|
+
|
|
797
|
+
it('merges spacing (via useLayoutAttrs) and grid item-placement styles together', () => {
|
|
798
|
+
const wrapper = mount(Element, {
|
|
799
|
+
props: {
|
|
800
|
+
p: 'md',
|
|
801
|
+
colSpan: 6,
|
|
802
|
+
},
|
|
803
|
+
})
|
|
804
|
+
const style = wrapper.attributes('style') ?? ''
|
|
805
|
+
expect(style).toContain('padding: var(--fluid-space-md)')
|
|
806
|
+
expect(style).toContain('--_ce: 6')
|
|
807
|
+
})
|
|
808
|
+
})
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
812
|
+
|
|
813
|
+
Run: `cd layers/structure/layout && npx vitest run app/components/Element.test.ts`
|
|
814
|
+
Expected: FAIL — `Element.vue` not found.
|
|
815
|
+
|
|
816
|
+
- [ ] **Step 3: Write the ElementProps type**
|
|
817
|
+
|
|
818
|
+
Create `layers/structure/layout/app/types/element.ts`:
|
|
819
|
+
|
|
820
|
+
```ts
|
|
821
|
+
import type { Component } from 'vue'
|
|
822
|
+
import type { LayoutAttrsInput } from '#layers/core/app/composables/useLayoutAttrs'
|
|
823
|
+
|
|
824
|
+
import type { ElementGridProps } from '../composables/useElementGrid'
|
|
825
|
+
import type { ElementInteractionProps } from '../composables/useElementInteraction'
|
|
826
|
+
import type { ElementLayoutProps } from '../composables/useElementLayout'
|
|
827
|
+
import type { ElementSizingProps } from '../composables/useElementSizing'
|
|
828
|
+
import type { ElementSurfaceProps } from '../composables/useElementSurface'
|
|
829
|
+
|
|
830
|
+
export type ElementProps = ElementLayoutProps &
|
|
831
|
+
ElementGridProps &
|
|
832
|
+
LayoutAttrsInput &
|
|
833
|
+
ElementSizingProps &
|
|
834
|
+
ElementSurfaceProps &
|
|
835
|
+
ElementInteractionProps & {
|
|
836
|
+
as?: string | Component
|
|
837
|
+
}
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
- [ ] **Step 4: Write Element.vue**
|
|
841
|
+
|
|
842
|
+
Create `layers/structure/layout/app/components/Element.vue`:
|
|
843
|
+
|
|
844
|
+
```vue
|
|
845
|
+
<script setup lang="ts">
|
|
846
|
+
import type { CSSProperties } from 'vue'
|
|
847
|
+
|
|
848
|
+
import type { ElementProps } from '../types/element'
|
|
849
|
+
|
|
850
|
+
const props = withDefaults(defineProps<ElementProps>(), {
|
|
851
|
+
as: 'div',
|
|
852
|
+
})
|
|
853
|
+
|
|
854
|
+
const style = computed<CSSProperties>(() => ({
|
|
855
|
+
...useElementLayout(props),
|
|
856
|
+
...useLayoutAttrs(() => props).style.value,
|
|
857
|
+
...useElementGrid(props),
|
|
858
|
+
...useElementSizing(props),
|
|
859
|
+
...useElementSurface(props),
|
|
860
|
+
...useElementInteraction(props),
|
|
861
|
+
}))
|
|
862
|
+
</script>
|
|
863
|
+
|
|
864
|
+
<template>
|
|
865
|
+
<component :is="as" :style>
|
|
866
|
+
<slot />
|
|
867
|
+
</component>
|
|
868
|
+
</template>
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
`useElementLayout`, `useLayoutAttrs`, `useElementGrid`, `useElementSizing`, `useElementSurface`, `useElementInteraction` are all Nuxt auto-imports (composables in `app/composables/` are auto-imported within the layer, and `useLayoutAttrs` is auto-imported from `core` since `layout` extends `core` — see `layers/structure/layout/nuxt.config.ts:7`). No explicit imports needed in the `<script setup>` block for them.
|
|
872
|
+
|
|
873
|
+
- [ ] **Step 5: Run test to verify it passes**
|
|
874
|
+
|
|
875
|
+
Run: `cd layers/structure/layout && npx vitest run app/components/Element.test.ts`
|
|
876
|
+
Expected: PASS, all 5 tests green.
|
|
877
|
+
|
|
878
|
+
If auto-imports aren't resolved in the Vitest environment (composables not globally available outside a running Nuxt instance), explicitly import them at the top of `Element.vue`'s `<script setup>` instead:
|
|
879
|
+
|
|
880
|
+
```ts
|
|
881
|
+
import { useLayoutAttrs } from '#layers/core/app/composables/useLayoutAttrs'
|
|
882
|
+
|
|
883
|
+
import { useElementGrid } from '../composables/useElementGrid'
|
|
884
|
+
import { useElementInteraction } from '../composables/useElementInteraction'
|
|
885
|
+
import { useElementLayout } from '../composables/useElementLayout'
|
|
886
|
+
import { useElementSizing } from '../composables/useElementSizing'
|
|
887
|
+
import { useElementSurface } from '../composables/useElementSurface'
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
Re-run the test after adding imports if the auto-import approach fails.
|
|
891
|
+
|
|
892
|
+
- [ ] **Step 6: Create the playground verification page**
|
|
893
|
+
|
|
894
|
+
Create `apps/playground/app/pages/element.vue`:
|
|
895
|
+
|
|
896
|
+
```vue
|
|
897
|
+
<template>
|
|
898
|
+
<Element block p="xl">
|
|
899
|
+
<h2>Grid container + item placement</h2>
|
|
900
|
+
<Element grid :cols="12" gap="lg" p="lg" bg="var(--ui-bg-elevated)" radius="0.5rem">
|
|
901
|
+
<Element :col-span="12" bg="var(--color-green-400)" color="white" p="md" radius="0.25rem">
|
|
902
|
+
colSpan 12
|
|
903
|
+
</Element>
|
|
904
|
+
<Element :col-span="8" bg="var(--color-green-500)" color="white" p="md" radius="0.25rem">
|
|
905
|
+
colSpan 8
|
|
906
|
+
</Element>
|
|
907
|
+
<Element :col-span="4" bg="var(--color-green-600)" color="white" p="md" radius="0.25rem">
|
|
908
|
+
colSpan 4
|
|
909
|
+
</Element>
|
|
910
|
+
<Element
|
|
911
|
+
:col-start="3"
|
|
912
|
+
:col-span="6"
|
|
913
|
+
bg="var(--color-green-700)"
|
|
914
|
+
color="white"
|
|
915
|
+
p="md"
|
|
916
|
+
radius="0.25rem"
|
|
917
|
+
>
|
|
918
|
+
colStart 3 / colSpan 6
|
|
919
|
+
</Element>
|
|
920
|
+
</Element>
|
|
921
|
+
</Element>
|
|
922
|
+
|
|
923
|
+
<Element block p="xl">
|
|
924
|
+
<h2>Flex + sizing + interaction</h2>
|
|
925
|
+
<Element flex gap="md" p="lg" bg="var(--ui-bg-elevated)" radius="0.5rem">
|
|
926
|
+
<Element
|
|
927
|
+
w="80px"
|
|
928
|
+
h="80px"
|
|
929
|
+
bg="var(--color-green-400)"
|
|
930
|
+
radius="9999px"
|
|
931
|
+
cursor="pointer"
|
|
932
|
+
select="none"
|
|
933
|
+
/>
|
|
934
|
+
<Element
|
|
935
|
+
w="160px"
|
|
936
|
+
h="80px"
|
|
937
|
+
bg="var(--color-green-500)"
|
|
938
|
+
color="white"
|
|
939
|
+
p="sm"
|
|
940
|
+
radius="0.5rem"
|
|
941
|
+
shadow="0 4px 12px rgb(0 0 0 / 0.15)"
|
|
942
|
+
>
|
|
943
|
+
card
|
|
944
|
+
</Element>
|
|
945
|
+
<Element
|
|
946
|
+
min-w="120px"
|
|
947
|
+
max-w="240px"
|
|
948
|
+
h="80px"
|
|
949
|
+
bg="var(--color-green-600)"
|
|
950
|
+
color="white"
|
|
951
|
+
p="sm"
|
|
952
|
+
radius="0.5rem"
|
|
953
|
+
>
|
|
954
|
+
min/max width
|
|
955
|
+
</Element>
|
|
956
|
+
</Element>
|
|
957
|
+
</Element>
|
|
958
|
+
|
|
959
|
+
<Element block p="xl">
|
|
960
|
+
<h2>Layout modes</h2>
|
|
961
|
+
<Element flex gap="sm">
|
|
962
|
+
<Element block bg="var(--color-green-400)" p="sm" radius="0.25rem" color="white"
|
|
963
|
+
>block</Element
|
|
964
|
+
>
|
|
965
|
+
<Element flex bg="var(--color-green-500)" p="sm" radius="0.25rem" color="white">flex</Element>
|
|
966
|
+
<Element hidden bg="var(--color-green-600)" p="sm" radius="0.25rem" color="white">
|
|
967
|
+
hidden (not rendered visibly)
|
|
968
|
+
</Element>
|
|
969
|
+
</Element>
|
|
970
|
+
</Element>
|
|
971
|
+
|
|
972
|
+
<Element block p="xl">
|
|
973
|
+
<h2>Margin (Task 1 verification)</h2>
|
|
974
|
+
<Element bg="var(--ui-bg-elevated)" p="md">
|
|
975
|
+
<Element bg="var(--color-green-500)" color="white" p="sm" m="lg" radius="0.25rem">
|
|
976
|
+
m="lg" pushes this box away from its siblings
|
|
977
|
+
</Element>
|
|
978
|
+
</Element>
|
|
979
|
+
</Element>
|
|
980
|
+
</template>
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
- [ ] **Step 7: Manually verify in the browser**
|
|
984
|
+
|
|
985
|
+
Run: `PLAYGROUND_LAYERS=core,layout pnpm dev` from the repo root, then navigate to `/element`.
|
|
986
|
+
|
|
987
|
+
Verify: the 12-column grid row renders with correctly spanned/positioned green blocks, the flex row shows a circle/card/min-max box, the layout-modes row hides the third box, and the margin box visibly indents from its container. Confirm no console errors.
|
|
988
|
+
|
|
989
|
+
- [ ] **Step 8: Run full test suite one more time**
|
|
990
|
+
|
|
991
|
+
Run: `pnpm --filter layers/core --filter layers/structure/layout test` (or `cd` into each and run `npx vitest run` if the filter form doesn't match this repo's script names — check `package.json`'s `scripts.test` first with `cat layers/core/package.json layers/structure/layout/package.json | grep -A2 '"scripts"'`)
|
|
992
|
+
Expected: PASS across both layers, zero regressions.
|
|
993
|
+
|
|
994
|
+
- [ ] **Step 9: Run typecheck**
|
|
995
|
+
|
|
996
|
+
Run: `pnpm typecheck`
|
|
997
|
+
Expected: PASS — no new type errors from `ElementProps`, `Element.vue`, or the modified `LayoutAttrsInput`.
|
|
998
|
+
|
|
999
|
+
- [ ] **Step 10: Commit**
|
|
1000
|
+
|
|
1001
|
+
```bash
|
|
1002
|
+
git add layers/structure/layout/app/types/element.ts layers/structure/layout/app/components/Element.vue layers/structure/layout/app/components/Element.test.ts apps/playground/app/pages/element.vue
|
|
1003
|
+
git commit -m "feat(layout): add Element polymorphic primitive
|
|
1004
|
+
|
|
1005
|
+
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
1006
|
+
Claude-Session: https://claude.ai/code/session_017vMSCbNb6g9hyb9sBUsAQN"
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
---
|
|
1010
|
+
|
|
1011
|
+
## Self-Review
|
|
1012
|
+
|
|
1013
|
+
**Spec coverage:**
|
|
1014
|
+
|
|
1015
|
+
- §3.1 Layout group → Task 2. §3.2 Spacing (incl. margin) → Task 1. §3.3 Grid (both axes) → Task 5. §3.4 Sizing → Task 3. §3.5 Surface → Task 4. §3.6 Interaction → Task 4. §4 `Element.vue` → Task 6. §5 `ElementProps` type → Task 6. §6 out-of-scope items — no task touches them (confirmed: no typography props, no semantic colour, no align/justify/bleed/density/layer exposed on Element, no LayoutGridItem/gridPlacementStyle edits, no new Grid/Section container component). §7 file layout → matches the File Structure section above exactly, plus a playground page for manual verification not explicitly required by the spec but implied by its own worked examples (§4, §5). §8 testing → per-composable unit tests (Task 1–5) plus an `Element.vue` mount test (Task 6) covering `as` passthrough, slot rendering, and style merge order, matching the spec's stated testing requirements. §9 success criteria — criterion 3 ("zero changes to LayoutGridItem/gridPlacementStyle.ts") is enforced by Task 5 Step 6 (full layout suite re-run) and the Global Constraints section; criterion 4 (HStack/VStack unaffected) is enforced by Task 1 Step 6.
|
|
1016
|
+
|
|
1017
|
+
**Placeholder scan:** No TBD/TODO/"add appropriate handling" patterns present. Every step has literal code or an exact command.
|
|
1018
|
+
|
|
1019
|
+
**Type consistency:** `ElementGridProps`, `ElementSizingProps`, `ElementSurfaceProps`, `ElementInteractionProps`, `ElementLayoutProps`, `LayoutAttrsInput` are named identically everywhere they're referenced across Tasks 2–6. `useElementGrid`/`useElementSizing`/`useElementSurface`/`useElementInteraction`/`useElementLayout` function names match between their defining task and their `Element.vue` usage (Task 6). `ElementProps`'s field list in Task 6 Step 3 is the exact union of every composable's Props type defined in Tasks 1–5, nothing added or missing.
|