@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.
- package/CHANGELOG.md +292 -0
- package/README.md +119 -0
- package/chat/chat-shell/chat-shell.a2ui.json +149 -0
- package/chat/chat-shell/chat-shell.css +10 -0
- package/chat/chat-shell/chat-shell.js +297 -0
- package/chat/chat-shell/chat-shell.yaml +119 -0
- package/chat/chat-shell/css/chat-shell.empty.css +12 -0
- package/chat/chat-shell/css/chat-shell.layout.css +60 -0
- package/chat/chat-shell/css/chat-shell.markdown.css +74 -0
- package/chat/chat-shell/css/chat-shell.messages.css +87 -0
- package/chat/chat-shell/css/chat-shell.streaming.css +30 -0
- package/chat/chat-shell/css/chat-shell.tokens.css +95 -0
- package/chat/index.js +1 -0
- package/editor/editor-shell/css/editor-shell.layout.css +171 -0
- package/editor/editor-shell/css/editor-shell.tokens.css +28 -0
- package/editor/editor-shell/editor-shell.a2ui.json +73 -0
- package/editor/editor-shell/editor-shell.css +6 -0
- package/editor/editor-shell/editor-shell.js +56 -0
- package/editor/editor-shell/editor-shell.yaml +59 -0
- package/editor/index.js +1 -0
- package/index.js +14 -0
- package/package.json +48 -0
- package/runtime/a2ui-root/a2ui-root.a2ui.json +125 -0
- package/runtime/a2ui-root/a2ui-root.js +191 -0
- package/runtime/a2ui-root/a2ui-root.yaml +87 -0
- package/runtime/gen-root/gen-root.a2ui.json +72 -0
- package/runtime/gen-root/gen-root.css +83 -0
- package/runtime/gen-root/gen-root.js +136 -0
- package/runtime/gen-root/gen-root.yaml +43 -0
- package/runtime/index.js +2 -0
- package/shell/admin-shell/admin-shell.a2ui.json +129 -0
- package/shell/admin-shell/admin-shell.css +14 -0
- package/shell/admin-shell/admin-shell.js +261 -0
- package/shell/admin-shell/admin-shell.yaml +89 -0
- package/shell/admin-shell/css/admin-shell.collapsed.css +86 -0
- package/shell/admin-shell/css/admin-shell.helpers.css +42 -0
- package/shell/admin-shell/css/admin-shell.main.css +182 -0
- package/shell/admin-shell/css/admin-shell.shell.css +48 -0
- package/shell/admin-shell/css/admin-shell.sidebar.css +165 -0
- package/shell/admin-shell/css/admin-shell.templates.css +215 -0
- package/shell/admin-shell/css/admin-shell.tokens.css +119 -0
- 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
|
+
}
|