@adia-ai/web-modules 0.3.6 → 0.4.1

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 (47) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +29 -15
  3. package/chat/chat-shell/chat-shell.js +28 -40
  4. package/chat/chat-shell/css/chat-shell.empty.css +3 -3
  5. package/chat/chat-shell/css/chat-shell.layout.css +2 -2
  6. package/editor/editor-canvas/editor-canvas.examples.html +65 -0
  7. package/editor/editor-canvas/editor-canvas.html +43 -0
  8. package/editor/editor-canvas-empty/editor-canvas-empty.examples.html +65 -0
  9. package/editor/editor-canvas-empty/editor-canvas-empty.html +42 -0
  10. package/editor/editor-shell/css/editor-shell.bespoke.css +67 -2
  11. package/editor/editor-shell/css/editor-shell.layout.css +6 -6
  12. package/editor/editor-shell/editor-shell.js +19 -17
  13. package/editor/editor-sidebar/editor-sidebar.a2ui.json +5 -0
  14. package/editor/editor-sidebar/editor-sidebar.examples.html +65 -0
  15. package/editor/editor-sidebar/editor-sidebar.html +43 -0
  16. package/editor/editor-sidebar/editor-sidebar.js +30 -6
  17. package/editor/editor-sidebar/editor-sidebar.test.js +19 -0
  18. package/editor/editor-sidebar/editor-sidebar.yaml +8 -0
  19. package/editor/editor-statusbar/editor-statusbar.examples.html +65 -0
  20. package/editor/editor-statusbar/editor-statusbar.html +42 -0
  21. package/editor/editor-toolbar/editor-toolbar.examples.html +65 -0
  22. package/editor/editor-toolbar/editor-toolbar.html +43 -0
  23. package/index.js +1 -0
  24. package/package.json +8 -5
  25. package/shell/admin-shell/admin-shell.js +27 -243
  26. package/shell/admin-shell/css/admin-shell.bespoke.css +22 -26
  27. package/shell/admin-shell/css/admin-shell.main.css +2 -2
  28. package/shell/admin-shell/css/admin-shell.shell.css +2 -2
  29. package/shell/admin-shell/css/admin-shell.sidebar.css +35 -33
  30. package/simple/index.js +2 -0
  31. package/simple/simple-content/simple-content.a2ui.json +67 -0
  32. package/simple/simple-content/simple-content.css +29 -0
  33. package/simple/simple-content/simple-content.examples.html +13 -0
  34. package/simple/simple-content/simple-content.html +42 -0
  35. package/simple/simple-content/simple-content.yaml +54 -0
  36. package/simple/simple-hero/simple-hero.a2ui.json +76 -0
  37. package/simple/simple-hero/simple-hero.css +45 -0
  38. package/simple/simple-hero/simple-hero.examples.html +33 -0
  39. package/simple/simple-hero/simple-hero.html +42 -0
  40. package/simple/simple-hero/simple-hero.yaml +57 -0
  41. package/simple/simple-shell/simple-shell.a2ui.json +87 -0
  42. package/simple/simple-shell/simple-shell.css +40 -0
  43. package/simple/simple-shell/simple-shell.examples.html +42 -0
  44. package/simple/simple-shell/simple-shell.html +42 -0
  45. package/simple/simple-shell/simple-shell.js +47 -0
  46. package/simple/simple-shell/simple-shell.test.js +83 -0
  47. package/simple/simple-shell/simple-shell.yaml +78 -0
@@ -7,25 +7,21 @@
7
7
  * </admin-shell>
8
8
  *
9
9
  * Module-tier app shell. Coordinates bespoke shell-tier children;
10
- * does NOT centralize their behavior. Each child owns its own
11
- * concern (resize/collapse on <admin-sidebar>, palette wiring on
12
- * <admin-command>).
10
+ * does NOT centralize their behavior. Each child owns its own concern
11
+ * (resize/collapse on <admin-sidebar>, palette wiring on <admin-command>).
13
12
  *
