@adia-ai/web-components 0.7.10 → 0.7.12
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 +34 -2
- package/README.md +2 -2
- package/USAGE.md +2 -2
- package/components/button/button.css +43 -16
- package/components/frame/frame.a2ui.json +1 -1
- package/components/frame/frame.css +9 -6
- package/components/frame/frame.d.ts +5 -3
- package/components/frame/frame.yaml +7 -5
- package/components/list/list.css +4 -0
- package/components/preview/preview.class.js +9 -0
- package/components/theme-provider/theme-provider.a2ui.json +88 -0
- package/components/theme-provider/theme-provider.class.js +134 -0
- package/components/theme-provider/theme-provider.css +18 -0
- package/components/theme-provider/theme-provider.d.ts +45 -0
- package/components/theme-provider/theme-provider.js +22 -0
- package/components/theme-provider/theme-provider.test.js +94 -0
- package/components/theme-provider/theme-provider.yaml +96 -0
- package/dist/host.min.css +1 -1
- package/dist/host.sheet.js +11 -0
- package/dist/prose.min.css +1 -0
- package/dist/prose.sheet.js +11 -0
- package/dist/themes.min.css +1 -0
- package/dist/themes.sheet.js +11 -0
- package/dist/verse.min.css +1 -0
- package/dist/verse.sheet.js +11 -0
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +1 -1
- package/dist/web-components.sheet.js +11 -0
- package/package.json +5 -1
- package/styles/README.md +1 -1
- package/styles/colors/parameters.css +1 -1
- package/styles/colors/primitives-accent.css +1 -1
- package/styles/colors/primitives-brand.css +1 -1
- package/styles/colors/primitives-danger.css +1 -1
- package/styles/colors/primitives-info.css +1 -1
- package/styles/colors/primitives-neutral.css +1 -1
- package/styles/colors/primitives-success.css +1 -1
- package/styles/colors/primitives-warning.css +1 -1
- package/styles/colors/scrims.css +1 -1
- package/styles/colors/semantics/aliases.css +1 -1
- package/styles/colors/semantics/buckets.css +1 -1
- package/styles/colors/semantics/core.css +1 -1
- package/styles/colors/semantics/data-viz.css +1 -1
- package/styles/colors/semantics/features.css +1 -1
- package/styles/colors/surfaces.css +1 -1
- package/styles/components.css +1 -0
- package/styles/design-tokens-export.js +1 -1
- package/styles/host.css +1 -1
- package/styles/prose.css +1 -1
- package/styles/themes.css +12 -12
- package/styles/verse.css +1 -1
- package/traits/resizable/resizable.js +49 -36
- package/traits/resizable/resizable.test.js +19 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <theme-provider> tests
|
|
3
|
+
*
|
|
4
|
+
* The provider adopts the AdiaUI foundation (a constructable stylesheet) into
|
|
5
|
+
* `document.adoptedStyleSheets` — once, deduped — and renders `display: contents`
|
|
6
|
+
* + `role=presentation`. The real foundation sheet (`web-components.sheet.js`,
|
|
7
|
+
* ~512 KB of LightningCSS output) is mocked here with a 1-rule stub so the test
|
|
8
|
+
* exercises the ELEMENT's contract (adopt-once, dedup, transparency), not the
|
|
9
|
+
* bundle's CSS. Byte-parity of the real sheet is covered by `check:css-bundles-fresh`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
13
|
+
|
|
14
|
+
// Stub the heavy foundation sheet — same default-export shape as the generated
|
|
15
|
+
// module: a constructSheet() returning a memoized CSSStyleSheet singleton.
|
|
16
|
+
vi.mock('../../dist/web-components.sheet.js', () => {
|
|
17
|
+
let sheet = null;
|
|
18
|
+
return {
|
|
19
|
+
default: () => {
|
|
20
|
+
if (typeof CSSStyleSheet === 'undefined') return null;
|
|
21
|
+
if (!sheet) { sheet = new CSSStyleSheet(); sheet.replaceSync('theme-provider{display:contents}'); }
|
|
22
|
+
return sheet;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Stub the opt-in twins — the element dynamic-imports these when theme=/scale= is
|
|
28
|
+
// set. We test the attribute contract (sync), not the adopted CSS. (Factories are
|
|
29
|
+
// inlined because vi.mock hoists above any shared `const`.)
|
|
30
|
+
vi.mock('../../dist/themes.sheet.js', () => ({ default: () => { const s = new CSSStyleSheet(); s.replaceSync(':root{}'); return s; } }));
|
|
31
|
+
vi.mock('../../dist/verse.sheet.js', () => ({ default: () => { const s = new CSSStyleSheet(); s.replaceSync(':root{}'); return s; } }));
|
|
32
|
+
vi.mock('../../dist/prose.sheet.js', () => ({ default: () => { const s = new CSSStyleSheet(); s.replaceSync(':root{}'); return s; } }));
|
|
33
|
+
|
|
34
|
+
import '../../core/element.js';
|
|
35
|
+
import './theme-provider.js'; // registers the tag + adopts the (mocked) foundation at module-eval
|
|
36
|
+
|
|
37
|
+
const tick = () => new Promise((r) => queueMicrotask(r));
|
|
38
|
+
|
|
39
|
+
function mount(html) {
|
|
40
|
+
const wrap = document.createElement('div');
|
|
41
|
+
wrap.innerHTML = html;
|
|
42
|
+
document.body.appendChild(wrap);
|
|
43
|
+
return wrap.firstElementChild;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe('<theme-provider>', () => {
|
|
47
|
+
beforeEach(() => { document.body.innerHTML = ''; });
|
|
48
|
+
|
|
49
|
+
it('adopts the foundation sheet at import (module-eval side effect)', () => {
|
|
50
|
+
expect(document.adoptedStyleSheets.length).toBeGreaterThanOrEqual(1);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('is display:contents + role=presentation on connect', async () => {
|
|
54
|
+
const el = mount('<theme-provider><span>hi</span></theme-provider>');
|
|
55
|
+
await tick();
|
|
56
|
+
expect(el.style.display).toBe('contents');
|
|
57
|
+
expect(el.getAttribute('role')).toBe('presentation');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('does not clobber an author-set role', async () => {
|
|
61
|
+
const el = mount('<theme-provider role="group"><span>hi</span></theme-provider>');
|
|
62
|
+
await tick();
|
|
63
|
+
expect(el.getAttribute('role')).toBe('group');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('dedupes — N instances adopt the foundation exactly once', async () => {
|
|
67
|
+
const before = document.adoptedStyleSheets.length; // singleton already adopted at import
|
|
68
|
+
mount('<theme-provider></theme-provider>');
|
|
69
|
+
mount('<theme-provider></theme-provider>');
|
|
70
|
+
mount('<theme-provider></theme-provider>');
|
|
71
|
+
await tick();
|
|
72
|
+
expect(document.adoptedStyleSheets.length).toBe(before);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('reflects theme= to the [theme] attribute hook', async () => {
|
|
76
|
+
const el = mount('<theme-provider theme="ocean"><span>x</span></theme-provider>');
|
|
77
|
+
await tick();
|
|
78
|
+
expect(el.getAttribute('theme')).toBe('ocean');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('scale="verse" maps to the [verse] register attribute', async () => {
|
|
82
|
+
const el = mount('<theme-provider scale="verse"><span>x</span></theme-provider>');
|
|
83
|
+
await tick();
|
|
84
|
+
expect(el.hasAttribute('verse')).toBe(true);
|
|
85
|
+
expect(el.hasAttribute('prose')).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('scale="prose" maps to the [prose] register attribute', async () => {
|
|
89
|
+
const el = mount('<theme-provider scale="prose"><span>x</span></theme-provider>');
|
|
90
|
+
await tick();
|
|
91
|
+
expect(el.hasAttribute('prose')).toBe(true);
|
|
92
|
+
expect(el.hasAttribute('verse')).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Edit this file; run `npm run build:components` to regenerate a2ui.json.
|
|
2
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
3
|
+
name: UIThemeProvider
|
|
4
|
+
tag: theme-provider
|
|
5
|
+
status: experimental
|
|
6
|
+
component: ThemeProvider
|
|
7
|
+
category: layout
|
|
8
|
+
version: 1
|
|
9
|
+
description: |
|
|
10
|
+
Foundation-providing wrapper — adopts the AdiaUI foundation (design tokens +
|
|
11
|
+
resets + page-frame + every primitive's CSS) into the document from anywhere
|
|
12
|
+
in the DOM, so a surface renders fully-styled WITHOUT a hand-wired
|
|
13
|
+
<link rel="stylesheet"> in <head>. Layout-transparent (display: contents): the
|
|
14
|
+
element owns no box; children lay out as if it weren't there.
|
|
15
|
+
|
|
16
|
+
Mechanism: it imports the constructable-stylesheet twin of web-components.min.css
|
|
17
|
+
(byte-identical to the CDN bundle, emitted from the same build buffer) and
|
|
18
|
+
adopts it once into document.adoptedStyleSheets, deduped — adoption fires at
|
|
19
|
+
module load, before paint. Coexists with the render-blocking <link> path; both
|
|
20
|
+
deliver the same bytes. Use a <link> (-> the CDN web-components.min.css) for
|
|
21
|
+
multi-page / top-level surfaces (cacheable across navigations, zero flash); use
|
|
22
|
+
<theme-provider> for SPA roots, embedded apps, and dynamic mounts where you do
|
|
23
|
+
not control <head> (e.g. <embed-shell>, an A2UI surface, a micro-frontend).
|
|
24
|
+
|
|
25
|
+
Theming: the base foundation is OS light/dark via light-dark() tokens. Two opt-in
|
|
26
|
+
attributes adopt their layer on demand — theme="ocean|forest|slate|…" applies a
|
|
27
|
+
named preset (adopts the themes layer; matches the [theme] hook) and
|
|
28
|
+
scale="verse|prose" sets the compact / long-form typographic register (adopts the
|
|
29
|
+
matching register layer; maps onto the [verse]/[prose] attribute). Each layer is
|
|
30
|
+
fetched only when its attribute is set, so a bare provider stays lean.
|
|
31
|
+
Opt-in infra: NOT in the all-in-one @adia-ai/web-components barrel (it carries
|
|
32
|
+
the whole foundation); import @adia-ai/web-components/components/theme-provider
|
|
33
|
+
explicitly. Distinct from <frame-ui> (a layout skeleton, owns no CSS delivery)
|
|
34
|
+
and the page shells (chrome over the same foundation).
|
|
35
|
+
props:
|
|
36
|
+
theme:
|
|
37
|
+
description: 'Named theme preset for the wrapped subtree (default, ocean, forest, sunset, lavender, rose, slate, midnight). Adopts the themes layer on demand + matches the [theme="…"] hook.'
|
|
38
|
+
type: string
|
|
39
|
+
default: ""
|
|
40
|
+
reflect: true
|
|
41
|
+
scale:
|
|
42
|
+
description: 'Typographic / density register for the subtree — "verse" (compact) or "prose" (long-form). Adopts the matching register layer on demand; maps onto the [verse]/[prose] attribute (additive).'
|
|
43
|
+
type: string
|
|
44
|
+
default: ""
|
|
45
|
+
reflect: true
|
|
46
|
+
events: {}
|
|
47
|
+
slots: {}
|
|
48
|
+
states:
|
|
49
|
+
- name: idle
|
|
50
|
+
description: Default — foundation adopted into the document; children rendered.
|
|
51
|
+
traits: []
|
|
52
|
+
tokens: {}
|
|
53
|
+
a2ui:
|
|
54
|
+
rules:
|
|
55
|
+
- rule: 'Wrap a client-mounted surface root (SPA shell, embedded app, dynamic mount) in <theme-provider> so it self-provides the AdiaUI foundation — no <head> stylesheet link required.'
|
|
56
|
+
reason: 'Adopts the foundation bundle into the document from anywhere in the tree (document.adoptedStyleSheets).'
|
|
57
|
+
- rule: 'For multi-page / top-level pages, prefer a render-blocking <link rel="stylesheet" href=".../web-components.min.css"> in <head> instead — cacheable across navigations, styled on the first frame. <theme-provider> and the link deliver byte-identical CSS, so they coexist.'
|
|
58
|
+
reason: 'Link for multi-page (no flash); provider for SPA/embedded (no head access).'
|
|
59
|
+
- rule: 'Theme the wrapped subtree with theme="ocean" (a named preset) and/or scale="verse|prose" (compact / long-form register) — the provider adopts the matching opt-in layer on demand. The base foundation is OS light/dark automatically. Keep <theme-provider> at/near the mount root — display:contents means an ancestor "> " child selector will not reach its children.'
|
|
60
|
+
reason: 'theme/scale each fetch + adopt their layer (themes.css / verse.css / prose.css) on demand; display:contents preserves custom-property inheritance but not child-combinator reach (Light DOM, ADR-0033).'
|
|
61
|
+
anti_patterns: []
|
|
62
|
+
examples:
|
|
63
|
+
- name: spa-root
|
|
64
|
+
description: A client-mounted app root that self-provides the foundation (no head link).
|
|
65
|
+
a2ui: >-
|
|
66
|
+
[
|
|
67
|
+
{ "id": "root", "component": "ThemeProvider", "children": ["panel"] },
|
|
68
|
+
{ "id": "panel", "component": "Frame", "children": ["body", "actions"] },
|
|
69
|
+
{ "id": "body", "component": "Section", "children": ["copy"] },
|
|
70
|
+
{ "id": "copy", "component": "Text", "variant": "body", "textContent": "Fully styled — no <head> stylesheet link." },
|
|
71
|
+
{ "id": "actions", "component": "Footer", "children": ["go"] },
|
|
72
|
+
{ "id": "go", "component": "Button", "text": "Continue", "variant": "primary" }
|
|
73
|
+
]
|
|
74
|
+
keywords:
|
|
75
|
+
- style
|
|
76
|
+
- provider
|
|
77
|
+
- foundation
|
|
78
|
+
- tokens
|
|
79
|
+
- reset
|
|
80
|
+
- adopt
|
|
81
|
+
- stylesheet
|
|
82
|
+
- shell
|
|
83
|
+
- embed
|
|
84
|
+
- spa
|
|
85
|
+
synonyms:
|
|
86
|
+
provider:
|
|
87
|
+
- provider
|
|
88
|
+
- root
|
|
89
|
+
- host
|
|
90
|
+
foundation:
|
|
91
|
+
- foundation
|
|
92
|
+
- tokens
|
|
93
|
+
- styles
|
|
94
|
+
related:
|
|
95
|
+
- Frame
|
|
96
|
+
- Card
|