@adia-ai/web-modules 0.6.11 → 0.6.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog — @adia-ai/web-modules
2
2
 
3
+ ## [0.6.13] — 2026-05-20
4
+
5
+ ### Changed
6
+ - **`<admin-shell>` — default `mode` is now `"rounded borderless"` (was flat-bordered).** A bare `<admin-shell>` with no `mode` attribute auto-promotes to `mode="rounded borderless"` in `connectedCallback` — the modern soft-chrome visual baseline becomes opt-out, not opt-in. Any author-supplied `mode` attribute (including the empty string `mode=""` for the flat legacy chrome) is respected verbatim: the default fires only when no `mode` attribute is present on the tag at all. The behavior change is scoped to consumers emitting a bare `<admin-shell>` — every in-repo composition surface and every audited external consumer except claims-ui-v3 already set `mode="rounded borderless"` explicitly. Opt-out matrix: `<admin-shell>` → `rounded borderless`; `<admin-shell mode="">` → `""`; `<admin-shell mode="rounded">` → `rounded`. The `props.mode` schema also drops its (incorrect) `enum: ["", rounded, borderless]` constraint — `mode` is a space-separated token set consumed by CSS `mode~="<token>"` attribute selectors, so the `.d.ts` `mode` type widens from `'' | 'rounded' | 'borderless'` to `string` (TS consumers can now assign `el.mode = "rounded borderless"` without a cast).
7
+
8
+ ## [0.6.12] — 2026-05-20
9
+
10
+ ### Cleanup (no in-tree or audited external consumer impact)
11
+ - **Legacy shell-shape CSS bridges retired.** Per ADR-0032 (continuation of ADR-0023 + ADR-0024), the `:is(legacy, bespoke)` selector lists in `admin-shell.main.css`, `admin-shell.templates.css`, `admin-shell.collapsed.css`, and `admin-shell.shell.css` have been stripped to bespoke-only. Standalone `[data-content-root]` / `[data-content-header]` / `[data-content-body]` / `[data-content-footer]` rules in `admin-shell.templates.css` are gone. ADR-0024 retired the legacy JS-level reads in v0.4.0; the CSS bridges remained as a soft-deprecation through v0.6.x, and the v0.6.12 dogfooding sweep eliminated the last in-tree consumers. External-consumer audit at the v0.6.12 cut found zero usage of the legacy shape under `<admin-shell>` across the 4 known external projects (`claims-ui-v3`, `claims-ui-v2`, `claims-ui`, `color-app`) — patch-cut safe. Anyone outside that audit emitting `<main><header-ui>…<article data-content-root><div data-content-header>…</div></article></section-ui></main>` under `<admin-shell>` will need to migrate to the canonical 10-tag bespoke composition: `<admin-shell><admin-sidebar slot="leading">…</admin-sidebar><admin-content><admin-topbar>…</admin-topbar><admin-scroll><admin-page><admin-page-header><header>…</header></admin-page-header><admin-page-body><section>…</section></admin-page-body></admin-page></admin-scroll><admin-statusbar>…</admin-statusbar></admin-content></admin-shell>`. A future `adia-ui-kit` skill cut will ship an AST codemod for static-HTML migration.
12
+
13
+ ### Substrate completion
14
+ - `admin-shell.bespoke.css` — completed missing slot-vocab properties on `:is(admin-topbar, admin-statusbar) > [slot="action"]` (added `display:flex`, `gap`, `align-items`, `flex-shrink` to handle multi-child action clusters). Added `scrollbar-width: none` to `<admin-scroll>`. Added `min-height: 0; width: 100%; box-sizing: border-box` to `<admin-page-body>`. Without these completions, stripping the legacy main.css rules would have lost cluster layout in multi-child topbar/statusbar slots.
15
+
16
+ ### Verification
17
+ - `verify:no-legacy-shell-shapes` regression guard exits 0 (added + wired into omnibus `check` in v0.6.12).
18
+ - Playground + site geometry pixel-identical pre/post strip across all 232 doc-template routes.
19
+ - `smoke:consumers` 6/6, `smoke:engines` green, `smoke:register-engine` 11/11, `verify:traits` 56/56 clean, `eval:diff --engine zettel` cov=49% / avg=90 (baseline-identical).
20
+ - 4 external consumer projects (`claims-ui-v3`, `claims-ui-v2`, `claims-ui`, `color-app`) audited — zero legacy-shape usage, no migration debt below the lockstep.
21
+
3
22
  ## [0.6.11] — 2026-05-20
4
23
 
5
24
  ### Maintenance
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/web-modules",
3
- "version": "0.6.11",
3
+ "version": "0.6.13",
4
4
  "description": "AdiaUI composite custom elements \u2014 shell, chat, editor, runtime clusters built from @adia-ai/web-components primitives. Subpath exports per cluster.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -17,14 +17,9 @@
17
17
  "const": "AppShell"
18
18
  },
19
19
  "mode": {
20
- "description": "Layout variant — default is bordered surface; rounded softens edges; borderless removes the outer chrome.",
20
+ "description": "Layout variant — space-separated token set. Recognized tokens:\n rounded softens content-surface edges (border-radius from --page-content-radius)\n borderless removes outer chrome borders (content dividers persist)\nThe CSS uses `mode~=\"<token>\"` attribute selectors, so multiple\ntokens combine additively (\"rounded borderless\" = both).\n\nDefault since v0.6.13: \"rounded borderless\". A bare `<admin-shell>`\nwith no `mode` attribute auto-promotes to this pair on connect.\nAuthor-supplied `mode` attributes (including the empty string\n`mode=\"\"` for flat bordered chrome) are respected verbatim.\n",
21
21
  "type": "string",
22
- "enum": [
23
- "",
24
- "rounded",
25
- "borderless"
26
- ],
27
- "default": ""
22
+ "default": "rounded borderless"
28
23
  }
