@adia-ai/web-components 0.8.6 → 0.8.8
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 +14 -0
- package/README.md +3 -3
- 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 +1 -1
- 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/type/roles.css +8 -8
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<header>
|
|
2
|
+
<div>
|
|
3
|
+
<h1>Record Detail Drawer</h1>
|
|
4
|
+
<div data-actions>
|
|
5
|
+
<tag-ui size="sm">drawer-ui</tag-ui> <tag-ui size="sm">tabs-ui</tag-ui> <tag-ui size="sm">description-list-ui</tag-ui> <tag-ui size="sm">timeline-ui</tag-ui>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
<p>Side drawer that shows the details of a selected row without navigating away from the list. Header carries the subject (avatar + name + status); body uses tabs to switch between Properties, Activity, and Comments.</p>
|
|
9
|
+
</header>
|
|
10
|
+
|
|
11
|
+
<section data-section data-property="when-to-use">
|
|
12
|
+
<h2 variant="section">When to use</h2>
|
|
13
|
+
<p data-note>
|
|
14
|
+
List-detail workflows where the user bounces between records quickly: CRM contacts,
|
|
15
|
+
issues in a tracker, orders in an e-commerce admin. A drawer preserves the list's
|
|
16
|
+
scroll position and filters, unlike navigating to a detail route.
|
|
17
|
+
</p>
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<section data-section data-property="simple-detail">
|
|
21
|
+
<h2 variant="section">Simple record detail</h2>
|
|
22
|
+
<p data-note>Identity in the header, key/value properties in the body, primary action in the footer.</p>
|
|
23
|
+
<div data-artifact-container>
|
|
24
|
+
<div data-artifact-item data-artifact-label="simple detail">
|
|
25
|
+
<button-ui data-chunk="record-detail-drawer-simple-detail" text="View contact" icon="user" variant="outline" onclick="this.nextElementSibling.open=true"></button-ui>
|
|
26
|
+
<drawer-ui side="right" size="md">
|
|
27
|
+
<header slot="header">
|
|
28
|
+
<row-ui gap="2" align="center" grow>
|
|
29
|
+
<avatar-ui text="Olivia Martin" size="md"></avatar-ui>
|
|
30
|
+
<col-ui gap="0" grow>
|
|
31
|
+
<text-ui strong>Olivia Martin</text-ui>
|
|
32
|
+
<text-ui color="subtle">Acme Corp · Engineering</text-ui>
|
|
33
|
+
</col-ui>
|
|
34
|
+
<badge-ui text="Active" variant="success"></badge-ui>
|
|
35
|
+
</row-ui>
|
|
36
|
+
</header>
|
|
37
|
+
<section slot="body">
|
|
38
|
+
<description-list-ui>
|
|
39
|
+
<dt>Email</dt><dd>olivia.martin@acme.com</dd>
|
|
40
|
+
<dt>Phone</dt><dd>+1 (555) 234-5678</dd>
|
|
41
|
+
<dt>Location</dt><dd>San Francisco, CA</dd>
|
|
42
|
+
<dt>Joined</dt><dd>March 12, 2024</dd>
|
|
43
|
+
<dt>Last active</dt><dd>2 hours ago</dd>
|
|
44
|
+
</description-list-ui>
|
|
45
|
+
</section>
|
|
46
|
+
<footer slot="footer">
|
|
47
|
+
<span slot="action-leading">
|
|
48
|
+
<button-ui text="Archive" icon="archive" variant="ghost"></button-ui>
|
|
49
|
+
</span>
|
|
50
|
+
<span slot="action">
|
|
51
|
+
<button-ui text="Edit" variant="primary"></button-ui>
|
|
52
|
+
</span>
|
|
53
|
+
</footer>
|
|
54
|
+
</drawer-ui>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</section>
|
|
58
|
+
|
|
59
|
+
<section data-section data-property="tabbed-detail">
|
|
60
|
+
<h2 variant="section">Tabbed detail (Properties · Activity · Comments)</h2>
|
|
61
|
+
<p data-note>Tabs sit directly under the header so switching views doesn't require scrolling. Each tab's panel scrolls independently inside the body.</p>
|
|
62
|
+
<div data-artifact-container>
|
|
63
|
+
<div data-artifact-item data-artifact-label="tabbed detail">
|
|
64
|
+
<button-ui data-chunk="record-detail-drawer-tabbed-detail" text="Open issue" icon="bug" variant="outline" onclick="this.nextElementSibling.open=true"></button-ui>
|
|
65
|
+
<drawer-ui side="right" size="lg">
|
|
66
|
+
<header slot="header">
|
|
67
|
+
<col-ui gap="1" grow>
|
|
68
|
+
<row-ui gap="2" align="center">
|
|
69
|
+
<badge-ui text="#142" variant="muted"></badge-ui>
|
|
70
|
+
<text-ui strong>Login button misaligned on Safari</text-ui>
|
|
71
|
+
</row-ui>
|
|
72
|
+
<row-ui gap="2" align="center">
|
|
73
|
+
<badge-ui text="Open" variant="success"></badge-ui>
|
|
74
|
+
<badge-ui text="High" variant="warning"></badge-ui>
|
|
75
|
+
<text-ui color="subtle">Opened by Alex Chen · 2 days ago</text-ui>
|
|
76
|
+
</row-ui>
|
|
77
|
+
</col-ui>
|
|
78
|
+
</header>
|
|
79
|
+
<section slot="body">
|
|
80
|
+
<col-ui gap="4">
|
|
81
|
+
<tabs-ui value="properties">
|
|
82
|
+
<tab-ui value="properties" text="Properties"></tab-ui>
|
|
83
|
+
<tab-ui value="activity" text="Activity"></tab-ui>
|
|
84
|
+
<tab-ui value="comments" text="Comments"></tab-ui>
|
|
85
|
+
</tabs-ui>
|
|
86
|
+
<description-list-ui>
|
|
87
|
+
<dt>Assignee</dt><dd>Sarah Miller</dd>
|
|
88
|
+
<dt>Reporter</dt><dd>Alex Chen</dd>
|
|
89
|
+
<dt>Priority</dt><dd>High</dd>
|
|
90
|
+
<dt>Component</dt><dd>Auth UI</dd>
|
|
91
|
+
<dt>Browser</dt><dd>Safari 17.2</dd>
|
|
92
|
+
<dt>Labels</dt><dd>regression, ui, safari-only</dd>
|
|
93
|
+
</description-list-ui>
|
|
94
|
+
<col-ui gap="2">
|
|
95
|
+
<h4>Recent activity</h4>
|
|
96
|
+
<timeline-ui>
|
|
97
|
+
<timeline-item-ui text="Opened by Alex Chen" time="2 days ago" icon="plus" status="completed"></timeline-item-ui>
|
|
98
|
+
<timeline-item-ui text="Assigned to Sarah Miller" time="1 day ago" icon="user" status="completed"></timeline-item-ui>
|
|
99
|
+
<timeline-item-ui text="Priority raised to High" time="6h ago" icon="arrow-up" status="completed"></timeline-item-ui>
|
|
100
|
+
<timeline-item-ui text="Investigating Safari-specific CSS" time="now" icon="magnifying-glass" status="active"></timeline-item-ui>
|
|
101
|
+
</timeline-ui>
|
|
102
|
+
</col-ui>
|
|
103
|
+
</col-ui>
|
|
104
|
+
</section>
|
|
105
|
+
<footer slot="footer">
|
|
106
|
+
<span slot="action-leading">
|
|
107
|
+
<button-ui text="Close issue" variant="ghost"></button-ui>
|
|
108
|
+
</span>
|
|
109
|
+
<span slot="action">
|
|
110
|
+
<button-ui text="Assign" icon="user" variant="outline"></button-ui>
|
|
111
|
+
<button-ui text="Comment" icon="chat" variant="primary"></button-ui>
|
|
112
|
+
</span>
|
|
113
|
+
</footer>
|
|
114
|
+
</drawer-ui>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</section>
|
|
118
|
+
|
|
119
|
+
<section data-section data-property="anatomy">
|
|
120
|
+
<h2 variant="section">Anatomy</h2>
|
|
121
|
+
<code-ui language="html"><drawer-ui side="right" size="lg">
|
|
122
|
+
|
|
123
|
+
<!-- identity -->
|
|
124
|
+
<header slot="header">
|
|
125
|
+
<row-ui gap="2" align="center" grow>
|
|
126
|
+
<avatar-ui text="Olivia Martin"></avatar-ui>
|
|
127
|
+
<col-ui grow>
|
|
128
|
+
<text-ui weight="semibold">Olivia Martin</text-ui>
|
|
129
|
+
<text-ui color="subtle">Acme Corp</text-ui>
|
|
130
|
+
</col-ui>
|
|
131
|
+
<badge-ui text="Active" variant="success"></badge-ui>
|
|
132
|
+
</row-ui>
|
|
133
|
+
</header>
|
|
134
|
+
|
|
135
|
+
<!-- tabs + content -->
|
|
136
|
+
<section slot="body">
|
|
137
|
+
<tabs-ui value="properties"> ...tabs... </tabs-ui>
|
|
138
|
+
<description-list-ui> ...props... </description-list-ui>
|
|
139
|
+
<timeline-ui> ...events... </timeline-ui>
|
|
140
|
+
</section>
|
|
141
|
+
|
|
142
|
+
<!-- actions — archive on the leading edge, primary on the trailing -->
|
|
143
|
+
<footer slot="footer">
|
|
144
|
+
<span slot="action-leading">
|
|
145
|
+
<button-ui text="Archive" variant="ghost"></button-ui>
|
|
146
|
+
</span>
|
|
147
|
+
<span slot="action">
|
|
148
|
+
<button-ui text="Edit" variant="primary"></button-ui>
|
|
149
|
+
</span>
|
|
150
|
+
</footer>
|
|
151
|
+
|
|
152
|
+
</drawer-ui></code-ui>
|
|
153
|
+
</section>
|
|
154
|
+
|
|
155
|
+
<section data-section data-property="rules">
|
|
156
|
+
<h2 variant="section">Rules</h2>
|
|
157
|
+
<ul>
|
|
158
|
+
<li>Header shows <em>who/what</em> this record is — avatar, name, status. Don't repeat it in the body.</li>
|
|
159
|
+
<li>Use <code><description-list-ui></code> for structured properties, not ad-hoc row/col pairs. Semantic <code>dt/dd</code> pairs read better for screen readers.</li>
|
|
160
|
+
<li>If the record has meaningful history, include a <code><timeline-ui></code>. One timeline per drawer is enough — collapse older events.</li>
|
|
161
|
+
<li>Footer actions: destructive left, primary right. Same convention as form drawer.</li>
|
|
162
|
+
<li>When the drawer is triggered from a table row, pass the row's id as <code>data-record-id</code> on the drawer so consumers can fetch fresh data on open.</li>
|
|
163
|
+
</ul>
|
|
164
|
+
</section>
|
|
@@ -0,0 +1,64 @@
|
|
|
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>Record Detail Drawer (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/avatar/avatar.css">
|
|
14
|
+
<link rel="stylesheet" href="../../components/badge/badge.css">
|
|
15
|
+
<link rel="stylesheet" href="../../components/button/button.css">
|
|
16
|
+
<link rel="stylesheet" href="../../components/code/code.css">
|
|
17
|
+
<link rel="stylesheet" href="../../components/col/col.css">
|
|
18
|
+
<link rel="stylesheet" href="../../components/description-list/description-list.css">
|
|
19
|
+
<link rel="stylesheet" href="../../components/drawer/drawer.css">
|
|
20
|
+
<link rel="stylesheet" href="../../components/row/row.css">
|
|
21
|
+
<link rel="stylesheet" href="../../components/tabs/tabs.css">
|
|
22
|
+
<link rel="stylesheet" href="../../components/tag/tag.css">
|
|
23
|
+
<link rel="stylesheet" href="../../components/text/text.css">
|
|
24
|
+
<link rel="stylesheet" href="../../components/timeline/timeline.css">
|
|
25
|
+
|
|
26
|
+
<!-- Component JS -->
|
|
27
|
+
<script type="module" src="../../components/avatar/avatar.js"></script>
|
|
28
|
+
<script type="module" src="../../components/badge/badge.js"></script>
|
|
29
|
+
<script type="module" src="../../components/button/button.js"></script>
|
|
30
|
+
<script type="module" src="../../components/code/code.js"></script>
|
|
31
|
+
<script type="module" src="../../components/col/col.js"></script>
|
|
32
|
+
<script type="module" src="../../components/description-list/description-list.js"></script>
|
|
33
|
+
<script type="module" src="../../components/drawer/drawer.js"></script>
|
|
34
|
+
<script type="module" src="../../components/row/row.js"></script>
|
|
35
|
+
<script type="module" src="../../components/tabs/tabs.js"></script>
|
|
36
|
+
<script type="module" src="../../components/tag/tag.js"></script>
|
|
37
|
+
<script type="module" src="../../components/text/text.js"></script>
|
|
38
|
+
<script type="module" src="../../components/timeline/timeline.js"></script>
|
|
39
|
+
|
|
40
|
+
<style>
|
|
41
|
+
: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); }
|
|
42
|
+
main { max-width: 960px; margin-inline: auto; padding: var(--a-space-6) var(--a-space-5); }
|
|
43
|
+
</style>
|
|
44
|
+
</head>
|
|
45
|
+
<body>
|
|
46
|
+
|
|
47
|
+
<main id="demo-root">
|
|
48
|
+
<p>Loading examples…</p>
|
|
49
|
+
</main>
|
|
50
|
+
|
|
51
|
+
<script type="module">
|
|
52
|
+
const root = document.getElementById('demo-root');
|
|
53
|
+
try {
|
|
54
|
+
const res = await fetch('./record-detail-drawer.examples.html');
|
|
55
|
+
if (!res.ok) throw new Error(`fetch failed (${res.status})`);
|
|
56
|
+
root.innerHTML = await res.text();
|
|
57
|
+
} catch (err) {
|
|
58
|
+
root.innerHTML = `<p style="color:var(--a-danger-strong);">Failed to load record-detail-drawer.examples.html — ${err.message}</p>`;
|
|
59
|
+
console.error('[record-detail-drawer.html]', err);
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
</body>
|
|
64
|
+
</html>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# responsive-shell-sidebar
|
|
2
|
+
|
|
3
|
+
> **Pattern** — Editor-shell with leading + trailing sidebars that collapse to a drawer below a viewport breakpoint. Documented in v0.5.3 §157 per FEEDBACK-06 §3.
|
|
4
|
+
|
|
5
|
+
## The problem
|
|
6
|
+
|
|
7
|
+
Every responsive editor-shell app (Figma, Linear, VSCode, Notion) needs sidebars that work at all viewport widths. On wide viewports the sidebars are inline panels; below ~900px they collapse to a drawer triggered by a toolbar button. This pattern hand-rolls the dual-mount across consumers — same content rendered in two surfaces, only one visible at a time.
|
|
8
|
+
|
|
9
|
+
FEEDBACK-06 §3 reported color-app needing ~40 LOC of CSS + state plumbing to land this pattern in Tokens Studio. This pattern doc standardizes the recipe so future consumers don't re-derive it.
|
|
10
|
+
|
|
11
|
+
## What's in the pattern
|
|
12
|
+
|
|
13
|
+
1. **CSS convention**: `.wide-only` (`display: contents` at `>900px`, `display: none` at `≤900px`) + `.narrow-only` (inverse).
|
|
14
|
+
2. **Dual mounts**: inline sidebars wrapped in `.wide-only`; drawer twins outside the shell's slots.
|
|
15
|
+
3. **Toolbar triggers**: buttons inside `.narrow-only` open the drawer twins.
|
|
16
|
+
4. **MediaQueryList listener**: auto-closes the drawers when the viewport widens past the breakpoint (the gap FEEDBACK-06 §3 explicitly flagged).
|
|
17
|
+
5. **Signal-based state sharing**: parent template reads signals once and passes extracted values to both mount points.
|
|
18
|
+
|
|
19
|
+
## Caveat: components with local DOM state
|
|
20
|
+
|
|
21
|
+
Components with **local DOM state** (uncontrolled form inputs, scroll position, focused element) will lose state when the viewport crosses the breakpoint — the inline copy unmounts and the drawer copy mounts. **Signal-bound components are safe** because their state lives in the signal, not the DOM. Most AdiaUI primitives are signal-backed; the affected cases are documented in the examples.
|
|
22
|
+
|
|
23
|
+
## Files
|
|
24
|
+
|
|
25
|
+
- `responsive-shell-sidebar.html` — pattern shell page (iframes the examples).
|
|
26
|
+
- `responsive-shell-sidebar.examples.html` — full working dual-mount example with auto-close MQL.
|
|
27
|
+
- `responsive-shell-sidebar.css` — the `.wide-only` / `.narrow-only` + breakpoint shell grid.
|
|
28
|
+
|
|
29
|
+
## Future graduation (v0.6.0)
|
|
30
|
+
|
|
31
|
+
FEEDBACK-06 §3 Option A proposed a built-in `<editor-sidebar responsive-breakpoint="900">` attribute that auto-stamps the drawer twin. Deferred to v0.6.0 — requires editor-shell + pane-ui semantics audit (focus handoff at the inline↔drawer transition, dual-mount event-listener deduplication, etc.). Pairs with a `<show-when breakpoint="...">` trait. Until then, this pattern is the canonical recipe.
|
|
32
|
+
|
|
33
|
+
## References
|
|
34
|
+
|
|
35
|
+
- FEEDBACK-06 §3 — author's recipe + Option A vs Option B trade-off
|
|
36
|
+
- RESPONSE-06 ✅ #3 verdict — Option B chosen (this pattern)
|
|
37
|
+
- .claude/docs/plans/0.5.3-release-plan.md §157
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/* responsive-shell-sidebar pattern — §157 (v0.5.3) per FEEDBACK-06 §3 */
|
|
2
|
+
|
|
3
|
+
/* Breakpoint: 900px. Below → drawer mode; above → inline mode. */
|
|
4
|
+
.wide-only { display: contents; }
|
|
5
|
+
.narrow-only { display: none; }
|
|
6
|
+
|
|
7
|
+
@media (max-width: 900px) {
|
|
8
|
+
.wide-only { display: none; }
|
|
9
|
+
.narrow-only { display: block; }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* Pattern shell — replace with your app's grid */
|
|
13
|
+
.shell {
|
|
14
|
+
display: grid;
|
|
15
|
+
grid-template-columns: 240px 1fr 320px;
|
|
16
|
+
gap: var(--a-space-3);
|
|
17
|
+
height: 100vh;
|
|
18
|
+
padding: var(--a-space-3);
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@media (max-width: 900px) {
|
|
23
|
+
.shell {
|
|
24
|
+
grid-template-columns: 1fr;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.toolbar {
|
|
29
|
+
display: none;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@media (max-width: 900px) {
|
|
33
|
+
.toolbar {
|
|
34
|
+
display: flex;
|
|
35
|
+
gap: var(--a-space-2);
|
|
36
|
+
padding-bottom: var(--a-space-2);
|
|
37
|
+
border-bottom: 1px solid var(--md-sys-color-neutral-outline);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
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>Responsive Shell Sidebar — examples</title>
|
|
7
|
+
|
|
8
|
+
<link rel="stylesheet" href="../../styles/resets.css">
|
|
9
|
+
<link rel="stylesheet" href="../../styles/tokens.css">
|
|
10
|
+
|
|
11
|
+
<link rel="stylesheet" href="../../components/button/button.css">
|
|
12
|
+
<link rel="stylesheet" href="../../components/card/card.css">
|
|
13
|
+
<link rel="stylesheet" href="../../components/col/col.css">
|
|
14
|
+
<link rel="stylesheet" href="../../components/drawer/drawer.css">
|
|
15
|
+
<link rel="stylesheet" href="../../components/icon/icon.css">
|
|
16
|
+
<link rel="stylesheet" href="../../components/list/list.css">
|
|
17
|
+
<link rel="stylesheet" href="../../components/pane/pane.css">
|
|
18
|
+
<link rel="stylesheet" href="../../components/row/row.css">
|
|
19
|
+
<link rel="stylesheet" href="../../components/text/text.css">
|
|
20
|
+
|
|
21
|
+
<script type="module" src="../../components/button/button.js"></script>
|
|
22
|
+
<script type="module" src="../../components/card/card.js"></script>
|
|
23
|
+
<script type="module" src="../../components/col/col.js"></script>
|
|
24
|
+
<script type="module" src="../../components/drawer/drawer.js"></script>
|
|
25
|
+
<script type="module" src="../../components/icon/icon.js"></script>
|
|
26
|
+
<script type="module" src="../../components/list/list.js"></script>
|
|
27
|
+
<script type="module" src="../../components/pane/pane.js"></script>
|
|
28
|
+
<script type="module" src="../../components/row/row.js"></script>
|
|
29
|
+
<script type="module" src="../../components/text/text.js"></script>
|
|
30
|
+
|
|
31
|
+
<script type="module" src="../../core/icons-phosphor.js"></script>
|
|
32
|
+
|
|
33
|
+
<link rel="stylesheet" href="responsive-shell-sidebar.css">
|
|
34
|
+
|
|
35
|
+
<style>
|
|
36
|
+
body { margin: 0; }
|
|
37
|
+
h1, h2, h3 { margin: 0; }
|
|
38
|
+
.doc-body { max-width: 800px; margin: 0 auto; padding: var(--a-space-4); }
|
|
39
|
+
.doc-body h2 { margin-top: var(--a-space-6); }
|
|
40
|
+
.doc-body pre {
|
|
41
|
+
background: var(--a-bg-muted);
|
|
42
|
+
padding: var(--a-space-3);
|
|
43
|
+
border-radius: var(--a-radius);
|
|
44
|
+
overflow-x: auto;
|
|
45
|
+
font-size: 13px;
|
|
46
|
+
}
|
|
47
|
+
</style>
|
|
48
|
+
</head>
|
|
49
|
+
<body data-chunk="responsive-shell-sidebar" data-chunk-kind="page" data-chunk-domain="layout"
|
|
50
|
+
data-chunk-description="Editor-shell with leading + trailing sidebars that collapse to drawers below a 900px viewport breakpoint. Dual-mount: same content renders inline on wide viewports + inside <drawer-ui> twins on narrow. State shared via single signal source consumed by both mount points.">
|
|
51
|
+
|
|
52
|
+
<!-- ═══ INLINE shell (wide-only) ═══ -->
|
|
53
|
+
<div class="shell">
|
|
54
|
+
<!-- Leading sidebar — inline mount (wide-only) -->
|
|
55
|
+
<aside class="wide-only" data-chunk-slot="leading-inline">
|
|
56
|
+
<pane-ui>
|
|
57
|
+
<header>
|
|
58
|
+
<h3>Tokens</h3>
|
|
59
|
+
</header>
|
|
60
|
+
<section>
|
|
61
|
+
<list-ui>
|
|
62
|
+
<list-item-ui>Primary</list-item-ui>
|
|
63
|
+
<list-item-ui>Secondary</list-item-ui>
|
|
64
|
+
<list-item-ui>Neutral</list-item-ui>
|
|
65
|
+
<list-item-ui>Surface</list-item-ui>
|
|
66
|
+
</list-ui>
|
|
67
|
+
</section>
|
|
68
|
+
</pane-ui>
|
|
69
|
+
</aside>
|
|
70
|
+
|
|
71
|
+
<!-- Main content (always inline) -->
|
|
72
|
+
<main data-chunk-slot="main">
|
|
73
|
+
<!-- ═══ Narrow toolbar (drawer triggers) ═══ -->
|
|
74
|
+
<row-ui class="toolbar" data-chunk-slot="narrow-toolbar">
|
|
75
|
+
<button-ui id="open-leading" text="Tokens" icon="list" variant="ghost" size="sm"></button-ui>
|
|
76
|
+
<button-ui id="open-trailing" text="Settings" icon="sliders" variant="ghost" size="sm"></button-ui>
|
|
77
|
+
</row-ui>
|
|
78
|
+
|
|
79
|
+
<card-ui>
|
|
80
|
+
<header>
|
|
81
|
+
<h2>Responsive Shell Sidebar pattern</h2>
|
|
82
|
+
</header>
|
|
83
|
+
<section>
|
|
84
|
+
<text-ui>
|
|
85
|
+
Resize this viewport: at ≤900px the leading + trailing sidebars collapse
|
|
86
|
+
to drawers triggered by the toolbar buttons. Above 900px the sidebars are
|
|
87
|
+
inline. The same content is mounted in both surfaces; consumer signals
|
|
88
|
+
drive both copies via the parent template's re-read.
|
|
89
|
+
</text-ui>
|
|
90
|
+
</section>
|
|
91
|
+
</card-ui>
|
|
92
|
+
|
|
93
|
+
<div class="doc-body">
|
|
94
|
+
<h2>The pattern</h2>
|
|
95
|
+
<p>Three pieces:</p>
|
|
96
|
+
<ol>
|
|
97
|
+
<li><strong>Inline mounts</strong> for the sidebars wrapped in <code>.wide-only</code> (display: contents at >900px; display: none at ≤900px).</li>
|
|
98
|
+
<li><strong>Drawer twins</strong> with the same children, mounted outside the shell's named slots, controlled by toolbar buttons inside <code>.narrow-only</code>.</li>
|
|
99
|
+
<li><strong>MediaQueryList</strong> listener that auto-closes the drawers on viewport widening (the gap FEEDBACK-06 §3 explicitly flagged).</li>
|
|
100
|
+
</ol>
|
|
101
|
+
|
|
102
|
+
<h2>Dual-mount caveat</h2>
|
|
103
|
+
<p>Components with <strong>local DOM state</strong> (uncontrolled form inputs, scroll position, focused element) will lose state when the viewport crosses the breakpoint — the inline copy unmounts and the drawer copy mounts (or vice versa). <strong>Signal-bound components are safe</strong> because their state lives in the signal, not the DOM. Most AdiaUI primitives are signal-backed; the affected cases are:</p>
|
|
104
|
+
<ul>
|
|
105
|
+
<li>Native <code><input></code>/<code><textarea></code> with no signal binding</li>
|
|
106
|
+
<li>Components using <code>this.querySelector(...)</code> with imperative state</li>
|
|
107
|
+
<li>Scroll-position-sensitive containers (e.g. chat threads at a specific scroll)</li>
|
|
108
|
+
</ul>
|
|
109
|
+
<p>For these, either: (a) lift the state to a signal in the parent, or (b) document the unmount-on-resize edge.</p>
|
|
110
|
+
|
|
111
|
+
<h2>Auto-close on viewport widening</h2>
|
|
112
|
+
<code-ui language="javascript">const mql = window.matchMedia('(max-width: 900px)');
|
|
113
|
+
mql.addEventListener('change', (e) => {
|
|
114
|
+
if (!e.matches) {
|
|
115
|
+
// Viewport widened past breakpoint — close any open drawers
|
|
116
|
+
document.getElementById('leading-drawer').open = false;
|
|
117
|
+
document.getElementById('trailing-drawer').open = false;
|
|
118
|
+
}
|
|
119
|
+
});</code-ui>
|
|
120
|
+
|
|
121
|
+
<h2>State sharing</h2>
|
|
122
|
+
<p>Single signal source consumed by both copies. The parent template reads the signal once and passes the extracted value to both the inline mount AND the drawer mount; on signal change the parent re-renders both:</p>
|
|
123
|
+
<code-ui language="javascript">import { signal } from '@adia-ai/web-components/core';
|
|
124
|
+
|
|
125
|
+
const activeToken = signal('primary');
|
|
126
|
+
|
|
127
|
+
static template = () => {
|
|
128
|
+
const current = activeToken.value; // dep read here
|
|
129
|
+
return html`
|
|
130
|
+
<aside class="wide-only">
|
|
131
|
+
<list-ui> ... .active=${current === 'primary'} ... </list-ui>
|
|
132
|
+
</aside>
|
|
133
|
+
<drawer-ui id="leading-drawer">
|
|
134
|
+
<list-ui> ... .active=${current === 'primary'} ... </list-ui>
|
|
135
|
+
</drawer-ui>
|
|
136
|
+
`;
|
|
137
|
+
}</code-ui>
|
|
138
|
+
|
|
139
|
+
<h2>Future v0.6.0: built-in <code>responsive-breakpoint</code></h2>
|
|
140
|
+
<p>FEEDBACK-06 §3 Option A proposed a built-in <code><editor-sidebar responsive-breakpoint="900"></code> attribute that auto-stamps the drawer twin. Deferred to v0.6.0 — requires editor-shell + pane-ui semantics audit (focus handoff at the inline↔drawer transition, dual-mount event-listener deduplication, etc.). Pairs with a <code><show-when breakpoint="..."></code> trait. Until then, the pattern above is the canonical recipe.</p>
|
|
141
|
+
</div>
|
|
142
|
+
</main>
|
|
143
|
+
|
|
144
|
+
<!-- Trailing sidebar — inline mount (wide-only) -->
|
|
145
|
+
<aside class="wide-only" data-chunk-slot="trailing-inline">
|
|
146
|
+
<pane-ui>
|
|
147
|
+
<header>
|
|
148
|
+
<h3>Settings</h3>
|
|
149
|
+
</header>
|
|
150
|
+
<section>
|
|
151
|
+
<list-ui>
|
|
152
|
+
<list-item-ui>Lightness</list-item-ui>
|
|
153
|
+
<list-item-ui>Chroma</list-item-ui>
|
|
154
|
+
<list-item-ui>Hue</list-item-ui>
|
|
155
|
+
</list-ui>
|
|
156
|
+
</section>
|
|
157
|
+
</pane-ui>
|
|
158
|
+
</aside>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
<!-- ═══ DRAWER twins (narrow-only) ═══ -->
|
|
162
|
+
<drawer-ui id="leading-drawer" side="left" size="sm" data-chunk-slot="leading-drawer">
|
|
163
|
+
<header>
|
|
164
|
+
<h3>Tokens</h3>
|
|
165
|
+
</header>
|
|
166
|
+
<section>
|
|
167
|
+
<list-ui>
|
|
168
|
+
<list-item-ui>Primary</list-item-ui>
|
|
169
|
+
<list-item-ui>Secondary</list-item-ui>
|
|
170
|
+
<list-item-ui>Neutral</list-item-ui>
|
|
171
|
+
<list-item-ui>Surface</list-item-ui>
|
|
172
|
+
</list-ui>
|
|
173
|
+
</section>
|
|
174
|
+
</drawer-ui>
|
|
175
|
+
|
|
176
|
+
<drawer-ui id="trailing-drawer" side="right" size="sm" data-chunk-slot="trailing-drawer">
|
|
177
|
+
<header>
|
|
178
|
+
<h3>Settings</h3>
|
|
179
|
+
</header>
|
|
180
|
+
<section>
|
|
181
|
+
<list-ui>
|
|
182
|
+
<list-item-ui>Lightness</list-item-ui>
|
|
183
|
+
<list-item-ui>Chroma</list-item-ui>
|
|
184
|
+
<list-item-ui>Hue</list-item-ui>
|
|
185
|
+
</list-ui>
|
|
186
|
+
</section>
|
|
187
|
+
</drawer-ui>
|
|
188
|
+
|
|
189
|
+
<script type="module">
|
|
190
|
+
// Wire toolbar buttons to drawer triggers
|
|
191
|
+
document.getElementById('open-leading')?.addEventListener('press', () => {
|
|
192
|
+
document.getElementById('leading-drawer').open = true;
|
|
193
|
+
});
|
|
194
|
+
document.getElementById('open-trailing')?.addEventListener('press', () => {
|
|
195
|
+
document.getElementById('trailing-drawer').open = true;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Auto-close drawers when viewport crosses breakpoint (widening)
|
|
199
|
+
const mql = window.matchMedia('(max-width: 900px)');
|
|
200
|
+
mql.addEventListener('change', (e) => {
|
|
201
|
+
if (!e.matches) {
|
|
202
|
+
document.getElementById('leading-drawer').open = false;
|
|
203
|
+
document.getElementById('trailing-drawer').open = false;
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
</script>
|
|
207
|
+
</body>
|
|
208
|
+
</html>
|
|
@@ -0,0 +1,47 @@
|
|
|
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>Responsive Shell Sidebar (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/button/button.css">
|
|
14
|
+
<link rel="stylesheet" href="../../components/card/card.css">
|
|
15
|
+
<link rel="stylesheet" href="../../components/col/col.css">
|
|
16
|
+
<link rel="stylesheet" href="../../components/drawer/drawer.css">
|
|
17
|
+
<link rel="stylesheet" href="../../components/icon/icon.css">
|
|
18
|
+
<link rel="stylesheet" href="../../components/list/list.css">
|
|
19
|
+
<link rel="stylesheet" href="../../components/pane/pane.css">
|
|
20
|
+
<link rel="stylesheet" href="../../components/row/row.css">
|
|
21
|
+
<link rel="stylesheet" href="../../components/text/text.css">
|
|
22
|
+
|
|
23
|
+
<!-- Component JS -->
|
|
24
|
+
<script type="module" src="../../components/button/button.js"></script>
|
|
25
|
+
<script type="module" src="../../components/card/card.js"></script>
|
|
26
|
+
<script type="module" src="../../components/col/col.js"></script>
|
|
27
|
+
<script type="module" src="../../components/drawer/drawer.js"></script>
|
|
28
|
+
<script type="module" src="../../components/icon/icon.js"></script>
|
|
29
|
+
<script type="module" src="../../components/list/list.js"></script>
|
|
30
|
+
<script type="module" src="../../components/pane/pane.js"></script>
|
|
31
|
+
<script type="module" src="../../components/row/row.js"></script>
|
|
32
|
+
<script type="module" src="../../components/text/text.js"></script>
|
|
33
|
+
|
|
34
|
+
<!-- Phosphor icon registry -->
|
|
35
|
+
<script type="module" src="../../core/icons-phosphor.js"></script>
|
|
36
|
+
|
|
37
|
+
<link rel="stylesheet" href="responsive-shell-sidebar.css">
|
|
38
|
+
</head>
|
|
39
|
+
<body>
|
|
40
|
+
<main>
|
|
41
|
+
<h1>Responsive Shell Sidebar</h1>
|
|
42
|
+
<p>Dual-mount editor-shell sidebars that collapse to a drawer below a viewport breakpoint. The same content renders inline on wide viewports + inside a drawer on narrow viewports. Documented in v0.5.3 §157 per FEEDBACK-06 §3.</p>
|
|
43
|
+
|
|
44
|
+
<iframe src="responsive-shell-sidebar.examples.html" style="width:100%; height:90vh; border:1px solid var(--md-sys-color-neutral-outline)"></iframe>
|
|
45
|
+
</main>
|
|
46
|
+
</body>
|
|
47
|
+
</html>
|