@adia-ai/web-components 0.8.5 → 0.8.7
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 +19 -1
- package/components/field/field.css +21 -0
- package/components/table/table.a2ui.json +1 -1
- package/components/table/table.class.js +17 -0
- package/components/table/table.css +15 -2
- package/components/table/table.d.ts +1 -1
- package/components/table/table.test.js +51 -0
- package/components/table/table.yaml +6 -1
- package/core/element.js +24 -0
- package/core/element.test.js +71 -0
- package/dist/host.min.css +1 -1
- package/dist/host.sheet.js +1 -1
- package/dist/theme-provider.min.js +2 -2
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +50 -50
- package/dist/web-components.sheet.js +1 -1
- package/package.json +2 -1
- package/patterns/access-requests/access-requests.examples.html +132 -0
- package/patterns/access-requests/access-requests.html +60 -0
- package/patterns/admin-keys-and-export/admin-keys-and-export.examples.html +236 -0
- package/patterns/admin-keys-and-export/admin-keys-and-export.html +64 -0
- package/patterns/admin-shell/admin-shell.examples.html +327 -0
- package/patterns/admin-shell/admin-shell.html +44 -0
- package/patterns/agent-cost/agent-cost.examples.html +111 -0
- package/patterns/agent-cost/agent-cost.html +62 -0
- package/patterns/agent-memory/agent-memory.examples.html +155 -0
- package/patterns/agent-memory/agent-memory.html +64 -0
- package/patterns/agent-prompt-library/agent-prompt-library.examples.html +181 -0
- package/patterns/agent-prompt-library/agent-prompt-library.html +64 -0
- package/patterns/agent-tool-call/agent-tool-call.examples.html +123 -0
- package/patterns/agent-tool-call/agent-tool-call.html +60 -0
- package/patterns/alert-dialog/alert-dialog.examples.html +105 -0
- package/patterns/alert-dialog/alert-dialog.html +57 -0
- package/patterns/app-nav/app-nav.examples.html +421 -0
- package/patterns/app-nav/app-nav.html +50 -0
- package/patterns/approvals/approvals.examples.html +131 -0
- package/patterns/approvals/approvals.html +58 -0
- package/patterns/audit-log/audit-log.examples.html +74 -0
- package/patterns/audit-log/audit-log.examples.js +60 -0
- package/patterns/audit-log/audit-log.html +63 -0
- package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.html +89 -0
- package/patterns/bulk-action-toolbar/bulk-action-toolbar.examples.js +69 -0
- package/patterns/bulk-action-toolbar/bulk-action-toolbar.html +62 -0
- package/patterns/changelog-feed/changelog-feed.examples.html +151 -0
- package/patterns/changelog-feed/changelog-feed.html +57 -0
- package/patterns/chart-in-card/chart-in-card.examples.html +193 -0
- package/patterns/chart-in-card/chart-in-card.examples.js +46 -0
- package/patterns/chart-in-card/chart-in-card.html +66 -0
- package/patterns/chat-shell/chat-shell.examples.html +129 -0
- package/patterns/chat-shell/chat-shell.html +46 -0
- package/patterns/comments-and-collaboration/comments-and-collaboration.examples.html +241 -0
- package/patterns/comments-and-collaboration/comments-and-collaboration.html +66 -0
- package/patterns/component-tokens/component-tokens.examples.html +483 -0
- package/patterns/component-tokens/component-tokens.html +42 -0
- package/patterns/conversion-funnel/conversion-funnel.examples.html +158 -0
- package/patterns/conversion-funnel/conversion-funnel.examples.js +4 -0
- package/patterns/conversion-funnel/conversion-funnel.html +64 -0
- package/patterns/data-tables-inline-edit-and-tree/data-tables-inline-edit-and-tree.examples.html +196 -0
- package/patterns/data-tables-inline-edit-and-tree/data-tables-inline-edit-and-tree.html +58 -0
- package/patterns/diff-review/diff-review.examples.html +147 -0
- package/patterns/diff-review/diff-review.html +56 -0
- package/patterns/editor-shell/editor-shell.examples.html +139 -0
- package/patterns/editor-shell/editor-shell.html +62 -0
- package/patterns/export-flow/export-flow.examples.html +152 -0
- package/patterns/export-flow/export-flow.examples.js +37 -0
- package/patterns/export-flow/export-flow.html +65 -0
- package/patterns/filter-bar/filter-bar.examples.html +116 -0
- package/patterns/filter-bar/filter-bar.examples.js +23 -0
- package/patterns/filter-bar/filter-bar.html +70 -0
- package/patterns/form-drawer/form-drawer.examples.html +210 -0
- package/patterns/form-drawer/form-drawer.html +64 -0
- package/patterns/form-system/form-system.examples.html +383 -0
- package/patterns/form-system/form-system.html +48 -0
- package/patterns/forms-conditional-and-autocomplete/forms-conditional-and-autocomplete.examples.html +189 -0
- package/patterns/forms-conditional-and-autocomplete/forms-conditional-and-autocomplete.html +64 -0
- package/patterns/inline-dialog/inline-dialog.examples.html +143 -0
- package/patterns/inline-dialog/inline-dialog.html +59 -0
- package/patterns/kanban-board/kanban-board.examples.html +224 -0
- package/patterns/kanban-board/kanban-board.html +58 -0
- package/patterns/layout/layout.examples.html +365 -0
- package/patterns/layout/layout.html +54 -0
- package/patterns/marketing-engagement/marketing-engagement.examples.html +280 -0
- package/patterns/marketing-engagement/marketing-engagement.html +64 -0
- package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.examples.html +213 -0
- package/patterns/notifications-bell-and-digest/notifications-bell-and-digest.html +58 -0
- package/patterns/overview/overview.examples.html +119 -0
- package/patterns/overview/overview.html +42 -0
- package/patterns/permissions-matrix/permissions-matrix.examples.html +91 -0
- package/patterns/permissions-matrix/permissions-matrix.examples.js +28 -0
- package/patterns/permissions-matrix/permissions-matrix.html +56 -0
- package/patterns/permissions-role-picker/permissions-role-picker.examples.html +81 -0
- package/patterns/permissions-role-picker/permissions-role-picker.html +56 -0
- package/patterns/permissions-sharing/permissions-sharing.examples.html +175 -0
- package/patterns/permissions-sharing/permissions-sharing.html +66 -0
- package/patterns/profile-public-and-verification/profile-public-and-verification.examples.html +227 -0
- package/patterns/profile-public-and-verification/profile-public-and-verification.html +70 -0
- package/patterns/record-detail-drawer/record-detail-drawer.examples.html +164 -0
- package/patterns/record-detail-drawer/record-detail-drawer.html +64 -0
- package/patterns/responsive-shell-sidebar/README.md +37 -0
- package/patterns/responsive-shell-sidebar/responsive-shell-sidebar.css +39 -0
- package/patterns/responsive-shell-sidebar/responsive-shell-sidebar.examples.html +208 -0
- package/patterns/responsive-shell-sidebar/responsive-shell-sidebar.html +47 -0
- package/patterns/retention-cohort/retention-cohort.examples.html +149 -0
- package/patterns/retention-cohort/retention-cohort.examples.js +37 -0
- package/patterns/retention-cohort/retention-cohort.html +62 -0
- package/patterns/search-discovery/search-discovery.examples.html +263 -0
- package/patterns/search-discovery/search-discovery.html +58 -0
- package/patterns/system-banners/system-banners.examples.html +150 -0
- package/patterns/system-banners/system-banners.html +52 -0
- package/patterns/timezone-selector/timezone-selector.examples.html +82 -0
- package/patterns/timezone-selector/timezone-selector.examples.js +72 -0
- package/patterns/timezone-selector/timezone-selector.html +57 -0
- package/patterns/wizard-drawer/wizard-drawer.examples.html +144 -0
- package/patterns/wizard-drawer/wizard-drawer.examples.js +33 -0
- package/patterns/wizard-drawer/wizard-drawer.html +72 -0
- package/styles/colors/semantics/core.css +1 -1
- package/styles/colors/semantics/features.css +1 -1
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
<header>
|
|
2
|
+
<div>
|
|
3
|
+
<h1>Component Token Pattern</h1>
|
|
4
|
+
</div>
|
|
5
|
+
<p>How AdiaUI components use <code>@scope</code>, CSS custom properties, and zero-specificity defaults to create a themeable, isolated styling system without Shadow DOM or BEM.</p>
|
|
6
|
+
</header>
|
|
7
|
+
|
|
8
|
+
<section data-section data-property="overview">
|
|
9
|
+
<h2 variant="section">Overview</h2>
|
|
10
|
+
<p>
|
|
11
|
+
Every AdiaUI component follows a consistent CSS architecture built on three principles:
|
|
12
|
+
</p>
|
|
13
|
+
<ul>
|
|
14
|
+
<li><strong>Scoped isolation</strong> via <code>@scope(tag-ui)</code> — styles cannot leak in or out</li>
|
|
15
|
+
<li><strong>Token-driven theming</strong> via <code>--{component}-{prop}</code> custom properties — every visual property is configurable</li>
|
|
16
|
+
<li><strong>Zero-specificity defaults</strong> via <code>:where(:scope)</code> — tokens can be overridden from outside without specificity wars</li>
|
|
17
|
+
</ul>
|
|
18
|
+
<p data-note>
|
|
19
|
+
This pattern gives you the encapsulation benefits of Shadow DOM (no style leakage)
|
|
20
|
+
with the flexibility of light DOM (global styles, form participation, CSS inheritance).
|
|
21
|
+
</p>
|
|
22
|
+
</section>
|
|
23
|
+
|
|
24
|
+
<section data-section data-property="the-scope-pattern">
|
|
25
|
+
<h2 variant="section">The @scope Pattern</h2>
|
|
26
|
+
<p>
|
|
27
|
+
CSS <code>@scope</code> limits style rules to a specific subtree. AdiaUI scopes
|
|
28
|
+
each component to its custom element tag:
|
|
29
|
+
</p>
|
|
30
|
+
<code-ui language="css">@scope (button-ui) {
|
|
31
|
+
/* Everything in here only applies inside <button-ui> */
|
|
32
|
+
:scope { ... } /* the button-ui element itself */
|
|
33
|
+
[slot="trailing"] { ... } /* children of button-ui */
|
|
34
|
+
}</code-ui>
|
|
35
|
+
<p>
|
|
36
|
+
Unlike Shadow DOM, <code>@scope</code> does not create a style boundary — it creates
|
|
37
|
+
a style <em>scope</em>. CSS custom properties still inherit through the scope boundary,
|
|
38
|
+
global resets still apply, and form elements participate in their parent form natively.
|
|
39
|
+
</p>
|
|
40
|
+
</section>
|
|
41
|
+
|
|
42
|
+
<section data-section data-property="wherescope-vs-scope">
|
|
43
|
+
<h2 variant="section">:where(:scope) vs :scope</h2>
|
|
44
|
+
<p>
|
|
45
|
+
This is the key mechanism that makes component tokens overridable. AdiaUI uses
|
|
46
|
+
two distinct selectors for two different purposes:
|
|
47
|
+
</p>
|
|
48
|
+
|
|
49
|
+
<h3><code>:where(:scope)</code> — Zero-specificity token defaults</h3>
|
|
50
|
+
<p>
|
|
51
|
+
Token declarations go in <code>:where(:scope)</code>, which has <strong>zero specificity</strong>.
|
|
52
|
+
This means any external rule — even a bare element selector — can override these values:
|
|
53
|
+
</p>
|
|
54
|
+
<code-ui language="css">@scope (button-ui) {
|
|
55
|
+
:where(:scope) {
|
|
56
|
+
--button-bg: var(--a-bg-muted);
|
|
57
|
+
--button-fg: var(--a-fg-muted);
|
|
58
|
+
--button-radius: var(--a-radius);
|
|
59
|
+
/* ... all component tokens declared here ... */
|
|
60
|
+
}
|
|
61
|
+
}</code-ui>
|
|
62
|
+
|
|
63
|
+
<h3><code>:scope</code> — Base styles (normal specificity)</h3>
|
|
64
|
+
<p>
|
|
65
|
+
Structural styles that <em>consume</em> the tokens go in <code>:scope</code>,
|
|
66
|
+
which has normal specificity. These define the component's layout, transitions,
|
|
67
|
+
and visual properties by referencing the tokens:
|
|
68
|
+
</p>
|
|
69
|
+
<code-ui language="css">@scope (button-ui) {
|
|
70
|
+
:scope {
|
|
71
|
+
display: inline-flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
background: var(--button-bg); /* consumes the token */
|
|
74
|
+
color: var(--button-fg); /* consumes the token */
|
|
75
|
+
border-radius: var(--button-radius); /* consumes the token */
|
|
76
|
+
transition: background var(--a-duration-fast) var(--a-easing);
|
|
77
|
+
}
|
|
78
|
+
}</code-ui>
|
|
79
|
+
|
|
80
|
+
<p data-note>
|
|
81
|
+
The separation is intentional: <code>:where(:scope)</code> sets <em>what the values are</em>,
|
|
82
|
+
<code>:scope</code> sets <em>how they are applied</em>. Because the defaults have zero
|
|
83
|
+
specificity, consumers can retheme without <code>!important</code> or deeply nested selectors.
|
|
84
|
+
</p>
|
|
85
|
+
</section>
|
|
86
|
+
|
|
87
|
+
<section data-section data-property="naming-convention">
|
|
88
|
+
<h2 variant="section">Naming Convention</h2>
|
|
89
|
+
<p>
|
|
90
|
+
Component tokens follow a strict <code>--{component}-{property}</code> naming scheme:
|
|
91
|
+
</p>
|
|
92
|
+
<table>
|
|
93
|
+
<thead>
|
|
94
|
+
<tr>
|
|
95
|
+
<th>Pattern</th>
|
|
96
|
+
<th>Example</th>
|
|
97
|
+
<th>Meaning</th>
|
|
98
|
+
</tr>
|
|
99
|
+
</thead>
|
|
100
|
+
<tbody>
|
|
101
|
+
<tr>
|
|
102
|
+
<td><code>--{comp}-bg</code></td>
|
|
103
|
+
<td><code>--button-bg</code></td>
|
|
104
|
+
<td>Background color</td>
|
|
105
|
+
</tr>
|
|
106
|
+
<tr>
|
|
107
|
+
<td><code>--{comp}-fg</code></td>
|
|
108
|
+
<td><code>--button-fg</code></td>
|
|
109
|
+
<td>Foreground / text color</td>
|
|
110
|
+
</tr>
|
|
111
|
+
<tr>
|
|
112
|
+
<td><code>--{comp}-border</code></td>
|
|
113
|
+
<td><code>--button-border</code></td>
|
|
114
|
+
<td>Border color</td>
|
|
115
|
+
</tr>
|
|
116
|
+
<tr>
|
|
117
|
+
<td><code>--{comp}-radius</code></td>
|
|
118
|
+
<td><code>--button-radius</code></td>
|
|
119
|
+
<td>Border radius</td>
|
|
120
|
+
</tr>
|
|
121
|
+
<tr>
|
|
122
|
+
<td><code>--{comp}-height</code></td>
|
|
123
|
+
<td><code>--button-height</code></td>
|
|
124
|
+
<td>Component height</td>
|
|
125
|
+
</tr>
|
|
126
|
+
<tr>
|
|
127
|
+
<td><code>--{comp}-px</code></td>
|
|
128
|
+
<td><code>--button-px</code></td>
|
|
129
|
+
<td>Horizontal padding</td>
|
|
130
|
+
</tr>
|
|
131
|
+
<tr>
|
|
132
|
+
<td><code>--{comp}-gap</code></td>
|
|
133
|
+
<td><code>--button-gap</code></td>
|
|
134
|
+
<td>Internal gap</td>
|
|
135
|
+
</tr>
|
|
136
|
+
<tr>
|
|
137
|
+
<td><code>--{comp}-font-size</code></td>
|
|
138
|
+
<td><code>--button-font-size</code></td>
|
|
139
|
+
<td>Font size</td>
|
|
140
|
+
</tr>
|
|
141
|
+
<tr>
|
|
142
|
+
<td><code>--{comp}-bg-{variant}</code></td>
|
|
143
|
+
<td><code>--button-bg-primary</code></td>
|
|
144
|
+
<td>Variant-specific background</td>
|
|
145
|
+
</tr>
|
|
146
|
+
<tr>
|
|
147
|
+
<td><code>--{comp}-{prop}-{state}</code></td>
|
|
148
|
+
<td><code>--button-bg-hover</code></td>
|
|
149
|
+
<td>State-specific value</td>
|
|
150
|
+
</tr>
|
|
151
|
+
</tbody>
|
|
152
|
+
</table>
|
|
153
|
+
<p data-note>
|
|
154
|
+
All variant and state tokens are pre-declared in <code>:where(:scope)</code>, even
|
|
155
|
+
if they are only used inside a variant selector. This makes every configurable value
|
|
156
|
+
discoverable at the top of the file.
|
|
157
|
+
</p>
|
|
158
|
+
</section>
|
|
159
|
+
|
|
160
|
+
<section data-section data-property="variant-overrides">
|
|
161
|
+
<h2 variant="section">Variant Overrides</h2>
|
|
162
|
+
<p>
|
|
163
|
+
Variants work by reassigning the base tokens to their variant-specific counterparts.
|
|
164
|
+
This keeps the <code>:scope</code> base styles unchanged — they always read from
|
|
165
|
+
<code>--button-bg</code>, <code>--button-fg</code>, etc. Variants just swap what
|
|
166
|
+
those tokens resolve to:
|
|
167
|
+
</p>
|
|
168
|
+
<code-ui language="css">/* Variant selectors reassign the base tokens */
|
|
169
|
+
:scope[variant="primary"] {
|
|
170
|
+
--button-bg: var(--button-bg-primary);
|
|
171
|
+
--button-fg: var(--button-fg-primary);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
:scope[variant="outline"] {
|
|
175
|
+
--button-bg: var(--button-bg-outline);
|
|
176
|
+
--button-fg: var(--button-fg-outline);
|
|
177
|
+
--button-border: var(--button-border-outline);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
:scope[variant="ghost"] {
|
|
181
|
+
--button-bg: var(--button-bg-ghost);
|
|
182
|
+
--button-fg: var(--button-fg-ghost);
|
|
183
|
+
--button-border: var(--button-border-ghost);
|
|
184
|
+
}</code-ui>
|
|
185
|
+
<p>
|
|
186
|
+
This "token indirection" approach means hover, focus, and disabled states only
|
|
187
|
+
need to be written once — they always target the base tokens. Variants just
|
|
188
|
+
redirect which values flow through.
|
|
189
|
+
</p>
|
|
190
|
+
</section>
|
|
191
|
+
|
|
192
|
+
<section data-section data-property="consumer-overrides">
|
|
193
|
+
<h2 variant="section">Consumer Overrides</h2>
|
|
194
|
+
<p>
|
|
195
|
+
Because tokens in <code>:where(:scope)</code> have zero specificity, consumers
|
|
196
|
+
can override any token from outside the component with a simple selector:
|
|
197
|
+
</p>
|
|
198
|
+
<code-ui language="css">/* Override from a parent context */
|
|
199
|
+
.my-toolbar button-ui {
|
|
200
|
+
--button-bg: var(--md-sys-color-primary);
|
|
201
|
+
--button-fg: var(--md-sys-color-primary-on-primary);
|
|
202
|
+
--button-radius: var(--a-radius-full);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* Override a specific instance */
|
|
206
|
+
button-ui.big-cta {
|
|
207
|
+
--button-height: 3rem;
|
|
208
|
+
--button-px: var(--a-space-6);
|
|
209
|
+
--button-font-size: var(--a-ui-lg);
|
|
210
|
+
}</code-ui>
|
|
211
|
+
<p data-note>
|
|
212
|
+
You never need <code>!important</code> to override component tokens. Even a type selector
|
|
213
|
+
like <code>button-ui { --button-bg: red; }</code> beats the zero-specificity defaults.
|
|
214
|
+
This is the entire point of the <code>:where()</code> wrapper.
|
|
215
|
+
</p>
|
|
216
|
+
</section>
|
|
217
|
+
|
|
218
|
+
<section data-section data-property="annotated-example-button-ui">
|
|
219
|
+
<h2 variant="section">Annotated Example: button-ui</h2>
|
|
220
|
+
<p>
|
|
221
|
+
Here is the complete <code>button-ui</code> CSS file, annotated to show how each
|
|
222
|
+
part of the pattern works together:
|
|
223
|
+
</p>
|
|
224
|
+
<code-ui language="css">@scope (button-ui) {
|
|
225
|
+
|
|
226
|
+
/* ── LAYER 1: Token defaults (zero specificity) ──
|
|
227
|
+
Every configurable value is declared here.
|
|
228
|
+
Consumers can override any of these from outside. */
|
|
229
|
+
:where(:scope) {
|
|
230
|
+
/* Base tokens — consumed by :scope styles */
|
|
231
|
+
--button-bg: var(--a-bg-muted);
|
|
232
|
+
--button-fg: var(--a-fg-muted);
|
|
233
|
+
--button-border: transparent;
|
|
234
|
+
--button-height: var(--a-size);
|
|
235
|
+
--button-px: var(--a-ui-px);
|
|
236
|
+
--button-radius: var(--a-radius);
|
|
237
|
+
--button-font-size: var(--a-ui-size);
|
|
238
|
+
--button-font-weight: var(--a-ui-weight);
|
|
239
|
+
--button-gap: var(--a-space-1);
|
|
240
|
+
|
|
241
|
+
/* Hover state tokens */
|
|
242
|
+
--button-bg-hover: var(--md-sys-color-neutral-background);
|
|
243
|
+
--button-fg-hover: var(--md-sys-color-neutral-on-surface);
|
|
244
|
+
--button-border-hover: var(--md-sys-color-neutral-on-surface);
|
|
245
|
+
|
|
246
|
+
/* Variant: primary */
|
|
247
|
+
--button-bg-primary: var(--md-sys-color-primary);
|
|
248
|
+
--button-fg-primary: var(--md-sys-color-primary-on-primary);
|
|
249
|
+
|
|
250
|
+
/* Variant: outline */
|
|
251
|
+
--button-bg-outline: transparent;
|
|
252
|
+
--button-fg-outline: var(--md-sys-color-neutral-on-surface);
|
|
253
|
+
--button-border-outline: var(--a-ui-border);
|
|
254
|
+
|
|
255
|
+
/* Variant: ghost */
|
|
256
|
+
--button-bg-ghost: transparent;
|
|
257
|
+
--button-fg-ghost: var(--md-sys-color-neutral-on-surface-variant);
|
|
258
|
+
--button-fg-ghost-hover: var(--md-sys-color-neutral-on-surface);
|
|
259
|
+
--button-bg-ghost-hover: var(--a-bg-hover);
|
|
260
|
+
--button-border-ghost: transparent;
|
|
261
|
+
--button-border-ghost-hover: var(--md-sys-color-neutral-outline);
|
|
262
|
+
|
|
263
|
+
/* Variant: danger */
|
|
264
|
+
--button-bg-danger: var(--md-sys-color-danger);
|
|
265
|
+
--button-fg-danger: var(--md-sys-color-danger-on-danger);
|
|
266
|
+
|
|
267
|
+
/* Size: sm */
|
|
268
|
+
--button-height-sm: var(--a-ui-size-sm);
|
|
269
|
+
--button-px-sm: var(--a-space-1);
|
|
270
|
+
--button-font-size-sm: var(--a-ui-sm);
|
|
271
|
+
|
|
272
|
+
/* Size: lg */
|
|
273
|
+
--button-height-lg: var(--a-ui-size-lg);
|
|
274
|
+
--button-px-lg: var(--a-space-2);
|
|
275
|
+
--button-font-size-lg: var(--a-ui-lg);
|
|
276
|
+
|
|
277
|
+
/* Disabled state */
|
|
278
|
+
--button-bg-disabled: var(--a-ui-bg-disabled);
|
|
279
|
+
--button-fg-disabled: var(--a-ui-text-disabled);
|
|
280
|
+
--button-border-disabled: transparent;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* ── LAYER 2: Base styles (normal specificity) ──
|
|
284
|
+
These consume the tokens above. They define layout
|
|
285
|
+
and visual properties that apply to ALL variants. */
|
|
286
|
+
:scope {
|
|
287
|
+
box-sizing: border-box;
|
|
288
|
+
display: inline-flex;
|
|
289
|
+
align-items: center;
|
|
290
|
+
justify-content: center;
|
|
291
|
+
gap: var(--button-gap);
|
|
292
|
+
min-width: var(--button-height);
|
|
293
|
+
min-height: var(--button-height);
|
|
294
|
+
padding-inline: var(--button-px);
|
|
295
|
+
border: 1px solid var(--button-border);
|
|
296
|
+
border-radius: var(--button-radius);
|
|
297
|
+
background: var(--button-bg);
|
|
298
|
+
color: var(--button-fg);
|
|
299
|
+
font-size: var(--button-font-size);
|
|
300
|
+
font-weight: var(--button-font-weight);
|
|
301
|
+
cursor: pointer;
|
|
302
|
+
transition:
|
|
303
|
+
background var(--a-duration-fast) var(--a-easing),
|
|
304
|
+
border-color var(--a-duration-fast) var(--a-easing),
|
|
305
|
+
color var(--a-duration-fast) var(--a-easing);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
:scope:focus-visible {
|
|
309
|
+
outline: none;
|
|
310
|
+
box-shadow: var(--a-focus-ring);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* ── LAYER 3: Variants ──
|
|
314
|
+
Reassign base tokens to variant-specific values.
|
|
315
|
+
Base styles don't change — only the token values do. */
|
|
316
|
+
:scope[variant="primary"] {
|
|
317
|
+
--button-bg: var(--button-bg-primary);
|
|
318
|
+
--button-fg: var(--button-fg-primary);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
:scope[variant="outline"] {
|
|
322
|
+
--button-bg: var(--button-bg-outline);
|
|
323
|
+
--button-fg: var(--button-fg-outline);
|
|
324
|
+
--button-border: var(--button-border-outline);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
:scope[variant="ghost"] {
|
|
328
|
+
--button-bg: var(--button-bg-ghost);
|
|
329
|
+
--button-fg: var(--button-fg-ghost);
|
|
330
|
+
--button-border: var(--button-border-ghost);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* ── LAYER 4: States ──
|
|
334
|
+
Hover, disabled, sizes. Written once, works for all variants. */
|
|
335
|
+
:scope:not([disabled]):hover {
|
|
336
|
+
--button-bg: var(--button-bg-hover);
|
|
337
|
+
--button-fg: var(--button-fg-hover);
|
|
338
|
+
--button-border: var(--button-border-hover);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
:scope[disabled] {
|
|
342
|
+
--button-bg: var(--button-bg-disabled);
|
|
343
|
+
--button-fg: var(--button-fg-disabled);
|
|
344
|
+
--button-border: var(--button-border-disabled);
|
|
345
|
+
pointer-events: none;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
:scope[size="sm"] {
|
|
349
|
+
--button-height: var(--button-height-sm);
|
|
350
|
+
--button-px: var(--button-px-sm);
|
|
351
|
+
--button-font-size: var(--button-font-size-sm);
|
|
352
|
+
}
|
|
353
|
+
}</code-ui>
|
|
354
|
+
</section>
|
|
355
|
+
|
|
356
|
+
<section data-section data-property="why-not-shadow-dom">
|
|
357
|
+
<h2 variant="section">Why Not Shadow DOM?</h2>
|
|
358
|
+
<p>
|
|
359
|
+
Shadow DOM provides strong style encapsulation, but it comes with significant trade-offs
|
|
360
|
+
for a design system:
|
|
361
|
+
</p>
|
|
362
|
+
<table>
|
|
363
|
+
<thead>
|
|
364
|
+
<tr>
|
|
365
|
+
<th>Concern</th>
|
|
366
|
+
<th>Shadow DOM</th>
|
|
367
|
+
<th>@scope + Tokens</th>
|
|
368
|
+
</tr>
|
|
369
|
+
</thead>
|
|
370
|
+
<tbody>
|
|
371
|
+
<tr>
|
|
372
|
+
<td>Style isolation</td>
|
|
373
|
+
<td>Hard boundary — no styles in or out</td>
|
|
374
|
+
<td>Soft boundary — scoped but inheritable</td>
|
|
375
|
+
</tr>
|
|
376
|
+
<tr>
|
|
377
|
+
<td>Theming</td>
|
|
378
|
+
<td>Requires <code>::part()</code> or CSS custom properties piercing the boundary</td>
|
|
379
|
+
<td>Standard CSS selectors work; tokens override naturally</td>
|
|
380
|
+
</tr>
|
|
381
|
+
<tr>
|
|
382
|
+
<td>Form participation</td>
|
|
383
|
+
<td>Requires <code>ElementInternals</code> + manual delegation</td>
|
|
384
|
+
<td>Native — inputs inside the element are in the form</td>
|
|
385
|
+
</tr>
|
|
386
|
+
<tr>
|
|
387
|
+
<td>Global styles</td>
|
|
388
|
+
<td>Blocked — resets, typography must be duplicated inside</td>
|
|
389
|
+
<td>Inherited — global resets and tokens flow through</td>
|
|
390
|
+
</tr>
|
|
391
|
+
<tr>
|
|
392
|
+
<td>Dev tools</td>
|
|
393
|
+
<td>Styles hidden in shadow tree</td>
|
|
394
|
+
<td>All styles visible in the regular cascade</td>
|
|
395
|
+
</tr>
|
|
396
|
+
<tr>
|
|
397
|
+
<td>SSR / progressive</td>
|
|
398
|
+
<td>Requires declarative shadow DOM or FOUC</td>
|
|
399
|
+
<td>Works with plain HTML — styles apply immediately</td>
|
|
400
|
+
</tr>
|
|
401
|
+
</tbody>
|
|
402
|
+
</table>
|
|
403
|
+
</section>
|
|
404
|
+
|
|
405
|
+
<section data-section data-property="why-not-bem">
|
|
406
|
+
<h2 variant="section">Why Not BEM?</h2>
|
|
407
|
+
<p>
|
|
408
|
+
BEM (<code>.block__element--modifier</code>) solves naming collisions through convention.
|
|
409
|
+
AdiaUI's approach solves it through the platform:
|
|
410
|
+
</p>
|
|
411
|
+
<table>
|
|
412
|
+
<thead>
|
|
413
|
+
<tr>
|
|
414
|
+
<th>Concern</th>
|
|
415
|
+
<th>BEM</th>
|
|
416
|
+
<th>@scope + Tokens</th>
|
|
417
|
+
</tr>
|
|
418
|
+
</thead>
|
|
419
|
+
<tbody>
|
|
420
|
+
<tr>
|
|
421
|
+
<td>Isolation</td>
|
|
422
|
+
<td>Convention-based (easy to break)</td>
|
|
423
|
+
<td>Enforced by <code>@scope</code></td>
|
|
424
|
+
</tr>
|
|
425
|
+
<tr>
|
|
426
|
+
<td>Selector length</td>
|
|
427
|
+
<td><code>.button__icon--large</code></td>
|
|
428
|
+
<td><code>icon-ui</code> (inside <code>@scope(button-ui)</code>)</td>
|
|
429
|
+
</tr>
|
|
430
|
+
<tr>
|
|
431
|
+
<td>Theming</td>
|
|
432
|
+
<td>Override each BEM class individually</td>
|
|
433
|
+
<td>Set a token, all usages update</td>
|
|
434
|
+
</tr>
|
|
435
|
+
<tr>
|
|
436
|
+
<td>Variants</td>
|
|
437
|
+
<td>New class per variant per element</td>
|
|
438
|
+
<td>Attribute selector redirects tokens</td>
|
|
439
|
+
</tr>
|
|
440
|
+
<tr>
|
|
441
|
+
<td>Specificity</td>
|
|
442
|
+
<td>Flat but requires discipline</td>
|
|
443
|
+
<td>Structured: zero for defaults, normal for styles</td>
|
|
444
|
+
</tr>
|
|
445
|
+
</tbody>
|
|
446
|
+
</table>
|
|
447
|
+
<p data-note>
|
|
448
|
+
The component token pattern lets you write clean, minimal selectors inside each component
|
|
449
|
+
while maintaining full isolation. No naming conventions to memorize, no class-name
|
|
450
|
+
collisions to debug.
|
|
451
|
+
</p>
|
|
452
|
+
</section>
|
|
453
|
+
|
|
454
|
+
<section data-section data-property="writing-a-new-component">
|
|
455
|
+
<h2 variant="section">Writing a New Component</h2>
|
|
456
|
+
<p>Follow this checklist when creating a new component's CSS:</p>
|
|
457
|
+
<ol>
|
|
458
|
+
<li>Open with <code>@scope (my-component-ui) {</code></li>
|
|
459
|
+
<li>Declare all tokens in <code>:where(:scope) { }</code> — base values, variants, sizes, states</li>
|
|
460
|
+
<li>Write base styles in <code>:scope { }</code> that consume only your component tokens</li>
|
|
461
|
+
<li>Add <code>:scope:focus-visible</code> with <code>box-shadow: var(--a-focus-ring)</code></li>
|
|
462
|
+
<li>Write variants as <code>:scope[variant="..."]</code> that reassign base tokens</li>
|
|
463
|
+
<li>Write sizes as <code>:scope[size="..."]</code> that reassign height/padding/font tokens</li>
|
|
464
|
+
<li>Write disabled as <code>:scope[disabled]</code></li>
|
|
465
|
+
<li>Reference global tokens (<code>--a-*</code>) in your token defaults, never in base styles</li>
|
|
466
|
+
</ol>
|
|
467
|
+
<code-ui language="css">@scope (my-widget-ui) {
|
|
468
|
+
:where(:scope) {
|
|
469
|
+
--widget-bg: var(--a-bg-subtle);
|
|
470
|
+
--widget-fg: var(--md-sys-color-neutral-on-surface);
|
|
471
|
+
--widget-radius: var(--a-radius-md);
|
|
472
|
+
--widget-px: var(--a-space-3);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
:scope {
|
|
476
|
+
display: flex;
|
|
477
|
+
padding-inline: var(--widget-px);
|
|
478
|
+
background: var(--widget-bg);
|
|
479
|
+
color: var(--widget-fg);
|
|
480
|
+
border-radius: var(--widget-radius);
|
|
481
|
+
}
|
|
482
|
+
}</code-ui>
|
|
483
|
+
</section>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" theme="auto">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Component Token Pattern (pattern) — AdiaUI</title>
|
|
7
|
+
|
|
8
|
+
<!-- Token base -->
|
|
9
|
+
<link rel="stylesheet" href="../../styles/resets.css">
|
|
10
|
+
<link rel="stylesheet" href="../../styles/tokens.css">
|
|
11
|
+
|
|
12
|
+
<!-- Component CSS — primitives this pattern composes -->
|
|
13
|
+
<link rel="stylesheet" href="../../components/code/code.css">
|
|
14
|
+
|
|
15
|
+
<!-- Component JS -->
|
|
16
|
+
<script type="module" src="../../components/code/code.js"></script>
|
|
17
|
+
|
|
18
|
+
<style>
|
|
19
|
+
:where(html, body) { margin: 0; min-height: 100vh; background: var(--md-sys-color-neutral-background); color: var(--md-sys-color-neutral-on-surface); font-family: var(--a-font-family); }
|
|
20
|
+
main { max-width: 960px; margin-inline: auto; padding: var(--a-space-6) var(--a-space-5); }
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
|
|
25
|
+
<main id="demo-root">
|
|
26
|
+
<p>Loading examples…</p>
|
|
27
|
+
</main>
|
|
28
|
+
|
|
29
|
+
<script type="module">
|
|
30
|
+
const root = document.getElementById('demo-root');
|
|
31
|
+
try {
|
|
32
|
+
const res = await fetch('./component-tokens.examples.html');
|
|
33
|
+
if (!res.ok) throw new Error(`fetch failed (${res.status})`);
|
|
34
|
+
root.innerHTML = await res.text();
|
|
35
|
+
} catch (err) {
|
|
36
|
+
root.innerHTML = `<p style="color:var(--a-danger-strong);">Failed to load component-tokens.examples.html — ${err.message}</p>`;
|
|
37
|
+
console.error('[component-tokens.html]', err);
|
|
38
|
+
}
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
</body>
|
|
42
|
+
</html>
|