29
24
  },
30
25
  "required": [
@@ -35,8 +35,18 @@ export interface AdminShellSidebarToggleEventDetail {
35
35
  export type AdminShellSidebarToggleEvent = CustomEvent<AdminShellSidebarToggleEventDetail>;
36
36
 
37
37
  export class AdminShell extends UIElement {
38
- /** Layout variant — default is bordered surface; rounded softens edges; borderless removes the outer chrome. */
39
- mode: '' | 'rounded' | 'borderless';
38
+ /** Layout variant — space-separated token set. Recognized tokens:
39
+ rounded — softens content-surface edges (border-radius from --page-content-radius)
40
+ borderless — removes outer chrome borders (content dividers persist)
41
+ The CSS uses `mode~="<token>"` attribute selectors, so multiple
42
+ tokens combine additively ("rounded borderless" = both).
43
+
44
+ Default since v0.6.13: "rounded borderless". A bare `<admin-shell>`
45
+ with no `mode` attribute auto-promotes to this pair on connect.
46
+ Author-supplied `mode` attributes (including the empty string
47
+ `mode=""` for flat bordered chrome) are respected verbatim.
48
+ */
49
+ mode: string;
40
50
 
41
51
  addEventListener<K extends keyof HTMLElementEventMap>(
42
52
  type: K,
@@ -17,7 +17,12 @@
17
17
  * compat reads; see git history for the legacy paths.
18
18
  *
19
19
  * Host responsibilities (the only behavior that stays here):
20
- * 1. [mode] reflection — "rounded", "borderless", or both
20
+ * 1. [mode] reflection — "rounded", "borderless", or both. **Default
21
+ * since v0.6.13: "rounded borderless"**. Authors who want the flat
22
+ * legacy chrome opt out via an explicit `mode=""` or `mode="rounded"`
23
+ * (etc.); any author-supplied `mode` attribute (including the empty
24
+ * string) is respected verbatim, so the default only fires when
25
+ * no `mode` attribute is present at all on the tag.
21
26
  * 2. [data-sidebar-toggle="<name>"] click forwarding to the matching
22
27
  * <admin-sidebar>'s .toggle() public method
23
28
  * 3. [data-command-trigger] click forwarding to <admin-command>.show()
@@ -26,6 +31,8 @@
26
31
 
27
32
  import { UIElement } from '../../../web-components/core/element.js';
28
33
 
34
+ const DEFAULT_MODE = 'rounded borderless';
35
+
29
36
  class AdminShell extends UIElement {
30
37
  static properties = {
31
38
  mode: { type: String, default: '', reflect: true },
@@ -34,6 +41,11 @@ class AdminShell extends UIElement {
34
41
  static template = () => null;
35
42
 
36
43
  connected() {
44
+ // Apply the v0.6.13 default mode if (and only if) the author did
45
+ // not supply a `mode` attribute on the tag. `hasAttribute` returns
46
+ // true for `mode=""` so explicit empty-string authoring (the flat
47
+ // legacy chrome opt-out) is preserved.
48
+ if (!this.hasAttribute('mode')) this.mode = DEFAULT_MODE;
37
49
  this.#wireToggleButtons();
38
50
  this.#wireCommandTriggers();
39
51
  }
@@ -13,10 +13,19 @@ description: |
13
13
  props:
14
14
  mode:
15
15
  type: string
16
- default: ""
17
- enum: ["", rounded, borderless]
16
+ default: "rounded borderless"
18
17
  reflect: true
19
- description: Layout variant — default is bordered surface; rounded softens edges; borderless removes the outer chrome.
18
+ description: |
19
+ Layout variant — space-separated token set. Recognized tokens:
20
+ rounded — softens content-surface edges (border-radius from --page-content-radius)
21
+ borderless — removes outer chrome borders (content dividers persist)
22
+ The CSS uses `mode~="<token>"` attribute selectors, so multiple
23
+ tokens combine additively ("rounded borderless" = both).
24
+
25
+ Default since v0.6.13: "rounded borderless". A bare `<admin-shell>`
26
+ with no `mode` attribute auto-promotes to this pair on connect.
27
+ Author-supplied `mode` attributes (including the empty string
28
+ `mode=""` for flat bordered chrome) are respected verbatim.
20
29
 
21
30
  events:
22
31
  sidebar-toggle:
@@ -1,16 +1,20 @@
1
1
  /* ═══════════════════════════════════════════════════════════════
2
2
  admin-shell — Bespoke shell-tier children (canonical since v0.4.0)
3
3
 
4
- Per ADR-0023 + ADR-0024 (Phase 3 deprecation, v0.4.0), this file
5
- carries the canonical styling for the bespoke shell-tier children:
4
+ Per ADR-0023 + ADR-0024 (host-element legacy reads stripped v0.4.0)
5
+ + ADR-0032 (CSS bridges stripped v0.6.12), this file carries the
6
+ canonical styling for the bespoke shell-tier children:
6
7
  admin-content, admin-topbar, admin-statusbar, admin-scroll,
7
8
  admin-page, admin-page-header, admin-page-body, admin-sidebar.
8
9
 
9
10
  The legacy raw-HTML shape (<main>, <header>, <footer>, <aside-ui slot>,
10
- <aside data-sidebar>, <dialog data-command>) was retired in v0.4.0;
11
- the existing :is() lifts in layered files (main.css, sidebar.css,
12
- templates.css) target ONLY the bespoke tags now. See git history
13
- prior to v0.4.0 for the legacy paths.
11
+ <aside data-sidebar>, <dialog data-command>, [data-content-root],
12
+ [data-content-header], [data-content-body], [data-content-footer])
13
+ was the v0.3.x authoring contract. JS-level reads were retired in
14
+ v0.4.0; CSS bridges in main.css, templates.css, collapsed.css and
15
+ site/site.css were retired in v0.6.12 (see ADR-0032). All :is() lifts
16
+ in layered files now target ONLY the bespoke tags. See git history
17
+ prior to v0.6.12 for the legacy paths.
14
18
  ═══════════════════════════════════════════════════════════════ */
15
19
 
16
20
  /* ── admin-content ≡ <main> ── */
@@ -58,6 +62,7 @@ admin-content > admin-scroll {
58
62
  min-height: 0;
59
63
  overflow-y: auto;
60
64
  overscroll-behavior: contain;
65
+ scrollbar-width: none;
61
66
  background: var(--page-content-bg);
62
67
  border-inline: var(--page-content-border);
63
68
  box-shadow: var(--page-content-shadow);
@@ -90,6 +95,9 @@ admin-page > admin-page-header {
90
95
  /* ── admin-page-body ≡ <[data-content-body]> centered body ── */
91
96
  admin-page > admin-page-body {
92
97
  flex: 1;
98
+ min-height: 0;
99
+ width: 100%;
100
+ box-sizing: border-box;
93
101
  display: flex;
94
102
  flex-direction: column;
95
103
  }
@@ -192,3 +200,27 @@ admin-sidebar[collapsed] {
192
200
  color: var(--page-header-fg-muted);
193
201
  font-size: var(--a-ui-sm);
194
202
  }
203
+
204
+ /* Multi-child action clusters: turn the slot into a flex row so nested
205
+ buttons gap correctly. Legacy <main> > <header> > [slot="action"]
206
+ got this from main.css; the equivalent for the bespoke topbar/
207
+ statusbar lives here. */
208
+ :is(admin-topbar, admin-statusbar) > [slot="action"],
209
+ :is(admin-topbar, admin-statusbar) > [slot="action-leading"] {
210
+ display: flex;
211
+ align-items: center;
212
+ gap: var(--page-actions-gap);
213
+ flex-shrink: 0;
214
+ }
215
+
216
+ /* Second + Nth [slot="action"] children: reset the auto-push from
217
+ :first-of-type so siblings sit flush against the first cluster. */
218
+ :is(admin-topbar, admin-statusbar) > [slot="action"] ~ [slot="action"] {
219
+ margin-inline-start: 0;
220
+ }
221
+
222
+ /* Slot icon: flex inner alignment (legacy parity). */
223
+ :is(admin-topbar, admin-statusbar) > [slot="icon"] {
224
+ display: flex;
225
+ align-items: center;
226
+ }
@@ -13,7 +13,7 @@
13
13
 
14
14
  @container sidebar (max-width: 96px) {
15
15
  /* Center header/footer content */
16
- :is(header, header-ui, footer, footer-ui) {
16
+ :is(admin-topbar, admin-statusbar) {
17
17
  justify-content: center;
18
18
  padding: var(--page-sidebar-px);
19
19
  }
@@ -1,143 +1,67 @@
1
1
  /* ═══════════════════════════════════════════════════════════════
2
- App Shell — Main column (topbar + scroll + footer)
3
-
4
- Authoring shapes (both styled identically):
5
- - Legacy: <main><header>…</header><section>…</section><footer>…</footer></main>
6
- - Slot vocabulary: <main><header-ui>…</header-ui><section-ui>…</section-ui><footer-ui>…</footer-ui></main>
7
-
8
- <main> stays as the main column container in both shapes. Slot
9
- primitives (header-ui / section-ui / footer-ui) are accepted as
10
- the chrome children alongside their raw-HTML counterparts.
2
+ App Shell — Main column layout helpers
3
+
4
+ Post-ADR-0032 (v0.6.12): the legacy <main> / <header-ui> / <section-ui>
5
+ / <footer-ui> selector lifts that lived here have been retired. The
6
+ canonical bespoke <admin-content> / <admin-topbar> / <admin-scroll> /
7
+ <admin-statusbar> geometry + slot vocabulary live in
8
+ admin-shell.bespoke.css.
9
+
10
+ What stays in this file:
11
+ • Edge-breathing-room :has() probes — they're cross-cutting (they
12
+ inspect both admin-shell-level chrome AND admin-content-level
13
+ chrome), so they don't belong in bespoke.css's pure-tag-keyed
14
+ rule blocks.
15
+ • <admin-scroll> WebKit scrollbar suppression sibling rule.
16
+ • Subnav rail grid layout — descendant-of-admin-scroll behavior
17
+ that's authored alongside the breathing-room logic.
11
18
  ═══════════════════════════════════════════════════════════════ */
12
19
 
13
- /* ── Main column ── */
14
- admin-shell > main {
15
- display: flex;
16
- flex-direction: column;
17
- flex: 1;
18
- min-width: 0;
19
- min-height: 0;
20
- border-inline: var(--page-main-border);
21
- }
22
-
23
20
  /* ── Edge breathing room when adjacent chrome is absent ──
24
21
  When the shell has no visible chrome on a given edge of the content
25
- surface, `> main > :is(section, section-ui)` owns a small inset
26
- against that edge so the rounded/elevated surface doesn't sit flush
27
- with the viewport. The `:has(... :not([hidden]))` probe catches
28
- both "element missing from the DOM" AND "element present but
29
- [hidden]" — toggling [hidden] flips the inset live with no JS
30
- coordination. Scope is the section, not main, so the topbar /
31
- statusbar inside main keep spanning full-width even when the
32
- section gets its inset.
33
-
34
- Block-edge probes accept either an app-level chrome bar
35
- (`admin-shell > :is(header, header-ui)` / `> :is(footer, footer-ui)`)
36
- OR a main-level chrome bar (`> main > :is(header, header-ui)` etc.).
37
- Either qualifies as "chrome present" and suppresses the
38
- corresponding margin. */
39
- admin-shell:not(:has(> :is(asideadmin-sidebar[slot="trailing"], admin-sidebar[slot="trailing"]):not([hidden]))) > main > :is(section, section-ui) {
22
+ surface, `<admin-scroll>` owns a small inset against that edge so
23
+ the rounded/elevated surface doesn't sit flush with the viewport.
24
+ The `:has(... :not([hidden]))` probe catches both "element missing
25
+ from the DOM" AND "element present but [hidden]" — toggling [hidden]
26
+ flips the inset live with no JS coordination. Scope is the scroll
27
+ container, not admin-content, so the topbar / statusbar inside
28
+ admin-content keep spanning full-width even when the scroll surface
29
+ gets its inset. */
30
+ admin-shell:not(:has(> :is(asideadmin-sidebar[slot="trailing"], admin-sidebar[slot="trailing"]):not([hidden]))) > admin-content > admin-scroll {
40
31
  margin-inline-end: var(--a-space-2);
41
32
  }
42
- admin-shell:not(:has(> :is(header, header-ui):not([hidden]), > main > :is(header, header-ui):not([hidden]))) > main > :is(section, section-ui) {
33
+ admin-shell:not(:has(> :is(header, header-ui, admin-topbar):not([hidden]), > admin-content > admin-topbar:not([hidden]))) > admin-content > admin-scroll {
43
34
  margin-block-start: var(--a-space-2);
44
35
  }
45
- admin-shell:not(:has(> :is(footer, footer-ui):not([hidden]), > main > :is(footer, footer-ui):not([hidden]))) > main > :is(section, section-ui) {
36
+ admin-shell:not(:has(> :is(footer, footer-ui, admin-statusbar):not([hidden]), > admin-content > admin-statusbar:not([hidden]))) > admin-content > admin-scroll {
46
37
  margin-block-end: var(--a-space-2);
47
38
  }
48
39
 
49
- /* ── Main > header (topbar) ──
50
- Contains: sidebar toggle, breadcrumb, spacer, action buttons.
51
-
52
- Slot contract (shared with > main > footer and admin-sidebar >
53
- header/footer) identical to card-ui / drawer-ui / editor-shell:
54
- [slot="icon"] leading glyph
55
- [slot="heading"] primary label; strong weight + strong fg
56
- [slot="description"] secondary metadata; muted fg + --a-ui-sm
57
- [slot="action"] trailing cluster; first pushes to end
58
- The legacy data-sidebar-toggle / breadcrumb-ui / <span data-spacer>
59
- / <div data-actions> hooks (see app-shell.helpers.css) remain
60
- fully supported — slots are additive, not a replacement. Use slots
61
- for simpler chrome surfaces; keep breadcrumb + data-actions for
62
- docs-style shells where those conventions carry semantic weight. */
63
- admin-shell > main > :is(header, header-ui) {
64
- display: flex;
65
- align-items: center;
66
- gap: var(--page-header-gap);
67
- min-height: var(--page-header-height);
68
- padding: 0 var(--page-header-px);
69
- border-bottom: var(--page-border);
70
- font-size: var(--page-header-font);
71
- flex-shrink: 0;
72
- }
73
-
74
- admin-shell > main > :is(header, header-ui) > [slot="icon"] {
75
- display: flex;
76
- align-items: center;
77
- flex-shrink: 0;
78
- color: var(--page-header-fg-muted);
79
- }
80
- admin-shell > main > :is(header, header-ui) > [slot="heading"] {
81
- font-weight: var(--a-weight-medium);
82
- color: var(--a-fg);
83
- }
84
- admin-shell > main > :is(header, header-ui) > [slot="description"] {
85
- color: var(--page-header-fg-muted);
86
- font-size: var(--a-ui-sm);
87
- }
88
- admin-shell > main > :is(header, header-ui) > [slot="action"] {
89
- display: flex;
90
- align-items: center;
91
- gap: var(--page-actions-gap);
92
- flex-shrink: 0;
93
- margin-inline-start: auto;
94
- }
95
- admin-shell > main > :is(header, header-ui) > [slot="action"] ~ [slot="action"] {
96
- margin-inline-start: 0;
97
- }
98
- /* Dual-cluster: leading group on inline-start, trailing cluster on inline-end. */
99
- admin-shell > main > :is(header, header-ui) > [slot="action-leading"] {
100
- display: flex;
101
- align-items: center;
102
- gap: var(--page-actions-gap);
103
- flex-shrink: 0;
104
- margin-inline-end: auto;
105
- }
106
-
107
- /* ── Main > section (scroll container) ──
108
- Wraps [data-content-root]. Scrolls vertically, hides scrollbar.
109
- Soft elevation via --page-content-shadow (defaults to --a-shadow-md)
110
- so the content surface lifts off the chrome canvas regardless of
111
- mode — pairs with --page-content-radius under "rounded" and stays
112
- visible under "borderless". */
113
- admin-shell > main > :is(section, section-ui) {
114
- flex: 1;
115
- min-height: 0;
116
- overflow-y: auto;
117
- overscroll-behavior: contain;
118
- scrollbar-width: none;
119
- background: var(--page-content-bg);
120
- box-shadow: var(--page-content-shadow);
121
- }
122
-
123
- admin-shell > main > :is(section, section-ui)::-webkit-scrollbar { display: none; }
40
+ /* ── Scroll container ──
41
+ Canonical styling for `<admin-scroll>` (flex, scrollbar suppression,
42
+ background, content shadow) lives in admin-shell.bespoke.css.
43
+ --page-content-shadow defaults to --a-shadow-md so the content
44
+ surface lifts off the chrome canvas regardless of mode — pairs with
45
+ --page-content-radius under "rounded" and stays visible under
46
+ "borderless". */
47
+ admin-shell > admin-content > admin-scroll::-webkit-scrollbar { display: none; }
124
48
 
125
49
  /* ── Subnav rail layout ──
126
- When the main section contains an `[data-subnav]` aside (the
127
- left-rail nav pattern — `<aside data-subnav>` followed by the
128
- `[data-content-root]` article), grid-layout the section so the
129
- rail sits on the inline-start side and the content fills the
130
- rest. Mirrors the docs-site convention (site.css `:has(#subnav-rail)`)
131
- so consumer apps using admin-shell + the subnav-rail authoring
132
- shape get the canonical layout for free. */
133
- admin-shell > main > :is(section, section-ui):has(> [data-subnav]:not([hidden])) {
50
+ When admin-scroll contains an `[data-subnav]` aside (the left-rail
51
+ nav pattern — `<aside data-subnav>` followed by the `<admin-page>`
52
+ article), grid-layout the scroll container so the rail sits on the
53
+ inline-start side and the content fills the rest. Mirrors the
54
+ docs-site convention (site.css `:has(#subnav-rail)`) so consumer
55
+ apps using admin-shell + the subnav-rail authoring shape get the
56
+ canonical layout for free. */
57
+ admin-shell > admin-content > admin-scroll:has(> [data-subnav]:not([hidden])) {
134
58
  display: grid;
135
59
  grid-template-columns: var(--subnav-width, 14rem) 1fr;
136
60
  grid-template-rows: minmax(0, 1fr);
137
61
  gap: 0;
138
62
  }
139
63
 
140
- admin-shell > main > :is(section, section-ui) > [data-subnav] {
64
+ admin-shell > admin-content > admin-scroll > [data-subnav] {
141
65
  min-height: 0;
142
66
  overflow-y: auto;
143
67
  overscroll-behavior: contain;
@@ -148,70 +72,11 @@ admin-shell > main > :is(section, section-ui) > [data-subnav] {
148
72
 
149
73
  /* Mobile: collapse to single column; subnav stacks above content. */
150
74
  @container (max-width: 40rem) {
151
- admin-shell > main > :is(section, section-ui):has(> [data-subnav]:not([hidden])) {
75
+ admin-shell > admin-content > admin-scroll:has(> [data-subnav]:not([hidden])) {
152
76
  grid-template-columns: 1fr;
153
77
  }
154
- admin-shell > main > :is(section, section-ui) > [data-subnav] {
78
+ admin-shell > admin-content > admin-scroll > [data-subnav] {
155
79
  border-inline-end: none;
156
80
  border-block-end: 1px solid var(--a-border-subtle);
157
81
  }
158
82
  }
159
-
160
- /* ── Main > footer (status bar) ──
161
- Legacy pattern: last-child auto-pushed to trailing edge via
162
- margin-inline-start: auto (works when authors use a simple
163
- <span>…</span><span>version</span> shape).
164
- Slot pattern: same icon / heading / description / action vocabulary
165
- as > main > header (see comment block above). */
166
- admin-shell > main > :is(footer, footer-ui) {
167
- flex-shrink: 0;
168
- display: flex;
169
- align-items: center;
170
- margin-top: auto;
171
- gap: var(--page-header-gap);
172
- min-height: var(--page-header-height);
173
- padding: 0 var(--page-header-px);
174
- border-top: var(--page-border);
175
- font-size: var(--page-header-font);
176
- color: var(--page-header-fg-muted);
177
- }
178
-
179
- /* Legacy: bare <span>…</span><span>version</span> shapes. Only kicks
180
- in when NO slot="action" is present, so it doesn't fight the slot
181
- rule below. */
182
- admin-shell > main > :is(footer, footer-ui):not(:has(> [slot="action"])) > :last-child {
183
- margin-inline-start: auto;
184
- }
185
-
186
- admin-shell > main > :is(footer, footer-ui) > [slot="icon"] {
187
- display: flex;
188
- align-items: center;
189
- flex-shrink: 0;
190
- color: var(--page-header-fg-muted);
191
- }
192
- admin-shell > main > :is(footer, footer-ui) > [slot="heading"] {
193
- font-weight: var(--a-weight-medium);
194
- color: var(--a-fg);
195
- }
196
- admin-shell > main > :is(footer, footer-ui) > [slot="description"] {
197
- color: var(--page-header-fg-muted);
198
- font-size: var(--a-ui-sm);
199
- }
200
- admin-shell > main > :is(footer, footer-ui) > [slot="action"] {
201
- display: flex;
202
- align-items: center;
203
- gap: var(--page-actions-gap);
204
- flex-shrink: 0;
205
- margin-inline-start: auto;
206
- }
207
- admin-shell > main > :is(footer, footer-ui) > [slot="action"] ~ [slot="action"] {
208
- margin-inline-start: 0;
209
- }
210
- /* Dual-cluster: leading group on inline-start, trailing cluster on inline-end. */
211
- admin-shell > main > :is(footer, footer-ui) > [slot="action-leading"] {
212
- display: flex;
213
- align-items: center;
214
- gap: var(--page-actions-gap);
215
- flex-shrink: 0;
216
- margin-inline-end: auto;
217
- }
@@ -1,32 +1,27 @@
1
1
  /* ═══════════════════════════════════════════════════════════════
2
2
  App Shell — Root layout + modes
3
3
 
4
- Structure (legacy raw-HTML form; slot-vocabulary equivalents in parens):
5
- admin-shell — root shell (flex row, fixed viewport)
6
- asideadmin-sidebar[slot="leading"] (or admin-sidebar[slot="leading"]) — nav sidebar (resizable, collapsible)
7
- main — center column (topbar + scroll + footer)
8
- header (or header-ui) — topbar
9
- section (or section-ui) — scroll container
10
- article[data-content-root] — page wrapper (sticky bands + centered body)
11
- div[data-content-header] > header — sticky page title + tabs
12
- div[data-content-body] > section — centered reading column
13
- footer (or footer-ui) — status bar
14
- asideadmin-sidebar[slot="trailing"] (or admin-sidebar[slot="trailing"]) — inspector sidebar
15
- dialog[data-command] — command palette
4
+ Structure (canonical bespoke shape, post-ADR-0032 v0.6.12):
5
+ admin-shell — root shell (flex row, fixed viewport)
6
+ admin-sidebar[slot="leading"] — nav sidebar (resizable, collapsible)
7
+ admin-content — center column (topbar + scroll + statusbar)
8
+ admin-topbar — topbar
9
+ admin-scroll — scroll container
10
+ admin-page — page wrapper (sticky bands + centered body)
11
+ admin-page-header > header — sticky page title + tabs
12
+ admin-page-body > section — centered reading column
13
+ admin-statusbar — status bar
14
+ admin-sidebar[slot="trailing"] — inspector sidebar
15
+ admin-command — command palette
16
16
 
17
- Both authoring shapes are valid simultaneously. The CSS uses
18
- :is(legacy, slot-ui) lifts so raw-HTML and slot-primitive
19
- composers render identically.
17
+ Modes (space-separated on mode attribute):
18
+ "rounded" — border-radius on scroll section
19
+ "borderless" removes chrome borders (content borders persist)
20
+ ═══════════════════════════════════════════════════════════════ */
20
21
 
21
- Modes (space-separated on mode attribute):
22
- "rounded" — border-radius on scroll section
23
- "borderless" removes chrome borders (content borders persist)
24
- ═══════════════════════════════════════════════════════════════ */
25
-
26
- /* ── Page modes ── */
27
- admin-shell[mode~="rounded"] > main > :is(section, section-ui) {
28
- border-radius: var(--page-content-radius);
29
- }
22
+ /* ── Page modes ──
23
+ Rounded-mode border-radius for the scroll container lives in
24
+ admin-shell.bespoke.css (`admin-shell[mode~="rounded"] > admin-content > admin-scroll`). */
30
25
 
31
26
  admin-shell[mode~="borderless"] {
32
27
  --page-main-border: none;
@@ -1,106 +1,65 @@
1
1
  /* ═══════════════════════════════════════════════════════════════
2
2
  admin-shell — Page templates (headers, body layouts, form sections)
3
3
 
4
- Structure:
5
- <article data-content-root>
6
- [data-content-header] — sticky top band, border-bottom
7
- <header>...</header> — centered column (padding + max-width)
8
- [data-content-body] — scrollable flex column (size only)
9
- <section>...</section> — centered column (padding + max-width + gap)
10
- [data-content-footer] — sticky bottom band, border-top
11
- <footer>...</footer> — centered column (padding + max-width)
12
-
13
- The outer `[data-*]` wrapper owns full-width concerns (sticky
14
- position, border, background, scroll sizing). The inner semantic
15
- element (<header>/<section>/<footer>) owns the centered reading
16
- column (max-width, gutter, rhythm). Full-bleed content (iframes,
17
- split panes) can skip <section> and sit directly in
18
- [data-content-body] to opt out of the column.
4
+ Structure (post-ADR-0032, v0.6.12):
5
+ <admin-page>
6
+ <admin-page-header> — sticky top band, border-bottom
7
+ <header>...</header> — centered column (padding + max-width)
8
+ <admin-page-body> — scrollable flex column (size only)
9
+ <section>...</section> — centered column (padding + max-width + gap)
10
+
11
+ The bespoke wrapper owns full-width concerns (sticky position, border,
12
+ background, scroll sizing) — see admin-shell.bespoke.css for those
13
+ rules. The inner semantic element (<header>/<section>) owns the
14
+ centered reading column (max-width, gutter, rhythm) that's what
15
+ this file covers. Full-bleed content (iframes, split panes) can skip
16
+ <section> and sit directly in <admin-page-body> to opt out of the
17
+ column.
18
+
19
+ Note: pre-v0.6.12 legacy attribute hooks (data-content-root,
20
+ data-content-header, data-content-body, data-content-footer) were
21
+ retired in ADR-0032. See git history prior to v0.6.12 for the legacy
22
+ selector lists.
19
23
  ═══════════════════════════════════════════════════════════════ */
20
24
 
21
- /* ── Content root ──
22
- `<article data-content-root>` provides an inline-size query container
23
- named `page-content` so descendants can adapt to the *available content
24
- width* (sidebars expanded vs collapsed) instead of the viewport.
25
-
26
- Breakpoint scale (mirrored in `:where(admin-shell) { --page-content-bp-* }`):
27
- sm 480px — phone / very narrow rail
28
- md 720px — tablet / 2-col → 1-col flip for [data-section]
29
- lg 1024px — wide content (no current rule; reserved)
30
- Container queries can't read custom properties as thresholds, so the
31
- numbers below are duplicated — keep them in lockstep with the tokens. */
32
- [data-content-root] {
33
- position: relative;
34
- isolation: isolate;
35
- min-block-size: 100%;
36
- container-type: inline-size;
37
- container-name: page-content;
38
- }
39
-
40
- /* Fill-mode: when the body contains only an iframe (or explicit opt-in),
41
- make the root a flex column that stretches to the scroll section's
42
- visible height, so [data-content-body] flex: 1 resolves against a real
43
- size and the iframe's block-size: 100% can fill. Prose pages (natural
44
- root height + section scroll) are unaffected. */
45
- [data-content-root]:has(> [data-content-body] > iframe:only-child),
46
- [data-content-root][data-fill] {
47
- display: flex;
48
- flex-direction: column;
49
- block-size: 100%;
50
- min-block-size: 0;
51
- }
52
-
53
25
  /* ── Scaffold content columns ─────────────────────────────────────
54
26
  Max-width + centering + horizontal inset are properties of the
55
- NAMED inner semantic elements, not the [data-*] wrappers.
27
+ NAMED inner semantic elements, not the bespoke wrappers.
56
28
 
57
29
  Who owns what:
58
- [data-content-header] / [data-content-footer]
59
- own sticky position, border, background (full-width band).
60
- [data-content-body]
61
- owns flex sizing + scroll; no padding, no max-width.
62
- • Inner <header> / <section> / <footer>
63
- own max-width, margin-inline: auto, padding (centered column).
30
+ <admin-page-header>
31
+ — sticky position, border, background (see bespoke.css).
32
+ <admin-page-body>
33
+ — flex sizing + scroll; no padding, no max-width (see bespoke.css).
34
+ • Inner <header> / <section>
35
+ — max-width, margin-inline: auto, padding (centered column).
64
36
  • Cards / modals / drawers
65
37
  — own margin/padding via their own CSS; never inherit this.
66
38
  ─────────────────────────────────────────────────────────────── */
67
- [data-content-header] > :is(header, header-ui),
68
- [data-content-body] > :is(section, section-ui),
69
- [data-content-footer] > :is(footer, footer-ui) {
39
+ admin-page-header > :is(header, header-ui),
40
+ admin-page-body > :is(section, section-ui) {
70
41
  max-width: var(--page-content-max-width);
71
42
  margin-inline: auto;
72
43
  width: 100%;
73
44
  box-sizing: border-box;
74
45
  }
75
46
 
76
- [data-content-header] > :is(header, header-ui),
77
- [data-content-footer] > :is(footer, footer-ui) {
47
+ admin-page-header > :is(header, header-ui) {
78
48
  padding-inline: var(--page-content-inset);
79
49
  }
80
50
 
81
- /* Full-width escape hatch — stretch the centered column to 100%. */
82
- [data-content-full] [data-content-header] > :is(header, header-ui),
83
- [data-content-full] [data-content-body] > :is(section, section-ui),
84
- [data-content-full] [data-content-footer] > :is(footer, footer-ui),
85
- [data-content-header][data-content-full] > :is(header, header-ui),
86
- [data-content-body][data-content-full] > :is(section, section-ui),
87
- [data-content-footer][data-content-full] > :is(footer, footer-ui),
88
- [data-content-header] > :is(header, header-ui)[data-content-full],
89
- [data-content-body] > :is(section, section-ui)[data-content-full],
90
- [data-content-footer] > :is(footer, footer-ui)[data-content-full] {
51
+ /* Full-width escape hatch — stretch the centered column to 100%.
52
+ [data-content-full] can land on the bespoke wrapper or its inner
53
+ semantic element. */
54
+ admin-page-header[data-content-full] > :is(header, header-ui),
55
+ admin-page-body[data-content-full] > :is(section, section-ui),
56
+ admin-page-header > :is(header, header-ui)[data-content-full],
57
+ admin-page-body > :is(section, section-ui)[data-content-full] {
91
58
  max-width: 100%;
92
59
  }
93
60
 
94
- /* ── Content header sticky top, border-bottom ── */
95
- [data-content-header] {
96
- position: sticky;
97
- top: 0;
98
- z-index: 1;
99
- border-bottom: var(--page-content-border);
100
- background: var(--page-content-bg);
101
- }
102
-
103
- [data-content-header] > :is(header, header-ui) {
61
+ /* ── Content header inner layout ── */
62
+ admin-page-header > :is(header, header-ui) {
104
63
  display: flex;
105
64
  flex-direction: column;
106
65
  gap: var(--page-header-gap);
@@ -108,24 +67,24 @@
108
67
  }
109
68
 
110
69
  /* When tabs are present, remove bottom padding so tabs sit flush against border */
111
- [data-content-header] > :is(header, header-ui):has(tabs-ui) {
70
+ admin-page-header > :is(header, header-ui):has(tabs-ui) {
112
71
  padding-bottom: 0;
113
72
  }
114
73
 
115
74
  /* Variant: non-sticky, no border, transparent bg */
116
- [data-content-header]:has(> :is(header, header-ui)[data-flush]) {
75
+ admin-page-header:has(> :is(header, header-ui)[data-flush]) {
117
76
  position: static;
118
77
  border-bottom: none;
119
78
  background: none;
120
79
  }
121
80
 
122
81
  /* Variant: compact padding (for dense headers) */
123
- [data-content-header] > :is(header, header-ui)[data-compact] {
82
+ admin-page-header > :is(header, header-ui)[data-compact] {
124
83
  padding-block: var(--page-header-px);
125
84
  }
126
85
 
127
86
  /* Title row: h1 left, actions right */
128
- [data-content-header] > :is(header, header-ui) > div:first-child {
87
+ admin-page-header > :is(header, header-ui) > div:first-child {
129
88
  display: flex;
130
89
  align-items: center;
131
90
  justify-content: space-between;
@@ -133,27 +92,20 @@
133
92
  }
134
93
 
135
94
  /* h1 + description — typography handled by [variant] attribute */
136
- [data-content-header] h1 { margin: 0; }
137
- [data-content-header] > :is(header, header-ui) > p { margin: 0; }
95
+ admin-page-header h1 { margin: 0; }
96
+ admin-page-header > :is(header, header-ui) > p { margin: 0; }
138
97
 
139
- /* ── Content body ──
98
+ /* ── Content body — flex column inner layout ──
140
99
  Wrapper: flex column that fills the scroll section. No padding,
141
100
  no max-width — those live on the inner <section>. Non-section
142
- children (iframes, split panes) go full-bleed. */
143
- [data-content-body] {
144
- flex: 1;
145
- min-height: 0;
146
- width: 100%;
147
- box-sizing: border-box;
148
- display: flex;
149
- flex-direction: column;
150
- }
101
+ children (iframes, split panes) go full-bleed.
102
+ (Wrapper geometry — flex, min-height, width — lives in bespoke.css.) */
151
103
 
152
104
  /* Inner <section> (or <section-ui>) — the centered reading column.
153
105
  Owns gutter and section-to-section rhythm. Stacks with flex gap
154
106
  so direct children (<card-ui>, <grid-ui>, nested <section>, …)
155
107
  have zero wrapper margin. */
156
- [data-content-body] > :is(section, section-ui) {
108
+ admin-page-body > :is(section, section-ui) {
157
109
  padding: var(--page-content-inset);
158
110
  display: flex;
159
111
  flex-direction: column;
@@ -162,7 +114,7 @@
162
114
 
163
115
  /* iframe-as-page-content: fill the body.
164
116
  display: block kills the 4-5px inline-descender gap. */
165
- [data-content-body] > iframe {
117
+ admin-page-body > iframe {
166
118
  display: block;
167
119
  inline-size: 100%;
168
120
  block-size: 100%;
@@ -170,19 +122,6 @@
170
122
  flex: 1;
171
123
  }
172
124
 
173
- /* ── Content footer — sticky bottom, border-top ── */
174
- [data-content-footer] {
175
- position: sticky;
176
- bottom: 0;
177
- z-index: 1;
178
- border-top: var(--page-content-border);
179
- background: var(--page-content-bg);
180
- }
181
-
182
- [data-content-footer] > footer {
183
- padding: var(--page-content-inset);
184
- }
185
-
186
125
  /* ── Tab content ──
187
126
  Marker for the active tab's form sections. Gutter/padding is
188
127
  owned by the <article> wrapper. */
@@ -239,12 +178,11 @@
239
178
 
240
179
  /* Tighten the content gutter when the article is genuinely narrow. */
241
180
  @container page-content (max-width: 480px) {
242
- [data-content-header] > :is(header, header-ui),
243
- [data-content-body] > :is(section, section-ui),
244
- [data-content-footer] > :is(footer, footer-ui) {
181
+ admin-page-header > :is(header, header-ui),
182
+ admin-page-body > :is(section, section-ui) {
245
183
  padding-inline: var(--a-space-4);
246
184
  }
247
- [data-content-body] > :is(section, section-ui) {
185
+ admin-page-body > :is(section, section-ui) {
248
186
  padding-block: var(--a-space-4);
249
187
  }
250
188
  }
@@ -44,7 +44,7 @@
44
44
  --page-content-border: 1px solid var(--a-border-subtle); /* content dividers (persists in "borderless") */
45
45
  --page-content-shadow: var(--a-shadow-sm); /* soft lift on the content surface */
46
46
 
47
- /* Content breakpoint scale — `[data-content-root]` is a `page-content`
47
+ /* Content breakpoint scale — `<admin-page>` is a `page-content`
48
48
  container query provider (admin-shell.templates.css). These tokens
49
49
  are the documented thresholds; @container queries can't read custom
50
50
  properties for thresholds, so the numbers are duplicated in CSS. */