14
- * Backwards compatibility: legacy authoring shapes still work
15
- * <aside data-sidebar="leading"> and <dialog data-command> are
16
- * recognized + wired the same as their bespoke equivalents. Mixed
17
- * markup (some bespoke, some legacy) renders identically. See
18
- * ADR-0023 for the migration window.
13
+ * **Bespoke-only since v0.4.0** (ADR-0023 Phase 3 + ADR-0024). The
14
+ * legacy authoring shapes (`<aside data-sidebar>`, `<dialog data-command>`,
15
+ * `[data-resize]`, `<aside-ui slot>`) are no longer recognized consumers
16
+ * MUST use the bespoke vocabulary. The v0.3.x line carried backwards
17
+ * compat reads; see git history for the legacy paths.
19
18
  *
20
19
  * Host responsibilities (the only behavior that stays here):
21
20
  * 1. [mode] reflection — "rounded", "borderless", or both
22
- * 2. [data-sidebar-toggle="leading|trailing"] click forwarding to
23
- * the matching sidebar's .toggle() (works for bespoke + legacy)
24
- * 3. [data-command-trigger] click forwarding to the inner palette's
25
- * .show() (works for bespoke + legacy)
26
- * 4. Re-emit child events as host events for backwards compat
27
- * (sidebar-toggle, sidebar-resize, command-select all bubble
28
- * already, but consumers who listened on the host get them)
21
+ * 2. [data-sidebar-toggle="<name>"] click forwarding to the matching
22
+ * <admin-sidebar>'s .toggle() public method
23
+ * 3. [data-command-trigger] click forwarding to <admin-command>.show()
24
+ * 4. command-select re-emit + nav routing when a <nav-ui> is present
29
25
  */
30
26
 
31
27
  import { UIElement } from '../../../web-components/core/element.js';
@@ -37,100 +33,49 @@ class AdminShell extends UIElement {
37
33
 
38
34
  static template = () => null;
39
35
 
40
- #cmdKeyHandler = null;
41
- #legacyResizeCleanups = [];
42
- #legacySidebarRO = null;
43
- #legacySidebarWidths = new Map();
44
-
45
36
  connected() {
46
37
  this.#wireToggleButtons();
47
38
  this.#wireCommandTriggers();
48
- this.#setupLegacyShapes();
49
- }
50
-
51
- disconnected() {
52
- for (const cleanup of this.#legacyResizeCleanups) cleanup();
53
- this.#legacyResizeCleanups = [];
54
- this.#legacySidebarRO?.disconnect();
55
- this.#legacySidebarRO = null;
56
- if (this.#cmdKeyHandler) {
57
- document.removeEventListener('keydown', this.#cmdKeyHandler);
58
- this.#cmdKeyHandler = null;
59
- }
60
39
  }
61
40
 
62
- // Selector that matches BOTH bespoke <admin-sidebar slot> AND legacy
63
- // shapes. Used by toggle-button wiring + ResizeObserver setup.
64
- static #SIDEBAR_SEL =
65
- ':is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"], ' +
66
- '[data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"])';
67
-
68
- #sidebarName(el) {
69
- return (
70
- el.getAttribute('name') ??
71
- el.getAttribute('data-sidebar') ??
72
- el.getAttribute('slot')
73
- );
74
- }
41
+ // No #disconnected children own their own cleanup; the host registers
42
+ // no document-level listeners and no observers. Element-level listeners
43
+ // are auto-cleaned when the element disconnects.
75
44
 
76
45
  #findSidebar(name) {
77
46
  return this.querySelector(
78
- ':is(' +
79
- `admin-sidebar[slot="${name}"], admin-sidebar[name="${name}"], ` +
80
- `[data-sidebar="${name}"], aside-ui[slot="${name}"]` +
81
- ')'
47
+ `admin-sidebar[slot="${name}"], admin-sidebar[name="${name}"]`
82
48
  );
83
49
  }
84
50
 
85
- // ── 1. Toggle buttons — works for bespoke + legacy ──
51
+ // ── 1. Toggle buttons — forward clicks to <admin-sidebar>.toggle() ──
86
52
 
87
53
  #wireToggleButtons() {
88
54
  for (const btn of this.querySelectorAll('[data-sidebar-toggle]')) {
89
55
  const name = btn.getAttribute('data-sidebar-toggle');
90
56
  btn.addEventListener('click', () => {
91
57
  const sidebar = this.#findSidebar(name);
92
- if (!sidebar) return;
93
-
94
- // Bespoke <admin-sidebar> has a public toggle() method
95
- if (typeof sidebar.toggle === 'function') {
96
- sidebar.toggle();
97
- return;
98
- }
99
-
100
- // Legacy: replicate the old in-place toggle on raw HTML
101
- this.#legacyToggle(sidebar, name);
58
+ sidebar?.toggle?.();
102
59
  });
103
60
  }
104
61
  }
