@adia-ai/web-modules 0.0.4

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 (42) hide show
  1. package/CHANGELOG.md +292 -0
  2. package/README.md +119 -0
  3. package/chat/chat-shell/chat-shell.a2ui.json +149 -0
  4. package/chat/chat-shell/chat-shell.css +10 -0
  5. package/chat/chat-shell/chat-shell.js +297 -0
  6. package/chat/chat-shell/chat-shell.yaml +119 -0
  7. package/chat/chat-shell/css/chat-shell.empty.css +12 -0
  8. package/chat/chat-shell/css/chat-shell.layout.css +60 -0
  9. package/chat/chat-shell/css/chat-shell.markdown.css +74 -0
  10. package/chat/chat-shell/css/chat-shell.messages.css +87 -0
  11. package/chat/chat-shell/css/chat-shell.streaming.css +30 -0
  12. package/chat/chat-shell/css/chat-shell.tokens.css +95 -0
  13. package/chat/index.js +1 -0
  14. package/editor/editor-shell/css/editor-shell.layout.css +171 -0
  15. package/editor/editor-shell/css/editor-shell.tokens.css +28 -0
  16. package/editor/editor-shell/editor-shell.a2ui.json +73 -0
  17. package/editor/editor-shell/editor-shell.css +6 -0
  18. package/editor/editor-shell/editor-shell.js +56 -0
  19. package/editor/editor-shell/editor-shell.yaml +59 -0
  20. package/editor/index.js +1 -0
  21. package/index.js +14 -0
  22. package/package.json +48 -0
  23. package/runtime/a2ui-root/a2ui-root.a2ui.json +125 -0
  24. package/runtime/a2ui-root/a2ui-root.js +191 -0
  25. package/runtime/a2ui-root/a2ui-root.yaml +87 -0
  26. package/runtime/gen-root/gen-root.a2ui.json +72 -0
  27. package/runtime/gen-root/gen-root.css +83 -0
  28. package/runtime/gen-root/gen-root.js +136 -0
  29. package/runtime/gen-root/gen-root.yaml +43 -0
  30. package/runtime/index.js +2 -0
  31. package/shell/admin-shell/admin-shell.a2ui.json +129 -0
  32. package/shell/admin-shell/admin-shell.css +14 -0
  33. package/shell/admin-shell/admin-shell.js +261 -0
  34. package/shell/admin-shell/admin-shell.yaml +89 -0
  35. package/shell/admin-shell/css/admin-shell.collapsed.css +86 -0
  36. package/shell/admin-shell/css/admin-shell.helpers.css +42 -0
  37. package/shell/admin-shell/css/admin-shell.main.css +182 -0
  38. package/shell/admin-shell/css/admin-shell.shell.css +48 -0
  39. package/shell/admin-shell/css/admin-shell.sidebar.css +165 -0
  40. package/shell/admin-shell/css/admin-shell.templates.css +215 -0
  41. package/shell/admin-shell/css/admin-shell.tokens.css +119 -0
  42. package/shell/index.js +1 -0
