kmcom-nuxt-layers 6.3.0 → 6.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/docs/MIGRATION-v6.4.0.md +215 -0
  2. package/docs/MIGRATION-v6.5.0.md +87 -0
  3. package/docs/MIGRATION.md +3 -1
  4. package/docs/USAGE.md +35 -10
  5. package/docs/adr/0003-modular-step-system-replaces-fluid-clamps.md +16 -0
  6. package/docs/component-levels.md +1 -0
  7. package/docs/layout-shift-causes.md +18 -0
  8. package/layers/content/app/components/content/Figure.vue +1 -1
  9. package/layers/content/app/composables/useContentSection.ts +1 -1
  10. package/layers/core/app/app.config.ts +9 -5
  11. package/layers/core/app/app.vue +2 -1
  12. package/layers/core/app/components/LoadingScreen.vue +35 -21
  13. package/layers/core/app/composables/useCache.ts +2 -2
  14. package/layers/core/app/composables/useErrorLog.ts +5 -5
  15. package/layers/core/app/composables/useLoading.ts +19 -10
  16. package/layers/core/app/plugins/loading.client.ts +11 -3
  17. package/layers/core/app/types/loading.ts +10 -3
  18. package/layers/core/app/utils/loading.ts +7 -16
  19. package/layers/core/nuxt.config.ts +14 -14
  20. package/layers/core/server/utils/app-config.ts +17 -0
  21. package/layers/data/auth/app/middleware/auth.ts +1 -1
  22. package/layers/data/forms/app/components/Form/Contact.vue +1 -1
  23. package/layers/data/forms/server/api/contact.post.ts +16 -8
  24. package/layers/data/forms/server/api/forms/status.get.ts +3 -0
  25. package/layers/data/forms/{app → shared}/utils/contact-schema.ts +2 -1
  26. package/layers/data/metadata/server/api/metadata/search.get.ts +27 -7
  27. package/layers/data/metadata/server/api/metadata/status.get.ts +4 -1
  28. package/layers/data/metadata/server/api/metadata/sync.post.ts +13 -3
  29. package/layers/data/metadata/server/utils/metadata/resolve-record.ts +3 -3
  30. package/layers/delivery/feeds/nuxt.config.ts +6 -1
  31. package/layers/delivery/feeds/package.json +2 -2
  32. package/layers/delivery/feeds/server/routes/feed/demo.get.ts +2 -2
  33. package/layers/delivery/feeds/server/utils/content-adapter.ts +1 -1
  34. package/layers/delivery/feeds/server/utils/feed-service.ts +2 -2
  35. package/layers/design-system/theming/app/plugins/theme.client.ts +7 -0
  36. package/layers/design-system/theming/nuxt.config.ts +0 -2
  37. package/layers/design-system/typography/app/assets/css/typography.css +41 -5
  38. package/layers/design-system/visual/app/components/HoverDimGroup.vue +11 -3
  39. package/layers/motion/scroll/app/plugins/locomotive-scroll.client.ts +3 -2
  40. package/layers/render/canvas/nuxt.config.ts +0 -4
  41. package/layers/render/shader/nuxt.config.ts +0 -4
  42. package/layers/structure/navigation/app/app.config.ts +37 -14
  43. package/layers/structure/navigation/app/components/Mast/Nav.vue +14 -13
  44. package/layers/structure/navigation/app/components/Mast/NavModal.vue +83 -28
  45. package/layers/structure/navigation/app/components/Site/Logo.vue +35 -0
  46. package/layers/structure/navigation/app/components/Site/Title.vue +3 -3
  47. package/layers/structure/navigation/app/composables/mastNav.ts +38 -13
  48. package/layers/structure/navigation/app/types/nav.ts +34 -0
  49. package/layers/structure/routing/app/middleware/01.maintenance.global.ts +4 -1
  50. package/layers/structure/routing/app/middleware/02.governance.global.ts +1 -1
  51. package/layers/structure/routing/nuxt.config.ts +19 -12
  52. package/layers/structure/routing/server/api/feature-flags.get.ts +4 -2
  53. package/package.json +35 -5
  54. package/docs/FALLOW-COMPLEXITY-DUPLICATION-AUDIT.md +0 -64
  55. package/docs/IMPROVE-AUDIT-README.md +0 -29
  56. package/docs/IMPROVE-AUDIT-RESULTS.md +0 -51
  57. package/docs/IMPROVE-DEEP-AUDIT-RESULTS.md +0 -81
  58. package/docs/fallow-refactor/apps-debug.md +0 -27
  59. package/docs/fallow-refactor/apps-playground.md +0 -46
  60. package/docs/fallow-refactor/apps-visual-identity.md +0 -41
  61. package/docs/fallow-refactor/layers-animations.md +0 -34
  62. package/docs/fallow-refactor/layers-canvas.md +0 -32
  63. package/docs/fallow-refactor/layers-content.md +0 -33
  64. package/docs/fallow-refactor/layers-core.md +0 -39
  65. package/docs/fallow-refactor/layers-feeds.md +0 -39
  66. package/docs/fallow-refactor/layers-forms.md +0 -30
  67. package/docs/fallow-refactor/layers-layout.md +0 -42
  68. package/docs/fallow-refactor/layers-mailer.md +0 -32
  69. package/docs/fallow-refactor/layers-motion.md +0 -27
  70. package/docs/fallow-refactor/layers-navigation.md +0 -31
  71. package/docs/fallow-refactor/layers-page-transitions.md +0 -30
  72. package/docs/fallow-refactor/layers-routing.md +0 -33
  73. package/docs/fallow-refactor/layers-scripts.md +0 -35
  74. package/docs/fallow-refactor/layers-scroll.md +0 -38
  75. package/docs/fallow-refactor/layers-seo.md +0 -32
  76. package/docs/fallow-refactor/layers-shader.md +0 -53
  77. package/docs/fallow-refactor/layers-theme.md +0 -33
  78. package/docs/fallow-refactor/layers-transitions.md +0 -27
  79. package/docs/fallow-refactor/layers-typography.md +0 -29
  80. package/docs/fallow-refactor/layers-visual.md +0 -34
  81. package/docs/superpowers/plans/2026-09-03-layout-layer-fluid-rebuild.md +0 -1850
  82. package/docs/superpowers/plans/2026-09-03-vue-element-system.md +0 -1019
  83. package/docs/superpowers/plans/2026-09-04-grid-layer.md +0 -977
  84. package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-0-1-design.md +0 -533
  85. package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-2-design.md +0 -154
  86. package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-3-design.md +0 -109
  87. package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-4-design.md +0 -124
  88. package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-5-design.md +0 -121
  89. package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-6-design.md +0 -77
  90. package/docs/superpowers/specs/2026-08-30-layer-taxonomy-tier-7-design.md +0 -73
  91. package/docs/superpowers/specs/2026-08-31-swiss-grid-layer-design.md +0 -190
  92. package/docs/superpowers/specs/2026-09-03-layout-layer-fluid-rebuild-design.md +0 -312
  93. package/docs/superpowers/specs/2026-09-03-vue-element-system-design.md +0 -342
  94. package/docs/superpowers/specs/2026-09-04-grid-layer-design.md +0 -236
  95. package/layers/content/app/utils/galleryImageMeta.test.ts +0 -30
  96. package/layers/core/app/components/Element.test.ts +0 -90
  97. package/layers/core/app/components/ElementTw.test.ts +0 -64
  98. package/layers/core/app/composables/useLayoutAttrs.test.ts +0 -77
  99. package/layers/core/app/utils/browserInfo.test.ts +0 -81
  100. package/layers/core/app/utils/featureClasses.test.ts +0 -101
  101. package/layers/core/app/utils/loading.test.ts +0 -48
  102. package/layers/core/app/utils/resolveElementStyle.test.ts +0 -99
  103. package/layers/core/app/utils/scrollGuardClamp.test.ts +0 -36
  104. package/layers/core/app/utils/styleToTailwindClasses.test.ts +0 -31
  105. package/layers/data/forms/app/utils/contact-schema.test.ts +0 -75
  106. package/layers/data/forms/app/utils/fieldProps.test.ts +0 -141
  107. package/layers/data/metadata/providers/comicvine/server/utils/metadata-comicvine/normalise.test.ts +0 -201
  108. package/layers/data/metadata/providers/google-books/server/utils/metadata-google-books/client.test.ts +0 -76
  109. package/layers/data/metadata/providers/google-books/server/utils/metadata-google-books/normalise.test.ts +0 -144
  110. package/layers/data/metadata/providers/openlibrary/server/utils/metadata-openlibrary/client.test.ts +0 -25
  111. package/layers/data/metadata/providers/openlibrary/server/utils/metadata-openlibrary/normalise.test.ts +0 -144
  112. package/layers/data/metadata/providers/themoviedb/server/utils/metadata-tmdb/client.test.ts +0 -29
  113. package/layers/data/metadata/providers/themoviedb/server/utils/metadata-tmdb/normalise.test.ts +0 -162
  114. package/layers/data/metadata/server/utils/metadata/cache.test.ts +0 -39
  115. package/layers/data/metadata/server/utils/metadata/provider-fetch.test.ts +0 -27
  116. package/layers/data/metadata/server/utils/metadata/provider-registry.test.ts +0 -54
  117. package/layers/data/metadata/server/utils/metadata/resolve-record.test.ts +0 -87
  118. package/layers/data/metadata/server/utils/metadata/search.test.ts +0 -204
  119. package/layers/delivery/feeds/server/utils/content-adapter.test.ts +0 -67
  120. package/layers/delivery/feeds/server/utils/feed-author.test.ts +0 -71
  121. package/layers/delivery/feeds/server/utils/feed-config.test.ts +0 -184
  122. package/layers/delivery/feeds/server/utils/formats.test.ts +0 -71
  123. package/layers/delivery/seo/app/utils/seoConfig.test.ts +0 -58
  124. package/layers/design-system/theming/server/utils/accent-css.test.ts +0 -71
  125. package/layers/design-system/theming/server/utils/fouc-config.test.ts +0 -78
  126. package/layers/design-system/typography/app/components/Typography/Headline.test.ts +0 -36
  127. package/layers/design-system/typography/app/components/Typography/index.test.ts +0 -31
  128. package/layers/design-system/typography/app/composables/color.test.ts +0 -30
  129. package/layers/design-system/typography/app/composables/typography.test.ts +0 -53
  130. package/layers/design-system/visual/app/composables/tint.test.ts +0 -22
  131. package/layers/design-system/visual/app/utils/colorTokens.test.ts +0 -51
  132. package/layers/design-system/visual/app/utils/gradientStyle.test.ts +0 -75
  133. package/layers/design-system/visual/app/utils/responsiveSizes.test.ts +0 -71
  134. package/layers/motion/animations/app/components/Motion/Staggered.test.ts +0 -40
  135. package/layers/motion/scroll/app/composables/useReducedMotion.test.ts +0 -23
  136. package/layers/motion/scroll/app/utils/parallax.test.ts +0 -24
  137. package/layers/structure/grid/app/composables/useGridTracks.test.ts +0 -94
  138. package/layers/structure/grid/app/types/tracks.test.ts +0 -32
  139. package/layers/structure/grid/app/utils/placement.test.ts +0 -19
  140. package/layers/structure/layout/app/utils/gridPlacementStyle.test.ts +0 -269
  141. package/layers/structure/navigation/app/utils/site.test.ts +0 -39
  142. package/layers/structure/routing/app/utils/resolveRoute.test.ts +0 -47