105
62
 
106
- // ── 2. Command triggers — works for bespoke + legacy ──
63
+ // ── 2. Command triggers — forward clicks to <admin-command>.show() ──
107
64
 
108
65
  #wireCommandTriggers() {
109
66
  const command = this.querySelector('admin-command');
110
- const legacyDialog = command ? null : this.querySelector('dialog[data-command]');
67
+ if (!command) return;
111
68
 
112
- if (!command && !legacyDialog) return;
113
-
114
- // Bespoke path: trigger calls .show() on <admin-command>
115
- if (command) {
116
- for (const trigger of this.querySelectorAll('[data-command-trigger]')) {
117
- trigger.addEventListener('click', (e) => {
118
- e.stopPropagation();
119
- command.show();
120
- });
121
- }
122
- // <admin-command> owns its own keyboard shortcut; nothing more
123
- // for the host to wire. We DO re-listen for command-select to
124
- // wire navigation if a <nav-ui> is present.
125
- this.#wireCommandSelectToNav(command);
126
- return;
69
+ for (const trigger of this.querySelectorAll('[data-command-trigger]')) {
70
+ trigger.addEventListener('click', (e) => {
71
+ e.stopPropagation();
72
+ command.show();
73
+ });
127
74
  }
128
75
 
129
- // Legacy path: replicate the old wiring around <dialog data-command>
130
- this.#wireLegacyCommand(legacyDialog);
131
- }
132
-
133
- #wireCommandSelectToNav(command) {
76
+ // <admin-command> owns its own Cmd+K listener. The host only wires
77
+ // command-select → nav routing if a <nav-ui> is present (so clicking
78
+ // a result in the palette navigates to that route).
134
79
  const nav = this.querySelector('nav-ui');
135
80
  if (!nav) return;
136
81
  command.addEventListener('command-select', (e) => {
@@ -138,167 +83,6 @@ class AdminShell extends UIElement {
138
83
  if (item) nav.select(item);
139
84
  });
140
85
  }
