@adia-ai/web-modules 0.6.15 → 0.6.17
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,39 @@
|
|
|
1
1
|
# Changelog — @adia-ai/web-modules
|
|
2
2
|
|
|
3
|
+
## [0.6.17] — 2026-05-21
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Collapsed sidebar — text labels overflow for vanilla HTML consumers.** When `<admin-sidebar>` collapses to its ≤96px icon-rail state, the existing `@container sidebar (max-width: 96px)` rules in `admin-shell.collapsed.css` target only the AdiaUI primitives (`<nav-ui>`, `<nav-item-ui>`, `<select-ui>`, `<button-ui>`). Consumers that use vanilla HTML inside the sidebar — `<button class="nav-item">Label</button>` with native text nodes, or `<span slot="heading">Brand</span>` for the app wordmark — were not covered, and their text content overflowed the 48px rail via `overflow: visible`. Symptom: "Claims UI" wordmark wrapped to two lines, and nav labels (Dashboard/Claims/Users/Settings) rendered visibly past the rail edge into the content area. Reported by claims-ui-v3 cold-start v0.6.16 screenshot.
|
|
7
|
+
|
|
8
|
+
Fix adds a forgiving fallback block to `admin-shell.collapsed.css`:
|
|
9
|
+
- Clips overflow on the sidebar itself + chrome containers (`admin-topbar`, `admin-statusbar`, `section`, `section-ui`, `nav`) so escaping text descendants stay inside the rail edge.
|
|
10
|
+
- Hides `[slot="heading"]` children — authors who want a brand mark visible when collapsed should put an `<icon-ui>` or logo image in `[slot="leading"]` instead.
|
|
11
|
+
- For vanilla `<button class="nav-item">`, `<a class="nav-item">`, and `[data-nav]` — applies `justify-content: center`, `overflow: hidden`, `text-indent: -9999px`, `white-space: nowrap` to clip text labels while preserving icons. Restores `text-indent: 0` on child `<icon-ui>` so the icon itself stays visible.
|
|
12
|
+
|
|
13
|
+
Using `<nav-item-ui>` / `<nav-ui>` remains the canonical, more capable path — popover tooltips on hover, badge slots, separator drawing all work via the primitives. The fallback is a safety net for consumers who don't (yet) wire the AdiaUI primitive stack inside the sidebar. Files: `packages/web-modules/shell/admin-shell/css/admin-shell.collapsed.css` (+60 LOC).
|
|
14
|
+
|
|
15
|
+
### Verification
|
|
16
|
+
- `components.mjs --verify` clean (142 files up-to-date).
|
|
17
|
+
- `verify:traits` 56/56 clean.
|
|
18
|
+
- `check:lockstep` — all 9 at 0.6.17, ranges at ^0.6.0.
|
|
19
|
+
- `eval:diff --engine zettel` cov=49% / avg=90 (baseline-identical).
|
|
20
|
+
- Browser-verified at `http://localhost:5175/` (claims-ui-v3, after copying the updated CSS to local `node_modules`): toggling the sidebar to collapsed state hides `[slot="heading"]` (display=none), applies `text-indent: -9999px` + `overflow: hidden` to vanilla nav buttons, and preserves icon visibility via `text-indent: 0` on `<icon-ui>` descendants. Vision pass confirms no overflow, no "Claims UI" wrapping, icons stacked vertically in the rail.
|
|
21
|
+
|
|
22
|
+
## [0.6.16] — 2026-05-21
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- **`<admin-page>` layout breaks at narrow viewports when wrapped in `<router-ui>` (sticky-overlap regression).** The bespoke selectors `admin-scroll > admin-page, admin-content > admin-page` use the direct-child combinator. When a router (e.g. `<router-ui>` in chat-ui's docs-site, or any consumer's SPA router) sits between `<admin-scroll>` and `<admin-page>` to stamp routed content, the selector misses, `<admin-page>` falls back to `HTMLUnknownElement` (`display: inline`), and its sticky/flex children silently fail layout. At wide viewports the inline flow happens to land roughly where flex would have placed it; at narrower widths (~700–900px), `<admin-page-header>` (sticky inside inline parent) overlaps `<admin-page-body>` content — the user's screenshot showed h1 "Dashboard" + tabs rendering ON TOP OF the KPI cards (Total Revenue, Subscriptions). Fix: relax the selector to use the descendant combinator (`:is(admin-scroll, admin-content) admin-page`) so any depth of intermediate wrapping (`router-ui`, `slot`, generic divs) still picks up the canonical `display: flex; flex-direction: column; min-height: 100%` layout. `<admin-page>` is never legitimately nested inside another `<admin-page>`, so depth-1 restriction isn't needed. Files: `packages/web-modules/shell/admin-shell/css/admin-shell.bespoke.css` (selector relaxation + inline doc comment explaining the trap).
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- **`--page-content-header-bg` token: `--a-canvas-2` → `--a-canvas-1`.** The page-header band now matches the shell + sidebar canvas (`oklch(0.19 0.01 225)` in dark mode) so the admin chrome reads as a continuous wrapper around the page-body's content surface (canvas-0). Previous v0.6.14 value (canvas-2, ΔL=0.08) was chosen under the "elevated step over content" intent, but vision-tests against dense layouts at 1572px viewport in dark mode showed the band still read as flush with body — the ΔL=0.08 step is below human-perceptible threshold for a band of this proportion against this surrounding content density. Reframing as **shell-chrome continuation** rather than as elevated step: the page-header reads as the horizontal arm of the shell's L-shape wrapping the rounded content area. Consumers who want a stronger raised band can override the token at `:root`. Files: `packages/web-modules/shell/admin-shell/css/admin-shell.tokens.css` (token value + history comment).
|
|
29
|
+
|
|
30
|
+
### Verification
|
|
31
|
+
- `components.mjs --verify` clean (142 files up-to-date).
|
|
32
|
+
- `verify:traits` 56/56 clean.
|
|
33
|
+
- `check:lockstep` — all 9 at 0.6.16, ranges at ^0.6.0.
|
|
34
|
+
- `eval:diff --engine zettel` cov=49% / avg=90 (baseline-identical).
|
|
35
|
+
- Browser-verified at `/site/examples/admin-dashboard` in dark mode at 1280px viewport: `admin-page` computed `display: flex` (was `inline` pre-fix), `flex-direction: column`; `admin-page-header` computed `display: block` (was `inline` pre-fix), `position: sticky`, bg `oklch(0.19 0.01 225)` matches sidebar canvas. Vision pass confirms no overlap between page-header and KPI cards across the layout.
|
|
36
|
+
|
|
3
37
|
## [0.6.15] — 2026-05-21
|
|
4
38
|
|
|
5
39
|
### Maintenance
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/web-modules",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.17",
|
|
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": {
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname, resolve } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const bespokeCSS = readFileSync(resolve(__dirname, 'css/admin-shell.bespoke.css'), 'utf8');
|
|
8
|
+
const collapsedCSS = readFileSync(resolve(__dirname, 'css/admin-shell.collapsed.css'), 'utf8');
|
|
9
|
+
|
|
10
|
+
// ── Helpers ────────────────────────────────────────────────────────
|
|
11
|
+
function mount(html) {
|
|
12
|
+
document.body.innerHTML = html;
|
|
13
|
+
return document.body.firstElementChild;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
document.body.innerHTML = '';
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// ── Regression guards ─────────────────────────────────────────────
|
|
21
|
+
//
|
|
22
|
+
// These tests document substrate selector decisions that have been
|
|
23
|
+
// regressed and re-fixed at least once each. They run against the raw
|
|
24
|
+
// CSS file content + DOM `element.matches()` rather than computed style,
|
|
25
|
+
// because happy-dom doesn't fully resolve external stylesheet cascade.
|
|
26
|
+
// The trade-off: we don't assert that the rule WAS applied, but we DO
|
|
27
|
+
// assert that the rule's selector REACHES the element it's meant to
|
|
28
|
+
// style — which is the part that broke in v0.6.16.
|
|
29
|
+
|
|
30
|
+
describe('admin-shell.bespoke.css — selector reachability', () => {
|
|
31
|
+
it('v0.6.16 fix: admin-page rule uses descendant combinator (not >)', () => {
|
|
32
|
+
// The previous selector `admin-scroll > admin-page` broke when a
|
|
33
|
+
// router (e.g. <router-ui>) sat between <admin-scroll> and
|
|
34
|
+
// <admin-page>. The fix uses :is() + descendant combinator so any
|
|
35
|
+
// depth of intermediate wrapping still matches.
|
|
36
|
+
expect(bespokeCSS).toMatch(/:is\(admin-scroll,\s*admin-content\)\s+admin-page\s*\{/);
|
|
37
|
+
// Negative assertion: the broken direct-child form should not be
|
|
38
|
+
// present any more (catches accidental reverts).
|
|
39
|
+
expect(bespokeCSS).not.toMatch(/^admin-scroll\s*>\s*admin-page\s*,/m);
|
|
40
|
+
expect(bespokeCSS).not.toMatch(/^admin-content\s*>\s*admin-page\s*\{/m);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('admin-page rule reaches admin-page inside <router-ui>', () => {
|
|
44
|
+
// Synthesize the docs-site DOM shape that hit the v0.6.16 bug.
|
|
45
|
+
// The selector under test must match <admin-page> via descendant
|
|
46
|
+
// combinator even when <router-ui> sits between.
|
|
47
|
+
mount(`
|
|
48
|
+
<admin-shell>
|
|
49
|
+
<admin-content>
|
|
50
|
+
<admin-scroll>
|
|
51
|
+
<router-ui id="router">
|
|
52
|
+
<admin-page id="page"></admin-page>
|
|
53
|
+
</router-ui>
|
|
54
|
+
</admin-scroll>
|
|
55
|
+
</admin-content>
|
|
56
|
+
</admin-shell>
|
|
57
|
+
`);
|
|
58
|
+
const page = document.getElementById('page');
|
|
59
|
+
expect(page).toBeTruthy();
|
|
60
|
+
// Direct-child match should fail (router-ui sits between)
|
|
61
|
+
expect(page.matches('admin-scroll > admin-page')).toBe(false);
|
|
62
|
+
expect(page.matches('admin-content > admin-page')).toBe(false);
|
|
63
|
+
// Descendant match must succeed (this is what v0.6.17 introduced)
|
|
64
|
+
expect(page.matches(':is(admin-scroll, admin-content) admin-page')).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('admin-page rule still reaches admin-page as direct child', () => {
|
|
68
|
+
// Canonical playground/example layout — no router wrapper.
|
|
69
|
+
// The descendant combinator must still match this case.
|
|
70
|
+
mount(`
|
|
71
|
+
<admin-shell>
|
|
72
|
+
<admin-content>
|
|
73
|
+
<admin-scroll>
|
|
74
|
+
<admin-page id="page"></admin-page>
|
|
75
|
+
</admin-scroll>
|
|
76
|
+
</admin-content>
|
|
77
|
+
</admin-shell>
|
|
78
|
+
`);
|
|
79
|
+
const page = document.getElementById('page');
|
|
80
|
+
expect(page.matches(':is(admin-scroll, admin-content) admin-page')).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('admin-shell.collapsed.css — vanilla HTML fallback (v0.6.17)', () => {
|
|
85
|
+
it('contains the vanilla-HTML fallback block', () => {
|
|
86
|
+
// v0.6.17 added rules covering <button class="nav-item"> etc. so
|
|
87
|
+
// text labels don't overflow the 48px rail when the consumer doesn't
|
|
88
|
+
// use AdiaUI <nav-item-ui> primitives.
|
|
89
|
+
expect(collapsedCSS).toMatch(/Vanilla-HTML fallback/);
|
|
90
|
+
expect(collapsedCSS).toMatch(/\[slot="heading"\]\s*\{[\s\S]*?display:\s*none/);
|
|
91
|
+
expect(collapsedCSS).toMatch(/button\.nav-item/);
|
|
92
|
+
expect(collapsedCSS).toMatch(/text-indent:\s*-9999px/);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('preserves icon visibility inside vanilla nav buttons', () => {
|
|
96
|
+
// text-indent:-9999px on the parent must be undone on the icon
|
|
97
|
+
// child, otherwise the icon disappears with the text. Both rules
|
|
98
|
+
// must be present in the file.
|
|
99
|
+
expect(collapsedCSS).toMatch(/button\.nav-item\s*>\s*icon-ui[\s\S]*?text-indent:\s*0/);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('fallback rules sit inside the @container sidebar query', () => {
|
|
103
|
+
// The vanilla-HTML fallback must be scoped to the collapsed state
|
|
104
|
+
// (max-width: 96px container query). Outside that scope, the
|
|
105
|
+
// text-indent: -9999px clip would also break the expanded state.
|
|
106
|
+
const containerMatch = collapsedCSS.match(/@container\s+sidebar\s*\(max-width:\s*96px\)\s*\{([\s\S]+)\}/);
|
|
107
|
+
expect(containerMatch).toBeTruthy();
|
|
108
|
+
const inside = containerMatch[1];
|
|
109
|
+
expect(inside).toMatch(/Vanilla-HTML fallback/);
|
|
110
|
+
expect(inside).toMatch(/text-indent:\s*-9999px/);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -72,9 +72,17 @@ admin-shell[mode~="rounded"] > admin-content > admin-scroll {
|
|
|
72
72
|
border-radius: var(--page-content-radius);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
/* ── admin-page ≡ <article data-content-root> ──
|
|
76
|
-
|
|
77
|
-
admin-
|
|
75
|
+
/* ── admin-page ≡ <article data-content-root> ──
|
|
76
|
+
Selector covers the canonical direct-child cases AND any indirect descent
|
|
77
|
+
(e.g. when <router-ui> sits between <admin-scroll> and <admin-page> to
|
|
78
|
+
stamp routed content; the docs-site at /site/* + every consumer using a
|
|
79
|
+
router pattern hits this layout). The previous `>` combinator silently
|
|
80
|
+
broke at narrow viewports because the unmatched <admin-page> rendered as
|
|
81
|
+
HTMLUnknownElement (display:inline) and its sticky/flex children
|
|
82
|
+
collapsed on top of each other. The descendant combinator is safe here:
|
|
83
|
+
<admin-page> is never legitimately nested inside another <admin-page>,
|
|
84
|
+
so we don't need depth-1 restriction. */
|
|
85
|
+
:is(admin-scroll, admin-content) admin-page {
|
|
78
86
|
display: flex;
|
|
79
87
|
flex-direction: column;
|
|
80
88
|
min-height: 100%;
|
|
@@ -83,4 +83,64 @@
|
|
|
83
83
|
button-ui [slot="trailing"] {
|
|
84
84
|
display: none;
|
|
85
85
|
}
|
|
86
|
+
|
|
87
|
+
/* ─── Vanilla-HTML fallback ───
|
|
88
|
+
The primitive-targeted rules above (nav-ui, nav-item-ui, button-ui) cover
|
|
89
|
+
the canonical AdiaUI composition. Consumers using vanilla HTML inside the
|
|
90
|
+
sidebar (e.g. <button class="nav-item">Label</button> with native text
|
|
91
|
+
nodes, or <span slot="heading">Brand</span>) need a forgiving fallback —
|
|
92
|
+
without it, text labels overflow the 48px collapsed bbox via
|
|
93
|
+
`overflow: visible` and spill into the content area, producing the
|
|
94
|
+
"Claims UI wraps to two lines + nav labels visible past the rail" bug
|
|
95
|
+
reported by claims-ui-v3 cold-start v0.6.16 §sidebar-overflow.
|
|
96
|
+
|
|
97
|
+
Strategy: clip the sidebar itself + hide common text-bearing slot
|
|
98
|
+
children. Icons (<icon-ui>) and avatars (<img>, <avatar-ui>) stay
|
|
99
|
+
visible. This is a safety net — using <nav-item-ui> / <nav-ui> remains
|
|
100
|
+
the canonical, more capable path (popover tooltips, badges, etc.). */
|
|
101
|
+
|
|
102
|
+
/* Clip the sidebar's chrome containers — text descendants that escape
|
|
103
|
+
their parent's width are now invisible past the rail edge. */
|
|
104
|
+
:scope,
|
|
105
|
+
:is(admin-topbar, admin-statusbar),
|
|
106
|
+
:is(section, section-ui, nav) {
|
|
107
|
+
overflow: hidden;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Hide [slot="heading"] (typically the app brand label in the sidebar's
|
|
111
|
+
<admin-topbar slot="header">) — keeps the rail clean in collapsed mode.
|
|
112
|
+
Authors who want a brand mark visible when collapsed should put an
|
|
113
|
+
<icon-ui> or logo image in [slot="leading"] instead. */
|
|
114
|
+
[slot="heading"] {
|
|
115
|
+
display: none;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Vanilla nav buttons: any <button class="nav-item"> or [data-nav] item
|
|
119
|
+
not built from nav-item-ui. Hide non-icon children so only the icon
|
|
120
|
+
remains visible. Detection: a child of the sidebar that is NOT
|
|
121
|
+
itself an icon/avatar primitive and is NOT an element wrapping such
|
|
122
|
+
a primitive — i.e., bare text/spans/labels. */
|
|
123
|
+
button.nav-item,
|
|
124
|
+
a.nav-item,
|
|
125
|
+
[data-nav] {
|
|
126
|
+
/* Center the icon; hide any text by clipping (we can't safely
|
|
127
|
+
:not(:has(icon-ui)) every child without false-positives, so we
|
|
128
|
+
clip the box and rely on flex centering the icon). */
|
|
129
|
+
justify-content: center;
|
|
130
|
+
overflow: hidden;
|
|
131
|
+
text-indent: -9999px; /* push label text off-screen */
|
|
132
|
+
white-space: nowrap;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* But: keep the icon inside the button visible. text-indent: -9999px
|
|
136
|
+
would push the icon too — so restore positioning for the icon child
|
|
137
|
+
and any element that wraps it. */
|
|
138
|
+
button.nav-item > icon-ui,
|
|
139
|
+
a.nav-item > icon-ui,
|
|
140
|
+
[data-nav] > icon-ui,
|
|
141
|
+
button.nav-item > :has(> icon-ui),
|
|
142
|
+
a.nav-item > :has(> icon-ui),
|
|
143
|
+
[data-nav] > :has(> icon-ui) {
|
|
144
|
+
text-indent: 0;
|
|
145
|
+
}
|
|
86
146
|
}
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
/* Content area — scroll region inside main */
|
|
40
40
|
--page-content-bg: var(--a-canvas-0); /* content surface (lighter than chrome) */
|
|
41
|
-
--page-content-header-bg: var(--a-canvas-
|
|
41
|
+
--page-content-header-bg: var(--a-canvas-1); /* sticky page-header band — matches sidebar/topbar canvas so the shell reads as a continuous L-shape of chrome wrapping the page-body. ΔL=0.04 above body canvas-0. History: v0.6.14 used canvas-2 (ΔL=0.08) under the "elevated step over content" intent, but vision-tests against dense layouts showed the band still read as flush with body. v0.6.15 reframes the page-header as shell-chrome continuation (canvas-1 matches sidebar) rather than as an elevated step — visual cohesion over contrast. Consumers who want a stronger raised band can override this token at :root. */
|
|
42
42
|
--page-content-radius: var(--a-radius-lg); /* used by "rounded" mode */
|
|
43
43
|
--page-content-inset: var(--a-space-10); /* padding for header/body/footer */
|
|
44
44
|
--page-content-max-width: 1540px; /* max-width for content children */
|