@@ -1,73 +0,0 @@
1
- # Layer Taxonomy Redesign — Part 7: Tier 7 (Delivery)
2
-
3
- ## Scope
4
-
5
- Forensic audit of `seo`, `scripts`, `feeds`, plus the `baseline` → `scripts` merge carried over
6
- as an open decision from Tier 0-1. Cleanup-only pass, per user direction from Tier 6.
7
-
8
- ## Findings and resolution
9
-
10
- ### `seo`: stale `OgImageBasic` fallback — fixed (real bug, low severity)
11
-
12
- `resolveOgImageConfig()`'s fallback (used only when `seoLayer` config is entirely absent) still
13
- returned `'OgImageBasic'` — the old default, already fixed at the `app.config.ts` level to
14
- `'OgImageNuxtSeo'` in an earlier session (`OgImageBasic` doesn't exist in `nuxt-og-image@6`). The
15
- fallback path is effectively dead in practice (app.config always provides `seoLayer`), but the
16
- stale value was still wrong and the test asserted the same stale value. **Fixed**: fallback and
17
- test both updated to `'OgImageNuxtSeo'`.
18
-
19
- ### `scripts/app/types/` — dead duplicate types — deleted
20
-
21
- `types/scripts.ts` exported `LoadStrategy`, `ScriptLoaderOptions`, and `AnalyticsProxy` — zero
22
- consumers anywhere. `useScriptLoader.ts` declares its own local `LoadStrategy`/
23
- `ScriptLoaderOptions` instead of importing the "shared" ones, and `AnalyticsProxy` was unused even
24
- internally (the real type in use is `AnalyticsClient` in `scriptClients.ts`). **Fixed**: deleted
25
- `types/scripts.ts` and `types/index.ts`, removed the now-pointless `#layers/scripts/types` alias.
26
-
27
- ### `baseline` merged into `scripts` — done (user decision, carried over from Tier 0-1)
28
-
29
- `baseline` was 3 files (`BaselineStatus.vue`, a client plugin registering the `baseline-status`
30
- web component, an `isCustomElement` compiler config) — a third-party embed integration, the same
31
- shape as `scripts`' existing analytics/GTM facades, not a standalone tier member. **Done**: moved
32
- `BaselineStatus.vue` and `baseline-status.client.ts` into `layers/scripts/app/`, added the
33
- `isCustomElement` compiler option and the `baseline-status` dependency to `scripts`, deleted
34
- `layers/baseline`, removed `baseline` from the playground's `AVAILABLE_LAYERS`/`LAYER_PATHS`/
35
- `LAYER_DEPENDENCIES`. Component name is unchanged (`BaselineStatus`, auto-imported by filename
36
- regardless of layer), so `apps/playground/app/pages/baseline.vue` and `index.vue` needed no edits.
37
-
38
- ## Confirmed correct, no action
39
-
40
- - `seo`: `nuxt.config.ts` (`@nuxtjs/seo` module + `site` config), `app.config.ts` (correct
41
- `defineAppConfig()` + colocated `declare module`), `useSeoConfig`/`resolveSeoConfig` — real,
42
- consumed by `apps/playground/app/pages/seo.vue`, well-tested (`seoConfig.test.ts`, now updated).
43
- - `scripts`: `app.config.ts` (correct structure), `useAnalytics`/`useGtm`/`useScriptsConsent`/
44
- `useScriptLoader`/`useYoutubeEmbed`, `scriptClients.ts` (provider factories: ga4/plausible/
45
- fathom/GTM, consent-gated triggers, noop fallbacks) — all real, consumed by
46
- `apps/playground/app/pages/scripts.vue`.
47
- - `feeds`: `nuxt.config.ts`'s conditional `extends` (empty unless `FEEDS_STANDALONE` env var is
48
- set) looks like the "don't rely on load order" anti-pattern at first glance, but it isn't — the
49
- playground's own `LAYER_DEPENDENCIES` mechanism (`feeds: ['core', 'content']`) is the actual
50
- dependency-injection path for this repo's layer resolver, and the env-conditional `extends`
51
- exists specifically to let `feeds` also be consumed as a truly standalone layer outside this
52
- playground. `app.config.ts`, `feed-catalog.ts` (collection resolution, missing-collection
53
- warnings), `feed-head.ts` plugin (auto-discovery `<link rel="alternate">` tags), `feed-service.ts`,
54
- all `server/routes/feed/**` handlers (sampled `rss.get.ts`, `index.get.ts` — consistent), `Feeds/
55
- Index.vue`, `Feeds/RouteCard.vue` — real, thorough, already well-tested
56
- (`content-adapter.test.ts`, `feed-author.test.ts`, `feed-config.test.ts`, `formats.test.ts`),
57
- consumed by `apps/playground/app/pages/feeds.vue`.
58
-
59
- ## Consolidated action list (Tier 7)
60
-
61
- - [x] Fix stale `OgImageBasic` fallback + test in `seo`
62
- - [x] Delete dead `scripts/app/types/` (unused duplicate types)
63
- - [x] Merge `baseline` into `scripts`, delete the `baseline` layer, update playground registration
64
-
65
- ## Not yet covered
66
-
67
- That closes the 8-tier sweep (Foundation → Design System → Structure → Content → Data → Motion →
68
- Render → Delivery). Still pending from earlier tiers, none touched this pass: `theme`'s
69
- `app.config.ts` structural defect and the `theme`→`theming` rename, `typography`'s font-prop
70
- infrastructure, breakpoint token consolidation, `AppContainer` clamp() conversion,
71
- `compatibilityVersion: 5` flip, `error.vue` real implementation, `starter` layer relocation into
72
- `apps/`, `useMetadataItem`'s fate, the openlibrary/google-books provider error-handling
73
- divergence, and the 8 zero-consumer `shader` composables left open in Tier 6.
@@ -1,190 +0,0 @@
1
- # Swiss Grid Layer — Design Spec
2
-
3
- Source: "Swiss Grid Spatial System for Web Interfaces" artifact (functional
4
- spec, rev. 2026-08-31), condensed from design discussion. This document
5
- translates that spec into a concrete new layer for this monorepo.
6
-
7
- ## §0 Scope and relationship to `layers/structure/layout`
8
-
9
- New layer: `layers/structure/swiss-grid` — tier 2, sibling to `layout`.
10
- Depends only on `core`.
11
-
12
- `layers/structure/layout` (Swiss Grid _subgrid_ system: `LayoutMain` /
13
- `LayoutSection` / `LayoutGridItem`, hero/centered presets, `HStack` /
14
- `VStack` / `ZStack` / `Spacer`, `useGridConfig()`) is **untouched** by this
15
- work. It stays the system in use by `layers/content` (Gallery/Blog/Portfolio
16
- Page + Item/Slug/Image variants) and `layers/structure/navigation`
17
- (`Nav`/`NavModal`, which call `useGridConfig()` directly). Migrating those
18
- consumers to the new layer is explicitly a later, separate pass — not part
19
- of this spec.
20
-
21
- The two layers may run in the same app simultaneously. They do not share
22
- components, CSS custom properties, or config namespaces (`layoutLayer.*` vs
23
- a new `swissGridLayer.*` app.config key), so there is no collision risk.
24
-
25
- ## §1 Primitives
26
-
27
- Two components, matching the artifact's §3 API surface exactly:
28
-
29
- ```ts
30
- // SwissGrid — grid root
31
- interface SwissGridProps {
32
- columns?: ResponsiveValue<number> // default: xs:4 sm:4 md:8 lg:12 xl:12
33
- gap?: ResponsiveValue<Spacing> // Spacing token from core, e.g. 'md'
34
- tag?: string // default: 'div'
35
- }
36
-
37
- // SwissGridItem — positioned child
38
- type SpanAlias = 'full' | 'half' | 'third' | 'quarter'
39
- interface SwissGridItemProps {
40
- start?: number
41
- span?: number | SpanAlias // default: 'full'
42
- order?: number
43
- tag?: string // default: 'div'
44
- }
45
- ```
46
-
47
- `SwissGrid` sets `display: grid`, a responsive `grid-template-columns:
48
- repeat(N, minmax(0, 1fr))` (N from `columns`, resolved per breakpoint via
49
- `core`'s `ResponsiveValue<T>` — 5 tiers: `default/sm/md/lg/xl`, media
50
- queries built from `core`'s existing `--breakpoint-sm/md/lg/xl` tokens), a
51
- responsive `gap` from `core`'s `Spacing` scale, and `container-type:
52
- inline-size` (§7 — makes it a container-query boundary by default; every
53
- `SwissGrid` is a potential nesting point, not just the page root).
54
-
55
- `SwissGridItem` sets `grid-column: <start> / span <n>` (or `span <n>` alone
56
- when `start` is omitted) and `order` via an inline style object, built by a
57
- pure function — same shape as the old layer's `gridPlacementStyle.ts`, but a
58
- new implementation in this layer, not shared code. Old and new stay
59
- decoupled so `layout`'s cleanup/behavior can't leak into this layer or vice
60
- versa.
61
-
62
- ## §2 Span aliases — resolving the artifact's open item
63
-
64
- The artifact leaves the exact alias set open (§13). Resolved here:
65
-
66
- - Aliases are expressed in a fixed **12-unit vocabulary**: `full=12,
67
- half=6, third=4, quarter=3` — independent of the grid's live column
68
- count, the same mental model as Bootstrap-style 12-col systems.
69
- - Because `columns` can be less than 12 at smaller breakpoints (4 at `xs`,
70
- 8 at `sm`/`md` per §1's default), an alias's rendered span is `min(alias
71
- value, current column count)` — computed in the component, not CSS
72
- `calc()` (integer `grid-column: span N` can't come from a fractional
73
- division). This keeps `half` always rendering as "half of whatever this
74
- breakpoint's grid actually has," clamped so it never overflows a
75
- narrower grid.
76
- - The 12-unit table lives in `app.config.ts` under `swissGridLayer.ui.spanAliases`,
77
- editable like the old layer's presets, not hardcoded in the component.
78
-
79
- ## §3 Composition tier — Cluster and Flow
80
-
81
- New components, this layer, alongside `SwissGrid`/`SwissGridItem`:
82
-
83
- - **`Cluster`** — horizontal grouping: `display: flex; flex-wrap: wrap`,
84
- `gap` from `core`'s `Spacing` scale, optional `align`/`justify` props.
85
- For groups of items that wrap as a unit (tag lists, button rows,
86
- metadata chips) — distinct from `SwissGrid`, which is column-track-based.
87
- - **`Flow`** — vertical stack with automatic rhythm: renders `> * + *
88
- { margin-block-start: var(--fluid-space-<size>) }` scoped via a CSS
89
- class, `size` prop selecting the `core` fluid-space token. For prose-like
90
- vertical sequences where every child needs the same gap, without each
91
- child managing its own margin.
92
-
93
- `HStack`/`VStack`/`Split` (old layer) are not duplicated here — `Cluster`
94
- and `Flow` cover shapes those don't (wrap-as-a-unit and auto-rhythm
95
- respectively), not full stack semantics.
96
-
97
- ## §4 Vertical rhythm and fluid tokens — reusing `core`
98
-
99
- `core` already ships `layers/core/app/assets/css/tokens/spacing-fluid.css`:
100
- `--fluid-space-xs` through `--fluid-space-3xl`, `clamp()`-based, **plus**
101
- `-cq` container-relative variants (`--fluid-space-md-cq`, etc.) already
102
- built on `cqi` units. This is exactly what the artifact's §5 (baseline
103
- scale) and §6 (fluid `clamp()` tokens) ask for.
104
-
105
- **Decision: swiss-grid does not define its own spacing scale.** `gap` on
106
- `SwissGrid`, `Cluster`, and `Flow` all take `core`'s `Spacing` token type
107
- and resolve to `core`'s existing `--fluid-space-*` custom properties (the
108
- `-cq` variant when the component sits inside a `container-type` ancestor,
109
- plain variant otherwise). No new token set, no duplication, no future
110
- convergence work needed — building a parallel scale here would be
111
- first-rung ladder work nobody asked for.
112
-
113
- ## §5 Container queries (§7)
114
-
115
- `container-type: inline-size` ships on `SwissGrid` by default (not opt-in)
116
- — every grid is nestable. Column-count and gap responsiveness _can_ be
117
- expressed via `@container` queries scoped to a `SwissGrid`'s own inline
118
- size (for a grid inside a sidebar vs. full page) as well as via the
119
- viewport-keyed `ResponsiveValue` breakpoints in §1 — both are supported,
120
- picked per-usage by which query type the CSS in question actually needs.
121
-
122
- ## §6 Dev overlay (§10)
123
-
124
- `SwissGridDebug` component, same `⌘⇧G` shortcut and visual language as the
125
- old layer's `LayoutGridDebug`, reimplemented against the new grid's column
126
- custom properties. Toggles a column-boundary overlay for whichever
127
- `SwissGrid` instances are on screen.
128
-
129
- ## §7 CSS conventions
130
-
131
- All new CSS in this layer uses **logical properties**: `inline-size` /
132
- `block-size` over `width`/`height`, `margin-inline`/`padding-block` over
133
- directional variants, etc. This is a deliberate departure from some
134
- existing CSS in the repo (which uses physical properties) — noted as a
135
- forward-looking convention for this layer only; not a retrofit of `layout`
136
- or other layers.
137
-
138
- ## §8 Not building
139
-
140
- - **No "Section" concept.** The artifact never mentions viewport-height
141
- sections — that's `layout`-specific (12-row/100vh `LayoutSection`). Out
142
- of scope.
143
- - **No migration of `content`/`navigation` consumers.** Explicitly deferred
144
- to a later pass per user decision.
145
- - **No shared code with `layout`'s `gridPlacementStyle.ts`.** Pattern
146
- reused, implementation is new and independent.
147
-
148
- ## §9 File layout
149
-
150
- ```
151
- layers/structure/swiss-grid/
152
- ├── app/
153
- │ ├── assets/css/
154
- │ │ └── swiss-grid.css # entry point, imports below
155
- │ ├── components/
156
- │ │ ├── SwissGrid.vue
157
- │ │ ├── SwissGridItem.vue
158
- │ │ ├── SwissGridDebug.vue
159
- │ │ ├── Cluster.vue
160
- │ │ └── Flow.vue
161
- │ ├── composables/
162
- │ │ └── useSwissGrid.ts # config resolution, span-alias lookup
163
- │ ├── utils/
164
- │ │ └── gridItemStyle.ts # pure style-building fns (start/span/order)
165
- │ ├── types/
166
- │ │ └── swissGrid.ts # SwissGridProps, SwissGridItemProps, SpanAlias
167
- │ └── app.config.ts # swissGridLayer.ui.{columns,spanAliases}
168
- ├── nuxt.config.ts # extends: ['../../core'], alias #layers/swiss-grid
169
- └── CLAUDE.md
170
- ```
171
-
172
- ## §10 Testing
173
-
174
- - `gridItemStyle.test.ts` — unit tests for the start/span/order → inline
175
- style function, and the alias→span clamped-lookup function (mirrors old
176
- layer's `gridPlacementStyle.test.ts` shape).
177
- - `tests/nuxt/use-swiss-grid.test.ts` — composable resolution test (config
178
- defaults, override via app.config), same pattern as
179
- `use-grid-config.test.ts`.
180
- - Component mount tests for `SwissGrid`/`SwissGridItem` (renders expected
181
- `grid-template-columns`/`grid-column` given props).
182
- - New playground demo page (not `layout.vue` — a separate page, e.g.
183
- `apps/playground/app/pages/swiss-grid.vue`) to visually verify: column
184
- grid at each breakpoint, span aliases, `Cluster`/`Flow`, container-query
185
- nesting, dev overlay.
186
-
187
- ## §11 Open items carried forward
188
-
189
- None from the artifact remain open — §2 above resolves the span-alias set,
190
- §6 resolves the dev-overlay mechanism (reuse of the old layer's shortcut).
@@ -1,312 +0,0 @@
1
- # Layout Layer — Fluid Rebuild Design Spec
2
-
3
- Source: `LAYOUT-SYSTEM.md` (forensic reference for the `swiss-test/`
4
- prototype — `src/layout.css`, vanilla Vite/TS, no framework), validated
5
- against the working prototype at `/Users/kieranmansfield/Developer/layers/swiss-test`.
6
- This document adapts that CSS-only derivation into a full rebuild of
7
- `layers/structure/layout`, replacing its current breakpoint-based
8
- 6/12/18-column subgrid system.
9
-
10
- ## §0 Relationship to the Swiss Grid tier-2 spec
11
-
12
- `docs/superpowers/specs/2026-08-31-swiss-grid-layer-design.md` proposes a
13
- **separate sibling layer** (`layers/structure/swiss-grid`) that leaves
14
- `layers/structure/layout` untouched. This spec is a **different track**: it
15
- replaces `layout` itself, in place, rather than adding a new layer beside
16
- it. The two specs are not both implementable as originally scoped — before
17
- implementation starts on this one, reconcile with the user whether the
18
- tier-2 sibling-layer spec is superseded, deferred, or still wanted
19
- alongside this rebuild.
20
-
21
- ## §1 Governing idea
22
-
23
- One root unit derives everything else. No breakpoints, no orientation
24
- query, no hand-picked column counts.
25
-
26
- ```text
27
- --fs (fluid font-size)
28
- → line-height (rounded to px)
29
- → --unit: 1rlh
30
- → --edge-min, --lpf, --measure-min
31
- → --lines, --fields, --type-b, --slack
32
- → padding-block, grid tracks, gap
33
- ```
34
-
35
- Change `--fs`'s clamp and the whole grid — margins, field height, column
36
- count threshold — rescales in step. The grid has no size values of its
37
- own, only ratios against `--unit`. This is CSS-only: no JS computes grid
38
- math at runtime.
39
-
40
- ## §2 Scope decisions (from brainstorming)
41
-
42
- - **Clean break.** No `colStart`/`colSpan`/`rowStart`/`rowSpan` numbered
43
- placement API. Auto-fill's native column resolution
44
- (`floor((available + gutter) / (measure-min + gutter))`) is the only
45
- column-count mechanism — no mathematically-generated `--cols` ladder,
46
- since a `calc()`/`round()`-derived integer fed into `repeat()`'s
47
- track-count position is the exact failure mode Pitfall 1 (§7) already
48
- proved broken in this codebase.
49
- - **Layout owns `--fs`/`line-height`.** Matches the repo's existing
50
- convention (CLAUDE.md's open item, option 1) — layout owns the one
51
- clamp that feeds the grid; typography layer owns everything else
52
- (font-family, weight scale, prose styles, headings outside grid cells).
53
- - **Per-section row derivation, restored.** Not a flat, purely continuous
54
- page (see §4) — the field/line math runs per `LayoutSection`, not once
55
- globally per page.
56
- - **CSS `@property` typing, not TS.** Every derived custom property gets a
57
- real `syntax` descriptor. No TypeScript interface for the JS→CSS var
58
- bridge (config tunables → `:style`) — out of scope.
59
- - **Unrelated components untouched.** `HStack`/`VStack`/`ZStack`/`Spacer`,
60
- `LayoutPage`/`LayoutPageHeader`, the z-index layer system
61
- (`useZIndex`/`GridLayers`), and the `⌘G` debug overlay carry forward —
62
- rebuilt only where they touch grid mechanics.
63
- - **No consumer migration in this task.** `layers/content`,
64
- `layers/structure/navigation`, and the playground/starter apps keep
65
- calling the old API until a separate pass updates them. This task's
66
- deliverable includes a migration guide document, not the migrations
67
- themselves.
68
-
69
- ## §3 Derivation, step by step
70
-
71
- Ported from the validated prototype (`swiss-test/src/layout.css`):
72
-
73
- 1. **Type.** `--fs: clamp(1rem, 0.95rem + 0.25svi, 1.125rem)`, registered
74
- via `@property` as `<length>` so downstream `calc()` gets a resolved
75
- length, not an unresolved token stream. `line-height` is
76
- `round(nearest, fs * 1.5, 1px)` — rounded, not raw, so grid boundaries
77
- land on-pixel.
78
- 2. **Unit.** `--unit: 1rlh`. `rlh` not `lh`: `lh` resolves against the
79
- _current_ element, so a heading with a different line-height would
80
- silently produce a different unit for anything nested in it. `rlh`
81
- always resolves against root.
82
- 3. **Minimums.**
83
- - `--measure-min: 22rem` — narrowest column, also the
84
- `repeat(auto-fill, minmax(...))` floor and the `@container cell`
85
- padding-step threshold.
86
- - `--edge-min: clamp(1unit, 8svi, 3unit)` — margin floor > 0, steep
87
- `svi` slope so it shrinks fast on narrow screens. Feeds `--avail-b`,
88
- so it also trims top/bottom slack, not just inline edges.
89
- 4. **Lines** (per-section, see §4). `--avail-b: 100svh - 2*edge-min`.
90
- `--lines: round(down, avail-b, unit) / unit` — the only way to get an
91
- integer line-count out of a length in CSS.
92
- 5. **Fields** (per-section). `--lpf` (lines-per-field) clamped 4–8, scaled
93
- by viewport height (`round(down, 100svh/unit/4.5, 1)`) so a fixed
94
- `lpf=8` can't strand up to 8 line-heights as dead padding on a short
95
- viewport. `--fields: max(1, round(down, (lines+1)/(lpf+1), 1))`.
96
- 6. **Margins** (per-section). Whatever's left over, split by the
97
- section's `align` prop (§4) — computed last because it can't be
98
- chosen, only what remains after the whole-line constraint.
99
- `--slack: 100svh - type-b`, clamped `max(0px, …)` on both sides so a
100
- short/landscape viewport degrades to scroll instead of negative
101
- padding.
102
- 7. **Columns.** `repeat(auto-fill, minmax(measure-min, 1fr))` on the root
103
- grid only. Native `auto-fill` computes column count from available
104
- space directly, without needing an integer derived via `calc()`.
105
-
106
- ## §4 Section model
107
-
108
- The root (`LayoutMain`) is a continuous auto-fill grid — content not
109
- wrapped in a `LayoutSection` flows and wraps normally, sized only by
110
- `--measure-min`/`--unit`, no viewport-height chunking.
111
-
112
- `LayoutSection` is an **opt-in full-width row**:
113
-
114
- - `grid-column: 1 / -1`, `grid-template-columns: subgrid` — inherits
115
- however many explicit columns the root's `auto-fill` resolved. This is
116
- legal: subgrid adopts the parent's resolved explicit track count
117
- whatever it is; it only breaks when something tries to _address_ a
118
- column by number, which nothing in this design does. A `.span-2` class
119
- inside a subgridded section asks for "2 of however many columns exist,"
120
- the same trick the prototype's own `.span-2` container-query gate uses
121
- (§6) — it works identically whether the parent's tracks came from a
122
- fixed count or from `auto-fill`.
123
- - Block-size defaults to `100svh` (overridable via a `height` prop), and
124
- it runs its own §3 steps 4–6 scoped to itself — same global
125
- `--unit`/`--edge-min`/`--lpf` from `:root`, applied per-section instead
126
- of once per-page.
127
- - `align` prop: `'split'` (the prototype's default 40/60 optical-center
128
- padding split) or `'center'` (`place-content: center` — true
129
- horizontal+vertical centering). Explicit choice, not a fight against
130
- `align-items: stretch` defaults — this directly addresses the prior
131
- centering pain: the old `LayoutSection`/`GridItem` combo had no
132
- first-class centering prop, so centering meant fighting
133
- `grid-auto-rows`'s fixed track height and the container's default
134
- stretch behavior.
135
-
136
- ## §5 Components
137
-
138
- ```text
139
- layers/structure/layout/app/components/
140
- ├── HStack.vue / VStack.vue / ZStack.vue / Spacer.vue # unchanged
141
- └── Layout/
142
- ├── Main.vue # rebuilt: root auto-fill grid, --fs/--unit owner
143
- ├── Section.vue # rebuilt: opt-in full-width subgrid row, §4
144
- ├── Cell.vue # new, replaces Grid/Item.vue
145
- ├── Hero.vue # new, replaces the `hero` GridItem preset
146
- ├── Grid/Debug.vue # rebuilt: visualizes --unit/field boundaries
147
- └── Page/
148
- ├── index.vue # unchanged
149
- └── Header.vue # unchanged
150
- ```
151
-
152
- - **`LayoutMain`** — kept name (minimizes churn for whatever later
153
- migrates to it), rebuilt internals. Renders `<main>` as the root
154
- auto-fill grid. Registers `--fs`, binds config tunables to CSS custom
155
- properties via `:style`.
156
- - **`LayoutSection`** — see §4.
157
- - **`LayoutCell`** (replaces `LayoutGridItem`) — `variant: 'block' |
158
- 'plate'` prop (border vs. filled, matching the prototype's `.block`/
159
- `.plate`), `span2` / `span2Rows` / `span3Rows` boolean/enum props. No
160
- `colStart`/`rowStart`/`preset`/`layer`/`bleed` — those go in the
161
- migration guide as removed, with the nearest equivalent noted.
162
- - **`LayoutHero`** (replaces the `hero` `GridItem` preset) — standalone,
163
- full-viewport (`100svh`) flex-centered, deliberately outside the grid
164
- entirely (no `--fields`, no column derivation) — matches the
165
- prototype's `.hero`.
166
- - **`LayoutGridDebug`** — same `⌘⇧G` shortcut, rewritten to overlay
167
- `--unit`/field boundaries (via `getComputedStyle`) instead of a fixed
168
- column count, since column count is now emergent.
169
- - `Layout/Grid/Item.vue` and `Layout/Section/{Gallery,Hero,Split}.vue` —
170
- deleted; superseded by `Cell`/`Hero`/`Section`.
171
-
172
- ## §6 CSS `@property` registration
173
-
174
- Every derived custom property gets a `syntax` descriptor, extending the
175
- prototype's existing `--fs` registration:
176
-
177
- | Property | `syntax` |
178
- | ---------------------------------- | ----------- |
179
- | `--fs` | `<length>` |
180
- | `--unit` | `<length>` |
181
- | `--edge-min` | `<length>` |
182
- | `--measure-min` | `<length>` |
183
- | `--lpf` | `<integer>` |
184
- | `--lines` | `<integer>` |
185
- | `--fields` | `<integer>` |
186
- | `--avail-b`, `--type-b`, `--slack` | `<length>` |
187
-
188
- Registration guards against Pitfall 1's failure mode (§7) as new CSS is
189
- added later: an unregistered custom property built from `calc()`/
190
- `round()` silently substitutes as a token stream instead of a resolved
191
- value at points that require one, invalidating the whole declaration and
192
- falling back to implicit auto-sized tracks — a squash bug that's invisible
193
- to `pnpm build` and only catchable via real browser `getComputedStyle()`
194
- inspection.
195
-
196
- ## §7 Pitfalls carried forward (verified in the prototype, must not regress)
197
-
198
- 1. **`repeat(var(--n), ...)` silently invalidates.** `repeat()`'s
199
- track-count argument requires a literal `<integer>` at computed-value
200
- time — an unregistered `calc()`/`round()`-derived custom property
201
- doesn't satisfy that. Fix: `repeat(auto-fill, minmax(<length>, 1fr))`,
202
- never a `var()`-derived count. This is _why_ §2 rejects a mathematical
203
- `--cols` ladder for v1.
204
- 2. **Span utilities forcing malformed implicit tracks.** `span-2`
205
- unconditionally applying `grid-column: span 2` can force CSS Grid to
206
- fabricate a content-sized implicit column when only 1 explicit column
207
- resolved (narrow/medium viewport), squashing siblings. Fix:
208
- `grid-auto-columns: minmax(measure-min, 1fr)` (belt-and-braces) plus
209
- gating `.span-2` behind `@container viewport (min-width: 2 *
210
- measure-min + gutter)` so it only activates once 2 columns have
211
- genuinely resolved.
212
- 3. **Hand-synced container-query thresholds.** `@container` conditions
213
- can't reference custom properties — the `@container cell (max-width:
214
- 22rem)` / `@container viewport (min-width: 46rem)` thresholds must be
215
- hand-kept in sync with `--measure-min`, commented at both ends.
216
-
217
- ## §8 Constraints to preserve
218
-
219
- - Every fluid `clamp()` keeps a `rem` term — a pure `vw`/`cqi` middle term
220
- ignores browser zoom, fails WCAG 1.4.4.
221
- - Never `line-height: normal` on `:root` — font-metric dependent, whole
222
- grid would reflow on webfont swap.
223
- - `svh`/`svi`, never `dvh`/`vw`/`vh` — `dvh` recomputes on mobile URL-bar
224
- collapse, reflowing mid-scroll.
225
- - `lh`/`rlh` don't resolve correctly in media queries (resolve against
226
- initial font there) — use `rem`, or a container query with `cqi`.
227
- - `container-type` never on `body` (becomes the `position: fixed`
228
- containing block for descendants — an unrelated footgun).
229
-
230
- ## §9 Config
231
-
232
- `app.config.ts`:
233
-
234
- ```ts
235
- layoutLayer: {
236
- ui: {
237
- grid: {
238
- mode: 'fluid' | 'disabled'
239
- tunables: {
240
- measureMin: string // e.g. '22rem'
241
- edgeMin: {
242
- min: string
243
- max: string
244
- } // clamp bounds
245
- lpf: {
246
- min: number
247
- max: number
248
- } // lines-per-field bounds
249
- }
250
- layers: GridLayers // z-index system, unchanged
251
- }
252
- }
253
- }
254
- ```
255
-
256
- `useGridConfig()`:
257
-
258
- ```ts
259
- const { config, mode, isEnabled, layers, useZIndex, cssVars } = useGridConfig()
260
- ```
261
-
262
- - `getPreset` — removed (no addressable placement left to preset).
263
- - `cssVars` — new: computed map of tunables → CSS custom property
264
- strings, consumed by `LayoutMain`/`LayoutSection`'s `:style` binding.
265
- This is the JS→CSS bridge; per §2, it carries no dedicated TS interface
266
- beyond the existing `GridConfig` type in `layouts.ts`.
267
- - `mode`/`isEnabled`/`layers`/`useZIndex` — unchanged behavior.
268
- `'disabled'` mode renders a plain `<main>`, no grid CSS, same fallback
269
- as today.
270
-
271
- ## §10 Testing
272
-
273
- - `gridPlacementStyle.ts`/`.test.ts` — deleted. Placement is static CSS
274
- classes now (`span2`, etc.); there's no positioning math left to unit
275
- test.
276
- - New: a small test for `useGridConfig`'s `cssVars` mapping and
277
- `mode`/`isEnabled` resolution (mirrors the existing composable test
278
- shape).
279
- - Manual verification: `getComputedStyle` sweep at 500 / 768 / 900 /
280
- 1024 / 1440 / 1920px, checking `gridTemplateColumns`/`gridAutoRows`
281
- against expectation at each width, and that `.span-2`/`--fields`
282
- thresholds gate correctly — the same method that caught Pitfalls 1–2 in
283
- the prototype, since computed-value grid bugs are invisible to
284
- `pnpm build`'s pure syntax check.
285
- - Playground: existing `apps/playground/app/pages/layout.vue` demo
286
- rebuilt against the new components — visual check of continuous grid,
287
- section centering (both `align` modes), hero, debug overlay.
288
-
289
- ## §11 Deliverables
290
-
291
- 1. Rebuilt `layers/structure/layout` per §5–§9.
292
- 2. `docs/LAYOUT.md` rewritten to describe the new system (supersedes the
293
- 6/12/18 breakpoint documentation).
294
- 3. A migration guide document (old → new API mapping: removed props,
295
- nearest equivalents, config shape changes) for later use when
296
- `layers/content`, `layers/structure/navigation`, and the
297
- playground/starter apps are updated. Not implemented as part of this
298
- task.
299
- 4. §0's spec-conflict reconciliation resolved before implementation
300
- starts.
301
-
302
- ## §12 Not building
303
-
304
- - No numbered column/row placement API (`colStart`, `rowStart`).
305
- - No mathematically-generated `--cols` (§2, §7 pitfall 1).
306
- - No consumer migration (content layer, navigation, playground/starter
307
- call sites) — guide only.
308
- - No TypeScript interface for the CSS var bridge — `@property` is the
309
- only typing layer (§2, §6).
310
- - No changes to `HStack`/`VStack`/`ZStack`/`Spacer`, `LayoutPage`/
311
- `LayoutPageHeader`, or the z-index layer system beyond what's needed to
312
- keep them working against the rebuilt `LayoutMain`.