141
-
142
- // ── 3. Legacy shape wiring (raw <aside data-sidebar>, <dialog data-command>) ──
143
-
144
- #setupLegacyShapes() {
145
- const legacySidebars = this.querySelectorAll(
146
- ':is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"])'
147
- );
148
- if (legacySidebars.length === 0) return;
149
-
150
- this.#restoreLegacyWidths(legacySidebars);
151
- this.#setupLegacyResizeHandles(legacySidebars);
152
- this.#setupLegacyResizeObserver(legacySidebars);
153
- }
154
-
155
- #restoreLegacyWidths(sidebars) {
156
- for (const sidebar of sidebars) {
157
- const name = this.#sidebarName(sidebar);
158
- try {
159
- const saved = localStorage.getItem(`adia-sidebar-${name}`);
160
- if (saved) {
161
- sidebar.style.width = saved;
162
- const w = parseFloat(saved);
163
- if (!isNaN(w) && w > 96) {
164
- this.#legacySidebarWidths.set(name, saved);
165
- }
166
- }
167
- } catch {}
168
- }
169
- }
170
-
171
- #legacyToggle(sidebar, name) {
172
- const isCollapsed = sidebar.getBoundingClientRect().width <= 96;
173
- if (isCollapsed) {
174
- const prev = this.#legacySidebarWidths.get(name);
175
- sidebar.style.width = prev || '';
176
- try { localStorage.setItem(`adia-sidebar-${name}`, prev || ''); } catch {}
177
- } else {
178
- this.#legacySidebarWidths.set(name, sidebar.style.width || getComputedStyle(sidebar).width);
179
- const minW = getComputedStyle(sidebar).minWidth;
180
- sidebar.style.width = minW;
181
- try { localStorage.setItem(`adia-sidebar-${name}`, minW); } catch {}
182
- }
183
- this.dispatchEvent(new CustomEvent('sidebar-toggle', {
184
- bubbles: true,
185
- detail: { sidebar: name, name, expanded: !isCollapsed },
186
- }));
187
- }
188
-
189
- #setupLegacyResizeHandles(sidebars) {
190
- for (const sidebar of sidebars) {
191
- const handle = sidebar.querySelector(':scope > [data-resize]');
192
- if (!handle) continue;
193
- const name = this.#sidebarName(sidebar);
194
- const isLeading = name === 'leading';
195
-
196
- const onPointerDown = (e) => {
197
- e.preventDefault();
198
- handle.setPointerCapture(e.pointerId);
199
- const startX = e.clientX;
200
- const startW = sidebar.getBoundingClientRect().width;
201
- sidebar.setAttribute('data-resizing', '');
202
- document.documentElement.style.cursor = 'col-resize';
203
-
204
- const onMove = (e) => {
205
- const dx = e.clientX - startX;
206
- const max = parseInt(getComputedStyle(sidebar).getPropertyValue('max-width')) || 480;
207
- const w = Math.max(48, Math.min(max, startW + (isLeading ? dx : -dx)));
208
- sidebar.style.width = `${w}px`;
209
- };
210
-
211
- const onUp = () => {
212
- sidebar.removeAttribute('data-resizing');
213
- document.documentElement.style.cursor = '';
214
- handle.removeEventListener('pointermove', onMove);
215
- handle.removeEventListener('pointerup', onUp);
216
-
217
- const w = sidebar.getBoundingClientRect().width;
218
- if (w <= 96) {
219
- sidebar.style.width = getComputedStyle(sidebar).minWidth;
220
- } else if (w < 160) {
221
- sidebar.style.width = '160px';
222
- }
223
- try { localStorage.setItem(`adia-sidebar-${name}`, sidebar.style.width); } catch {}
224
-
225
- this.dispatchEvent(new CustomEvent('sidebar-resize', {
226
- bubbles: true,
227
- detail: { sidebar: name, name, width: sidebar.getBoundingClientRect().width },
228
- }));
229
- };
230
-
231
- handle.addEventListener('pointermove', onMove);
232
- handle.addEventListener('pointerup', onUp);
233
- };
234
-
235
- handle.addEventListener('pointerdown', onPointerDown);
236
- this.#legacyResizeCleanups.push(() => handle.removeEventListener('pointerdown', onPointerDown));
237
- }
238
- }
239
-
240
- #setupLegacyResizeObserver(sidebars) {
241
- this.#legacySidebarRO = new ResizeObserver((entries) => {
242
- for (const entry of entries) {
243
- const sidebar = entry.target;
244
- const narrow = entry.contentBoxSize[0].inlineSize <= 96;
245
- for (const sel of sidebar.querySelectorAll('select-ui')) {
246
- sel.setAttribute('placement', narrow ? 'right' : 'bottom-start');
247
- }
248
- }
249
- });
250
- for (const sb of sidebars) this.#legacySidebarRO.observe(sb);
251
- }
252
-
253
- #wireLegacyCommand(dialog) {
254
- const cmdEl = dialog.querySelector('command-ui');
255
- const nav = this.querySelector('nav-ui');
256
-
257
- const openCmd = () => {
258
- dialog.showModal();
259
- if (cmdEl) { cmdEl.open = true; cmdEl.value = ''; cmdEl.focus(); }
260
- };
261
- const closeCmd = () => {
262
- dialog.close();
263
- if (cmdEl) cmdEl.open = false;
264
- };
265
-
266
- for (const trigger of this.querySelectorAll('[data-command-trigger]')) {
267
- trigger.addEventListener('click', (e) => {
268
- e.stopPropagation();
269
- openCmd();
270
- });
271
- }
272
-
273
- dialog.addEventListener('click', (e) => {
274
- if (e.target === dialog) closeCmd();
275
- });
276
-
277
- if (cmdEl) {
278
- cmdEl.addEventListener('dismiss', closeCmd);
279
- cmdEl.addEventListener('select', (e) => {
280
- closeCmd();
281
- if (nav) {
282
- const item = nav.querySelector(`nav-item-ui[value="${e.detail.value}"]`);
283
- if (item) nav.select(item);
284
- }
285
- this.dispatchEvent(new CustomEvent('command-select', {
286
- bubbles: true,
287
- detail: e.detail,
288
- }));
289
- });
290
- }
291
-
292
- // Cmd+K listener — only for legacy shape (bespoke <admin-command>
293
- // owns its own listener)
294
- this.#cmdKeyHandler = (e) => {
295
- if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
296
- e.preventDefault();
297
- dialog.open ? closeCmd() : openCmd();
298
- }
299
- };
300
- document.addEventListener('keydown', this.#cmdKeyHandler);
301
- }
302
86
  }