@@ -0,0 +1,182 @@
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.
11
+ ═══════════════════════════════════════════════════════════════ */
12
+
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
+ /* ── Edge breathing room when adjacent chrome is absent ──
24
+ 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(aside[data-sidebar="trailing"], aside-ui[slot="trailing"]):not([hidden]))) > main > :is(section, section-ui) {
40
+ margin-inline-end: var(--a-space-2);
41
+ }
42
+ admin-shell:not(:has(> :is(header, header-ui):not([hidden]), > main > :is(header, header-ui):not([hidden]))) > main > :is(section, section-ui) {
43
+ margin-block-start: var(--a-space-2);
44
+ }
45
+ admin-shell:not(:has(> :is(footer, footer-ui):not([hidden]), > main > :is(footer, footer-ui):not([hidden]))) > main > :is(section, section-ui) {
46
+ margin-block-end: var(--a-space-2);
47
+ }
48
+
49
+ /* ── Main > header (topbar) ──
50
+ Contains: sidebar toggle, breadcrumb, spacer, action buttons.
51
+
52
+ Slot contract (shared with > main > footer and [data-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; }
124
+
125
+ /* ── Main > footer (status bar) ──
126
+ Legacy pattern: last-child auto-pushed to trailing edge via
127
+ margin-inline-start: auto (works when authors use a simple
128
+ <span>…</span><span>version</span> shape).
129
+ Slot pattern: same icon / heading / description / action vocabulary
130
+ as > main > header (see comment block above). */
131
+ admin-shell > main > :is(footer, footer-ui) {
132
+ flex-shrink: 0;
133
+ display: flex;
134
+ align-items: center;
135
+ margin-top: auto;
136
+ gap: var(--page-header-gap);
137
+ min-height: var(--page-header-height);
138
+ padding: 0 var(--page-header-px);
139
+ border-top: var(--page-border);
140
+ font-size: var(--page-header-font);
141
+ color: var(--page-header-fg-muted);
142
+ }
143
+
144
+ /* Legacy: bare <span>…</span><span>version</span> shapes. Only kicks
145
+ in when NO slot="action" is present, so it doesn't fight the slot
146
+ rule below. */
147
+ admin-shell > main > :is(footer, footer-ui):not(:has(> [slot="action"])) > :last-child {
148
+ margin-inline-start: auto;
149
+ }
150
+
151
+ admin-shell > main > :is(footer, footer-ui) > [slot="icon"] {
152
+ display: flex;
153
+ align-items: center;
154
+ flex-shrink: 0;
155
+ color: var(--page-header-fg-muted);
156
+ }
157
+ admin-shell > main > :is(footer, footer-ui) > [slot="heading"] {
158
+ font-weight: var(--a-weight-medium);
159
+ color: var(--a-fg);
160
+ }
161
+ admin-shell > main > :is(footer, footer-ui) > [slot="description"] {
162
+ color: var(--page-header-fg-muted);
163
+ font-size: var(--a-ui-sm);
164
+ }
165
+ admin-shell > main > :is(footer, footer-ui) > [slot="action"] {
166
+ display: flex;
167
+ align-items: center;
168
+ gap: var(--page-actions-gap);
169
+ flex-shrink: 0;
170
+ margin-inline-start: auto;
171
+ }
172
+ admin-shell > main > :is(footer, footer-ui) > [slot="action"] ~ [slot="action"] {
173
+ margin-inline-start: 0;
174
+ }
175
+ /* Dual-cluster: leading group on inline-start, trailing cluster on inline-end. */
176
+ admin-shell > main > :is(footer, footer-ui) > [slot="action-leading"] {
177
+ display: flex;
178
+ align-items: center;
179
+ gap: var(--page-actions-gap);
180
+ flex-shrink: 0;
181
+ margin-inline-end: auto;
182
+ }
@@ -0,0 +1,48 @@
1
+ /* ═══════════════════════════════════════════════════════════════
2
+ App Shell — Root layout + modes
3
+
4
+ Structure (legacy raw-HTML form; slot-vocabulary equivalents in parens):
5
+ admin-shell — root shell (flex row, fixed viewport)
6
+ aside[data-sidebar="leading"] (or aside-ui[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
+ aside[data-sidebar="trailing"] (or aside-ui[slot="trailing"]) — inspector sidebar
15
+ dialog[data-command] — command palette
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.
20
+
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
+ }
30
+
31
+ admin-shell[mode~="borderless"] {
32
+ --page-main-border: none;
33
+ --page-border: none;
34
+ /* Note: --page-content-border is NOT reset — content dividers persist */
35
+ }
36
+
37
+ /* ── Page shell ── */
38
+ admin-shell {
39
+ display: flex;
40
+ height: 100dvh;
41
+ overflow: hidden;
42
+ background: var(--page-bg);
43
+ position: fixed;
44
+ inset: 0;
45
+ overscroll-behavior: none;
46
+ font-family: var(--page-font-family);
47
+ font-size: var(--page-body-size);
48
+ }
@@ -0,0 +1,165 @@
1
+ /* ═══════════════════════════════════════════════════════════════
2
+ admin-shell — Sidebar
3
+
4
+ Resizable side panels with header/section/footer structure.
5
+ Acts as a CSS container query provider (container-name: sidebar)
6
+ so children can adapt to collapsed width via @container.
7
+
8
+ Authoring shapes (both styled identically):
9
+ - Legacy: <aside data-sidebar="leading|trailing">
10
+ - Slot vocabulary: <aside-ui slot="leading|trailing">
11
+ ═══════════════════════════════════════════════════════════════ */
12
+
13
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) {
14
+ display: flex;
15
+ flex-direction: column;
16
+ flex-shrink: 0;
17
+ min-width: var(--page-sidebar-min-width);
18
+ max-width: var(--page-sidebar-max-width);
19
+ min-height: 0;
20
+ font-size: var(--page-sidebar-font);
21
+ position: relative;
22
+ container-type: inline-size;
23
+ container-name: sidebar;
24
+ transition: width var(--page-duration) var(--page-easing);
25
+ }
26
+
27
+ /* ── Resize handle ──
28
+ 6px invisible hit area straddling the sidebar edge.
29
+ Accent color on hover/drag. */
30
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > [data-resize] {
31
+ position: absolute;
32
+ top: 0;
33
+ bottom: 0;
34
+ width: 6px;
35
+ cursor: col-resize;
36
+ background: transparent;
37
+ transition: background var(--page-duration-fast) var(--page-easing);
38
+ z-index: 2;
39
+ }
40
+
41
+ :is([data-sidebar="leading"], aside-ui[slot="leading"]) > [data-resize] { right: -3px; }
42
+ :is([data-sidebar="trailing"], aside-ui[slot="trailing"]) > [data-resize] { left: -3px; }
43
+
44
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > [data-resize]:hover {
45
+ background: var(--page-sidebar-resize-accent);
46
+ }
47
+
48
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"])[data-resizing] > [data-resize] {
49
+ background: var(--page-sidebar-resize-accent);
50
+ }
51
+
52
+ /* During drag: disable transition + text selection */
53
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"])[data-resizing] {
54
+ user-select: none;
55
+ transition: none;
56
+ }
57
+
58
+ /* ── Sidebar widths ── */
59
+ :is([data-sidebar="leading"], aside-ui[slot="leading"]) {
60
+ width: var(--page-sidebar-width-leading);
61
+ }
62
+
63
+ :is([data-sidebar="trailing"], aside-ui[slot="trailing"]) {
64
+ width: var(--page-sidebar-width-trailing);
65
+ }
66
+
67
+ /* ── Sidebar header / footer ──
68
+ Share the same slot contract as > main > header/footer (see
69
+ app-shell.main.css top-of-file comment): icon / heading /
70
+ description / action. Sidebar chromes are typically single-child
71
+ (a workspace-select or user-select) so the slot rules rarely come
72
+ into play — but they stay consistent for authors that compose
73
+ richer sidebar chromes. */
74
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(header, header-ui),
75
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(footer, footer-ui) {
76
+ display: flex;
77
+ align-items: center;
78
+ gap: var(--page-sidebar-gap);
79
+ padding: var(--page-sidebar-px);
80
+ flex-shrink: 0;
81
+ overflow: hidden;
82
+ min-width: 0;
83
+ }
84
+
85
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(header, header-ui) {
86
+ min-height: var(--page-header-height);
87
+ border-bottom: var(--page-border);
88
+ }
89
+
90
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(footer, footer-ui) {
91
+ min-height: var(--page-header-height);
92
+ margin-top: auto;
93
+ border-top: var(--page-border);
94
+ }
95
+
96
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(header, header-ui) > [slot="icon"],
97
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(footer, footer-ui) > [slot="icon"] {
98
+ display: flex;
99
+ align-items: center;
100
+ flex-shrink: 0;
101
+ color: var(--page-header-fg-muted);
102
+ }
103
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(header, header-ui) > [slot="heading"],
104
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(footer, footer-ui) > [slot="heading"] {
105
+ font-weight: var(--a-weight-medium);
106
+ color: var(--a-fg);
107
+ }
108
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(header, header-ui) > [slot="description"],
109
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(footer, footer-ui) > [slot="description"] {
110
+ color: var(--page-header-fg-muted);
111
+ font-size: var(--a-ui-sm);
112
+ }
113
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(header, header-ui) > [slot="action"],
114
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(footer, footer-ui) > [slot="action"] {
115
+ display: flex;
116
+ align-items: center;
117
+ gap: var(--page-actions-gap);
118
+ flex-shrink: 0;
119
+ margin-inline-start: auto;
120
+ }
121
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(header, header-ui) > [slot="action"] ~ [slot="action"],
122
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(footer, footer-ui) > [slot="action"] ~ [slot="action"] {
123
+ margin-inline-start: 0;
124
+ }
125
+ /* Dual-cluster: leading group on inline-start, trailing cluster on inline-end. */
126
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(header, header-ui) > [slot="action-leading"],
127
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(footer, footer-ui) > [slot="action-leading"] {
128
+ display: flex;
129
+ align-items: center;
130
+ gap: var(--page-actions-gap);
131
+ flex-shrink: 0;
132
+ margin-inline-end: auto;
133
+ }
134
+
135
+ /* ── Sidebar section (scrollable body) ── */
136
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(section, section-ui) {
137
+ flex: 1;
138
+ overflow-y: auto;
139
+ padding: var(--page-sidebar-px);
140
+ min-height: 0;
141
+ scrollbar-width: none;
142
+ }
143
+
144
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(section, section-ui)::-webkit-scrollbar { display: none; }
145
+
146
+ /* span[slot="pad"] — opt-in padding wrapper for sidebar content
147
+ that needs inset (e.g. trailing sidebar inspector fields) */
148
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) span[slot="pad"] {
149
+ display: block;
150
+ padding: var(--page-sidebar-px);
151
+ }
152
+
153
+ /* Reset nav-ui border/padding inside sidebars */
154
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) nav-ui {
155
+ border: none;
156
+ padding: 0;
157
+ }
158
+
159
+ /* Sidebar-level divider (outside nav-ui scope) */
160
+ :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(section, section-ui) > hr[data-nav-divider] {
161
+ border: none;
162
+ height: 1px;
163
+ background: var(--page-sidebar-divider-bg);
164
+ margin: var(--page-sidebar-gap) var(--page-sidebar-px);
165
+ }
@@ -0,0 +1,215 @@
1
+ /* ═══════════════════════════════════════════════════════════════
2
+ admin-shell — Page templates (headers, body layouts, form sections)
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.
19
+ ═══════════════════════════════════════════════════════════════ */
20
+
21
+ /* ── Content root ── */
22
+ [data-content-root] {
23
+ position: relative;
24
+ isolation: isolate;
25
+ min-block-size: 100%;
26
+ }
27
+
28
+ /* Fill-mode: when the body contains only an iframe (or explicit opt-in),
29
+ make the root a flex column that stretches to the scroll section's
30
+ visible height, so [data-content-body] flex: 1 resolves against a real
31
+ size and the iframe's block-size: 100% can fill. Prose pages (natural
32
+ root height + section scroll) are unaffected. */
33
+ [data-content-root]:has(> [data-content-body] > iframe:only-child),
34
+ [data-content-root][data-fill] {
35
+ display: flex;
36
+ flex-direction: column;
37
+ block-size: 100%;
38
+ min-block-size: 0;
39
+ }
40
+
41
+ /* ── Scaffold content columns ─────────────────────────────────────
42
+ Max-width + centering + horizontal inset are properties of the
43
+ NAMED inner semantic elements, not the [data-*] wrappers.
44
+
45
+ Who owns what:
46
+ • [data-content-header] / [data-content-footer]
47
+ — own sticky position, border, background (full-width band).
48
+ • [data-content-body]
49
+ — owns flex sizing + scroll; no padding, no max-width.
50
+ • Inner <header> / <section> / <footer>
51
+ — own max-width, margin-inline: auto, padding (centered column).
52
+ • Cards / modals / drawers
53
+ — own margin/padding via their own CSS; never inherit this.
54
+ ─────────────────────────────────────────────────────────────── */
55
+ [data-content-header] > :is(header, header-ui),
56
+ [data-content-body] > :is(section, section-ui),
57
+ [data-content-footer] > :is(footer, footer-ui) {
58
+ max-width: var(--page-content-max-width);
59
+ margin-inline: auto;
60
+ width: 100%;
61
+ box-sizing: border-box;
62
+ }
63
+
64
+ [data-content-header] > :is(header, header-ui),
65
+ [data-content-footer] > :is(footer, footer-ui) {
66
+ padding-inline: var(--page-content-inset);
67
+ }
68
+
69
+ /* Full-width escape hatch — stretch the centered column to 100%. */
70
+ [data-content-full] [data-content-header] > :is(header, header-ui),
71
+ [data-content-full] [data-content-body] > :is(section, section-ui),
72
+ [data-content-full] [data-content-footer] > :is(footer, footer-ui),
73
+ [data-content-header][data-content-full] > :is(header, header-ui),
74
+ [data-content-body][data-content-full] > :is(section, section-ui),
75
+ [data-content-footer][data-content-full] > :is(footer, footer-ui),
76
+ [data-content-header] > :is(header, header-ui)[data-content-full],
77
+ [data-content-body] > :is(section, section-ui)[data-content-full],
78
+ [data-content-footer] > :is(footer, footer-ui)[data-content-full] {
79
+ max-width: 100%;
80
+ }
81
+
82
+ /* ── Content header — sticky top, border-bottom ── */
83
+ [data-content-header] {
84
+ position: sticky;
85
+ top: 0;
86
+ z-index: 1;
87
+ border-bottom: var(--page-content-border);
88
+ background: var(--page-content-bg);
89
+ }
90
+
91
+ [data-content-header] > :is(header, header-ui) {
92
+ display: flex;
93
+ flex-direction: column;
94
+ gap: var(--page-header-gap);
95
+ padding-block: var(--page-content-inset) var(--page-header-gap);
96
+ }
97
+
98
+ /* When tabs are present, remove bottom padding so tabs sit flush against border */
99
+ [data-content-header] > :is(header, header-ui):has(tabs-ui) {
100
+ padding-bottom: 0;
101
+ }
102
+
103
+ /* Variant: non-sticky, no border, transparent bg */
104
+ [data-content-header]:has(> :is(header, header-ui)[data-flush]) {
105
+ position: static;
106
+ border-bottom: none;
107
+ background: none;
108
+ }
109
+
110
+ /* Variant: compact padding (for dense headers) */
111
+ [data-content-header] > :is(header, header-ui)[data-compact] {
112
+ padding-block: var(--page-header-px);
113
+ }
114
+
115
+ /* Title row: h1 left, actions right */
116
+ [data-content-header] > :is(header, header-ui) > div:first-child {
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: space-between;
120
+ gap: var(--page-header-gap);
121
+ }
122
+
123
+ /* h1 + description — typography handled by [variant] attribute */
124
+ [data-content-header] h1 { margin: 0; }
125
+ [data-content-header] > :is(header, header-ui) > p { margin: 0; }
126
+
127
+ /* ── Content body ──
128
+ Wrapper: flex column that fills the scroll section. No padding,
129
+ no max-width — those live on the inner <section>. Non-section
130
+ children (iframes, split panes) go full-bleed. */
131
+ [data-content-body] {
132
+ flex: 1;
133
+ min-height: 0;
134
+ width: 100%;
135
+ box-sizing: border-box;
136
+ display: flex;
137
+ flex-direction: column;
138
+ }
139
+
140
+ /* Inner <section> (or <section-ui>) — the centered reading column.
141
+ Owns gutter and section-to-section rhythm. Stacks with flex gap
142
+ so direct children (<card-ui>, <grid-ui>, nested <section>, …)
143
+ have zero wrapper margin. */
144
+ [data-content-body] > :is(section, section-ui) {
145
+ padding: var(--page-content-inset);
146
+ display: flex;
147
+ flex-direction: column;
148
+ gap: var(--page-section-gap, var(--a-space-8));
149
+ }
150
+
151
+ /* iframe-as-page-content: fill the body.
152
+ display: block kills the 4-5px inline-descender gap. */
153
+ [data-content-body] > iframe {
154
+ display: block;
155
+ inline-size: 100%;
156
+ block-size: 100%;
157
+ border: 0;
158
+ flex: 1;
159
+ }
160
+
161
+ /* ── Content footer — sticky bottom, border-top ── */
162
+ [data-content-footer] {
163
+ position: sticky;
164
+ bottom: 0;
165
+ z-index: 1;
166
+ border-top: var(--page-content-border);
167
+ background: var(--page-content-bg);
168
+ }
169
+
170
+ [data-content-footer] > footer {
171
+ padding: var(--page-content-inset);
172
+ }
173
+
174
+ /* ── Tab content ──
175
+ Marker for the active tab's form sections. Gutter/padding is
176
+ owned by the <article> wrapper. */
177
+
178
+ /* ── Form sections ──
179
+ Each section: kicker-styled h2 label, then [data-section] grid. */
180
+ [data-tab-content] > section {
181
+ margin-bottom: var(--page-section-gap);
182
+ }
183
+
184
+ /* h2 — kicker role (small, uppercase, muted, letter-spaced) */
185
+ [data-tab-content] > section > h2 {
186
+ font-size: var(--page-section-size);
187
+ font-weight: var(--page-section-weight);
188
+ line-height: var(--page-section-leading);
189
+ letter-spacing: var(--page-section-tracking);
190
+ text-transform: var(--page-section-case);
191
+ color: var(--page-section-color);
192
+ margin: 0 0 var(--page-section-mb);
193
+ padding-bottom: var(--page-section-pb);
194
+ border-bottom: var(--page-content-border);
195
+ }
196
+
197
+ /* Two-column settings layout: description left, controls right */
198
+ [data-tab-content] > section > [data-section] {
199
+ display: grid;
200
+ grid-template-columns: 1fr 2fr;
201
+ gap: var(--page-section-gap) var(--page-header-gap);
202
+ }
203
+
204
+ /* h3 — subsection role */
205
+ [data-tab-content] > section > [data-section] > aside > h3 {
206
+ font-size: var(--page-subsection-size);
207
+ font-weight: var(--page-subsection-weight);
208
+ color: var(--page-subsection-color);
209
+ margin: 0 0 var(--page-section-pb);
210
+ }
211
+
212
+ /* Aside description */
213
+ [data-tab-content] > section > [data-section] > aside > p {
214
+ margin: 0;
215
+ }