303
87
 
304
88
  customElements.define('admin-shell', AdminShell);
@@ -1,20 +1,16 @@
1
1
  /* ═══════════════════════════════════════════════════════════════
2
- admin-shell — Bespoke shell-tier children (Phase 2 of ADR-0023)
3
-
4
- The admin-* CSS-only structural children are styled by SHARING the
5
- same CSS rules as their legacy raw-HTML counterparts. This file
6
- re-applies the existing selectors (main / section / header / footer)
7
- to the new bespoke tags via display + structural mappings.
8
-
9
- Rather than duplicate every rule in main.css / sidebar.css /
10
- templates.css, we declare the bespoke tags as `display: contents` or
11
- the relevant layout box, then let the existing selectors (which
12
- already use :is() lifts for legacy + slot-vocabulary) match.
13
-
14
- For Phase 2, the simplest map: each bespoke tag mimics its legacy
15
- counterpart's display behavior. The host shell.css already handles
16
- admin-shell flex layout; main.css handles main-as-column; sidebar.css
17
- handles sidebar geometry.
2
+ admin-shell — Bespoke shell-tier children (canonical since v0.4.0)
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:
6
+ admin-content, admin-topbar, admin-statusbar, admin-scroll,
7
+ admin-page, admin-page-header, admin-page-body, admin-sidebar.
8
+
9
+ 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.
18
14
  ═══════════════════════════════════════════════════════════════ */
19
15
 
20
16
  /* ── admin-content ≡ <main> ── */
@@ -30,7 +26,7 @@ admin-shell > admin-content {
30
26
  /* ── admin-topbar ≡ <header-ui> at shell tier ── */
31
27
  admin-content > admin-topbar,
32
28
  admin-shell > admin-topbar,
33
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"], admin-sidebar) > admin-topbar {
29
+ admin-sidebar > admin-topbar {
34
30
  display: flex;
35
31
  align-items: center;
36
32
  gap: var(--page-header-gap);
@@ -44,7 +40,7 @@ admin-shell > admin-topbar,
44
40
  /* ── admin-statusbar ≡ <footer-ui> at shell tier ── */
45
41
  admin-content > admin-statusbar,
46
42
  admin-shell > admin-statusbar,
47
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"], admin-sidebar) > admin-statusbar {
43
+ admin-sidebar > admin-statusbar {
48
44
  display: flex;
49
45
  align-items: center;
50
46
  gap: var(--page-header-gap);
@@ -98,13 +94,13 @@ admin-page > admin-page-body {
98
94
  flex-direction: column;
99
95
  }
100
96
 
101
- /* ── admin-sidebar — bridge to existing sidebar.css selectors ──
102
- Rather than expand every :is([data-sidebar], aside-ui[slot=…], …)
103
- selector in sidebar.css with `, admin-sidebar[slot=…]`, we apply the
104
- core sidebar geometry + container query rules here. The remaining
105
- slot-routing rules in sidebar.css (header/footer chrome, section,
106
- nav-ui colors) match by descendant selector rather than parent
107
- tag, so they pick up admin-sidebar children automatically. */
97
+ /* ── admin-sidebar — geometry + resize handle + container queries ──
98
+ Canonical styling for <admin-sidebar> per ADR-0023. Pre-v0.4.0 this
99
+ bridged to legacy <aside-ui slot> / <aside data-sidebar> selectors;
100
+ in v0.4.0 those layered-CSS legacy lifts were stripped, so this block
101
+ now carries the full sidebar styling. The descendant rules in
102
+ sidebar.css (header/footer chrome, section, nav-ui colors) still
103
+ match via tag-name descendant selectors. */
108
104
 
109
105
  :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) {
110
106
  display: flex;
@@ -131,7 +127,7 @@ admin-sidebar[slot="trailing"] {
131
127
  border-left: var(--page-border);
132
128
  }
133
129
 
134
- /* Resize handle — same shape as legacy [data-sidebar] > [data-resize] */
130
+ /* Resize handle */
135
131
  :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > [data-resize] {
136
132
  position: absolute;
137
133
  top: 0;
@@ -36,7 +36,7 @@ admin-shell > main {
36
36
  OR a main-level chrome bar (`> main > :is(header, header-ui)` etc.).
37
37
  Either qualifies as "chrome present" and suppresses the
38
38
  corresponding margin. */
39
- admin-shell:not(:has(> :is(aside[data-sidebar="trailing"], aside-ui[slot="trailing"]):not([hidden]))) > main > :is(section, section-ui) {
39
+ admin-shell:not(:has(> :is(asideadmin-sidebar[slot="trailing"], admin-sidebar[slot="trailing"]):not([hidden]))) > main > :is(section, section-ui) {
40
40
  margin-inline-end: var(--a-space-2);
41
41
  }
42
42
  admin-shell:not(:has(> :is(header, header-ui):not([hidden]), > main > :is(header, header-ui):not([hidden]))) > main > :is(section, section-ui) {
@@ -49,7 +49,7 @@ admin-shell:not(:has(> :is(footer, footer-ui):not([hidden]), > main > :is(footer
49
49
  /* ── Main > header (topbar) ──
50
50
  Contains: sidebar toggle, breadcrumb, spacer, action buttons.
51
51
 
52
- Slot contract (shared with > main > footer and [data-sidebar] >
52
+ Slot contract (shared with > main > footer and admin-sidebar >
53
53
  header/footer) — identical to card-ui / drawer-ui / editor-shell:
54
54
  [slot="icon"] leading glyph
55
55
  [slot="heading"] primary label; strong weight + strong fg
@@ -3,7 +3,7 @@
3
3
 
4
4
  Structure (legacy raw-HTML form; slot-vocabulary equivalents in parens):
5
5
  admin-shell — root shell (flex row, fixed viewport)
6
- aside[data-sidebar="leading"] (or aside-ui[slot="leading"]) — nav sidebar (resizable, collapsible)
6
+ asideadmin-sidebar[slot="leading"] (or admin-sidebar[slot="leading"]) — nav sidebar (resizable, collapsible)
7
7
  main — center column (topbar + scroll + footer)
8
8
  header (or header-ui) — topbar
9
9
  section (or section-ui) — scroll container
@@ -11,7 +11,7 @@
11
11
  div[data-content-header] > header — sticky page title + tabs
12
12
  div[data-content-body] > section — centered reading column
13
13
  footer (or footer-ui) — status bar
14
- aside[data-sidebar="trailing"] (or aside-ui[slot="trailing"]) — inspector sidebar
14
+ asideadmin-sidebar[slot="trailing"] (or admin-sidebar[slot="trailing"]) — inspector sidebar
15
15
  dialog[data-command] — command palette
16
16
 
17
17
  Both authoring shapes are valid simultaneously. The CSS uses
@@ -5,12 +5,14 @@
5
5
  Acts as a CSS container query provider (container-name: sidebar)
6
6
  so children can adapt to collapsed width via @container.
7
7
 
8
- Authoring shapes (both styled identically):
9
- - Legacy: <aside data-sidebar="leading|trailing">
10
- - Slot vocabulary: <aside-ui slot="leading|trailing">
8
+ Canonical authoring shape (since v0.4.0, ADR-0023 Phase 3):
9
+ <admin-sidebar slot="leading|trailing" resizable collapsible>
10
+
11
+ Pre-v0.4.0 legacy shapes (<aside data-sidebar>, <aside-ui slot>)
12
+ were retired; see ADR-0024 + git history for the migration recipe.
11
13
  ═══════════════════════════════════════════════════════════════ */
12
14
 
13
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) {
15
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) {
14
16
  display: flex;
15
17
  flex-direction: column;
16
18
  flex-shrink: 0;
@@ -27,7 +29,7 @@
27
29
  /* ── Resize handle ──
28
30
  6px invisible hit area straddling the sidebar edge.
29
31
  Accent color on hover/drag. */
30
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > [data-resize] {
32
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > [data-resize] {
31
33
  position: absolute;
32
34
  top: 0;
33
35
  bottom: 0;
@@ -38,29 +40,29 @@
38
40
  z-index: 2;
39
41
  }
40
42
 
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
+ admin-sidebar[slot="leading"] > [data-resize] { right: -3px; }
44
+ admin-sidebar[slot="trailing"] > [data-resize] { left: -3px; }
43
45
 
44
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > [data-resize]:hover {
46
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > [data-resize]:hover {
45
47
  background: var(--page-sidebar-resize-accent);
46
48
  }
47
49
 
48
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"])[data-resizing] > [data-resize] {
50
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"])[data-resizing] > [data-resize] {
49
51
  background: var(--page-sidebar-resize-accent);
50
52
  }
51
53
 
52
54
  /* During drag: disable transition + text selection */
53
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"])[data-resizing] {
55
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"])[data-resizing] {
54
56
  user-select: none;
55
57
  transition: none;
56
58
  }
57
59
 
58
60
  /* ── Sidebar widths ── */
59
- :is([data-sidebar="leading"], aside-ui[slot="leading"]) {
61
+ admin-sidebar[slot="leading"] {
60
62
  width: var(--page-sidebar-width-leading);
61
63
  }
62
64
 
63
- :is([data-sidebar="trailing"], aside-ui[slot="trailing"]) {
65
+ admin-sidebar[slot="trailing"] {
64
66
  width: var(--page-sidebar-width-trailing);
65
67
  }
66
68
 
@@ -71,8 +73,8 @@
71
73
  (a workspace-select or user-select) so the slot rules rarely come
72
74
  into play — but they stay consistent for authors that compose
73
75
  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
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(header, header-ui),
77
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(footer, footer-ui) {
76
78
  display: flex;
77
79
  align-items: center;
78
80
  gap: var(--page-sidebar-gap);
@@ -82,49 +84,49 @@
82
84
  min-width: 0;
83
85
  }
84
86
 
85
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(header, header-ui) {
87
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(header, header-ui) {
86
88
  min-height: var(--page-header-height);
87
89
  border-bottom: var(--page-border);
88
90
  }
89
91
 
90
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(footer, footer-ui) {
92
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(footer, footer-ui) {
91
93
  min-height: var(--page-header-height);
92
94
  margin-top: auto;
93
95
  border-top: var(--page-border);
94
96
  }
95
97
 
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
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(header, header-ui) > [slot="icon"],
99
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(footer, footer-ui) > [slot="icon"] {
98
100
  display: flex;
99
101
  align-items: center;
100
102
  flex-shrink: 0;
101
103
  color: var(--page-header-fg-muted);
102
104
  }
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
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(header, header-ui) > [slot="heading"],
106
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(footer, footer-ui) > [slot="heading"] {
105
107
  font-weight: var(--a-weight-medium);
106
108
  color: var(--a-fg);
107
109
  }
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
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(header, header-ui) > [slot="description"],
111
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(footer, footer-ui) > [slot="description"] {
110
112
  color: var(--page-header-fg-muted);
111
113
  font-size: var(--a-ui-sm);
112
114
  }
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
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(header, header-ui) > [slot="action"],
116
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(footer, footer-ui) > [slot="action"] {
115
117
  display: flex;
116
118
  align-items: center;
117
119
  gap: var(--page-actions-gap);
118
120
  flex-shrink: 0;
119
121
  margin-inline-start: auto;
120
122
  }
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
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(header, header-ui) > [slot="action"] ~ [slot="action"],
124
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(footer, footer-ui) > [slot="action"] ~ [slot="action"] {
123
125
  margin-inline-start: 0;
124
126
  }
125
127
  /* 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
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(header, header-ui) > [slot="action-leading"],
129
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(footer, footer-ui) > [slot="action-leading"] {
128
130
  display: flex;
129
131
  align-items: center;
130
132
  gap: var(--page-actions-gap);
@@ -133,7 +135,7 @@
133
135
  }
134
136
 
135
137
  /* ── Sidebar section (scrollable body) ── */
136
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(section, section-ui) {
138
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(section, section-ui) {
137
139
  flex: 1;
138
140
  overflow-y: auto;
139
141
  padding: var(--page-sidebar-px);
@@ -141,23 +143,23 @@
141
143
  scrollbar-width: none;
142
144
  }
143
145
 
144
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) > :is(section, section-ui)::-webkit-scrollbar { display: none; }
146
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(section, section-ui)::-webkit-scrollbar { display: none; }
145
147
 
146
148
  /* span[slot="pad"] — opt-in padding wrapper for sidebar content
147
149
  that needs inset (e.g. trailing sidebar inspector fields) */
148
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) span[slot="pad"] {
150
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) span[slot="pad"] {
149
151
  display: block;
150
152
  padding: var(--page-sidebar-px);
151
153
  }
152
154
 
153
155
  /* Reset nav-ui border/padding inside sidebars */
154
- :is([data-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"]) nav-ui {
156
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) nav-ui {
155
157
  border: none;
156
158
  padding: 0;
157
159
  }
158
160
 
159
161
  /* 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] {
162
+ :is(admin-sidebar[slot="leading"], admin-sidebar[slot="trailing"]) > :is(section, section-ui) > hr[data-nav-divider] {
161
163
  border: none;
162
164
  height: 1px;
163
165
  background: var(--page-sidebar-divider-bg);
@@ -0,0 +1,2 @@
1
+ export { SimpleShell } from './simple-shell/simple-shell.js';
2
+ // simple-content and simple-hero are CSS-only — no JS export needed.