bedrock-flows 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/auth-schema.sql +8 -0
- package/bin/bedrock-flows.mjs +127 -0
- package/lib/setup.mjs +262 -0
- package/package.json +11 -0
- package/template/.storybook/main.js +46 -0
- package/template/.storybook/manager-head.html +963 -0
- package/template/.storybook/preview-head.html +35 -0
- package/template/.storybook/preview.js +23 -0
- package/template/CHANGELOG.md +236 -0
- package/template/README.md +26 -0
- package/template/apps/dashboard/index.html +15 -0
- package/template/apps/dashboard/package.json +22 -0
- package/template/apps/dashboard/src/App.module.css +1318 -0
- package/template/apps/dashboard/src/App.tsx +2716 -0
- package/template/apps/dashboard/src/auth-client.ts +17 -0
- package/template/apps/dashboard/src/changelog.tsx +92 -0
- package/template/apps/dashboard/src/index.css +86 -0
- package/template/apps/dashboard/src/main.tsx +15 -0
- package/template/apps/dashboard/src/theme.ts +31 -0
- package/template/apps/dashboard/src/vite-env.d.ts +4 -0
- package/template/apps/dashboard/vite.config.ts +48 -0
- package/template/apps/worker/.dev.vars.example +50 -0
- package/template/apps/worker/package.json +19 -0
- package/template/apps/worker/src/index.ts +295 -0
- package/template/apps/worker/tsconfig.json +11 -0
- package/template/apps/worker/wrangler.jsonc +29 -0
- package/template/bedrock.config.ts +16 -0
- package/template/design-system/README.md +97 -0
- package/template/design-system/starter-v1/components/button/component.css +42 -0
- package/template/design-system/starter-v1/components/button/danger.html +21 -0
- package/template/design-system/starter-v1/components/button/default.html +21 -0
- package/template/design-system/starter-v1/components/button/disabled.html +21 -0
- package/template/design-system/starter-v1/components/button/ghost.html +21 -0
- package/template/design-system/starter-v1/components/button/macro.njk +14 -0
- package/template/design-system/starter-v1/components/button/primary.html +21 -0
- package/template/design-system/starter-v1/components/button/variants.json +30 -0
- package/template/design-system/starter-v1/ds.json +3 -0
- package/template/design-system/starter-v1/global.css +52 -0
- package/template/design-system/starter-v1/style.css +107 -0
- package/template/gitignore +8 -0
- package/template/package.json +41 -0
- package/template/prototypes/F-001-hello/1-welcome.njk +30 -0
- package/template/prototypes/F-001-hello/2-form.njk +46 -0
- package/template/prototypes/F-001-hello/3-done.njk +29 -0
- package/template/prototypes/F-001-hello/meta.json +6 -0
- package/template/prototypes/_shared/_auth-gate.njk +54 -0
- package/template/prototypes/_shared/delivery.njk +43 -0
- package/template/prototypes/_shared/layout.njk +15 -0
- package/template/prototypes/_shared/screen.njk +1818 -0
- package/template/prototypes/_shared/wireflow.njk +4731 -0
- package/template/public/auth-gate.css +150 -0
- package/template/public/bedrock/color-inspector.js +284 -0
- package/template/public/bedrock/component-overlay.js +219 -0
- package/template/public/bedrock/data/bedrock-config.js +45 -0
- package/template/public/bedrock/font-size-overlay.js +590 -0
- package/template/public/bedrock/grid-overlay.js +379 -0
- package/template/public/bedrock/prototype-navigation.js +974 -0
- package/template/public/cmdk.js +146 -0
- package/template/public/ds-xray.css +112 -0
- package/template/public/ds-xray.js +271 -0
- package/template/public/favicon.svg +4 -0
- package/template/public/icons/bolt-fill.svg +3 -0
- package/template/public/icons/bolt.svg +3 -0
- package/template/public/icons/caret-down-fill.svg +3 -0
- package/template/public/icons/check-double.svg +4 -0
- package/template/public/icons/check.svg +3 -0
- package/template/public/icons/chevron-left.svg +3 -0
- package/template/public/icons/chevron-right.svg +3 -0
- package/template/public/icons/circle-info.svg +6 -0
- package/template/public/icons/grid.svg +6 -0
- package/template/public/icons/message-square-1.svg +3 -0
- package/template/public/icons/message-square.svg +3 -0
- package/template/public/icons/messages.svg +4 -0
- package/template/public/icons/options-horizontal.svg +5 -0
- package/template/public/icons/swatches.svg +6 -0
- package/template/public/icons/workflow.svg +6 -0
- package/template/public/lightbox.js +87 -0
- package/template/public/proto-chrome.css +596 -0
- package/template/public/screen-comments.css +723 -0
- package/template/public/wireflow-client.js +26 -0
- package/template/scripts/build-storybooks.mjs +8 -0
- package/template/scripts/dev-setup.mjs +15 -0
- package/template/scripts/generate-stories.mjs +12 -0
- package/template/scripts/generate-variants.mjs +22 -0
- package/template/tsconfig.base.json +19 -0
|
@@ -0,0 +1,1318 @@
|
|
|
1
|
+
/* Dashboard styles. All chrome surfaces read from --dash-* tokens defined
|
|
2
|
+
in src/index.css; @media (prefers-color-scheme: dark) flips them. The
|
|
3
|
+
status badges have their own dark overrides at the bottom because
|
|
4
|
+
they're semantic-color pills, not chrome. */
|
|
5
|
+
|
|
6
|
+
.page {
|
|
7
|
+
display: flex;
|
|
8
|
+
min-height: 100vh;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* App brand at the top of the sidebar (replaces the old top bar). */
|
|
12
|
+
.sidebarBrand {
|
|
13
|
+
padding: 4px 12px 18px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Full-width top bar: optional mobile sidebar toggle · title + info ·
|
|
17
|
+
user pill (pushed right by .headerMain flex:1). Sticky so it stays
|
|
18
|
+
while the main column scrolls. */
|
|
19
|
+
.topbar {
|
|
20
|
+
position: sticky;
|
|
21
|
+
top: 0;
|
|
22
|
+
z-index: 30;
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
gap: 16px;
|
|
26
|
+
padding: 16px 24px;
|
|
27
|
+
background: var(--dash-bg, #fff);
|
|
28
|
+
border-bottom: 1px solid var(--dash-border);
|
|
29
|
+
}
|
|
30
|
+
.headerMain { flex: 1; min-width: 0; }
|
|
31
|
+
|
|
32
|
+
/* Mobile-only top bar: sidebar toggle on the left + current-tab title
|
|
33
|
+
centered. Replaces the earlier floating FAB toggle so users know
|
|
34
|
+
which section they're in. Hidden ≥860px; shown via media query below. */
|
|
35
|
+
.mobileTopBar {
|
|
36
|
+
display: none;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: 8px;
|
|
39
|
+
padding: 8px 12px;
|
|
40
|
+
background: var(--dash-bg, #fff);
|
|
41
|
+
border-bottom: 1px solid var(--dash-border);
|
|
42
|
+
position: sticky;
|
|
43
|
+
top: 0;
|
|
44
|
+
/* Above the full-screen mobile sidebar (z-40) so its toggle stays tappable
|
|
45
|
+
to close the sidebar even when it takes over the screen. */
|
|
46
|
+
z-index: 60;
|
|
47
|
+
}
|
|
48
|
+
.mobileTopBarTitle {
|
|
49
|
+
flex: 1;
|
|
50
|
+
margin: 0;
|
|
51
|
+
text-align: center;
|
|
52
|
+
font-size: 15px;
|
|
53
|
+
font-weight: 600;
|
|
54
|
+
color: var(--dash-text);
|
|
55
|
+
/* Reserve symmetric space against the 36px toggle so the title is
|
|
56
|
+
visually centered in the bar. */
|
|
57
|
+
padding-right: 36px;
|
|
58
|
+
}
|
|
59
|
+
.sidebarToggle {
|
|
60
|
+
display: inline-flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
width: 36px;
|
|
64
|
+
height: 36px;
|
|
65
|
+
padding: 0;
|
|
66
|
+
border: 1px solid var(--dash-border);
|
|
67
|
+
border-radius: 8px;
|
|
68
|
+
background: var(--dash-surface);
|
|
69
|
+
color: var(--dash-text);
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
}
|
|
72
|
+
.sidebarToggle:hover { background: var(--dash-surface-alt, var(--dash-surface)); }
|
|
73
|
+
|
|
74
|
+
/* Body: sidebar + scrollable main. */
|
|
75
|
+
.shell {
|
|
76
|
+
display: flex;
|
|
77
|
+
flex: 1;
|
|
78
|
+
align-items: stretch;
|
|
79
|
+
min-height: 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.sidebar {
|
|
83
|
+
flex: none;
|
|
84
|
+
width: 224px;
|
|
85
|
+
display: flex;
|
|
86
|
+
flex-direction: column;
|
|
87
|
+
gap: 2px;
|
|
88
|
+
padding: 24px 12px 16px;
|
|
89
|
+
border-right: 1px solid var(--dash-border);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.navBtn {
|
|
93
|
+
appearance: none;
|
|
94
|
+
display: flex;
|
|
95
|
+
align-items: center;
|
|
96
|
+
justify-content: space-between;
|
|
97
|
+
gap: 8px;
|
|
98
|
+
width: 100%;
|
|
99
|
+
text-align: left;
|
|
100
|
+
background: none;
|
|
101
|
+
border: 0;
|
|
102
|
+
border-radius: 8px;
|
|
103
|
+
padding: 9px 12px;
|
|
104
|
+
font-size: 14px;
|
|
105
|
+
font-weight: 500;
|
|
106
|
+
color: var(--dash-text-muted);
|
|
107
|
+
cursor: pointer;
|
|
108
|
+
}
|
|
109
|
+
.navIcon {
|
|
110
|
+
flex: none;
|
|
111
|
+
color: var(--dash-text-muted);
|
|
112
|
+
}
|
|
113
|
+
.navBtn:hover .navIcon,
|
|
114
|
+
.navBtnActive .navIcon {
|
|
115
|
+
color: var(--dash-text);
|
|
116
|
+
}
|
|
117
|
+
.navLabel {
|
|
118
|
+
flex: 1;
|
|
119
|
+
}
|
|
120
|
+
.navBadge {
|
|
121
|
+
flex: none;
|
|
122
|
+
display: inline-flex;
|
|
123
|
+
align-items: center;
|
|
124
|
+
justify-content: center;
|
|
125
|
+
min-width: 20px;
|
|
126
|
+
height: 20px;
|
|
127
|
+
padding: 0 6px;
|
|
128
|
+
border-radius: 999px;
|
|
129
|
+
background: var(--dash-surface);
|
|
130
|
+
border: 1px solid var(--dash-border);
|
|
131
|
+
font-size: 11px;
|
|
132
|
+
font-weight: 600;
|
|
133
|
+
color: var(--dash-text-muted);
|
|
134
|
+
}
|
|
135
|
+
.navBtnActive .navBadge {
|
|
136
|
+
color: var(--dash-text);
|
|
137
|
+
}
|
|
138
|
+
.navBtn:hover { color: var(--dash-text); background: var(--dash-surface); }
|
|
139
|
+
.navBtnActive,
|
|
140
|
+
.navBtnActive:hover {
|
|
141
|
+
color: var(--dash-text);
|
|
142
|
+
background: var(--dash-surface);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Sidebar section header (e.g. "Wireflows") above a group of nav items. */
|
|
146
|
+
.navSectionLabel {
|
|
147
|
+
padding: 6px 12px 2px;
|
|
148
|
+
font-size: 11px;
|
|
149
|
+
font-weight: 600;
|
|
150
|
+
text-transform: uppercase;
|
|
151
|
+
letter-spacing: 0.04em;
|
|
152
|
+
color: var(--dash-text-muted);
|
|
153
|
+
}
|
|
154
|
+
/* Thin separator between nav groups. */
|
|
155
|
+
.navSep {
|
|
156
|
+
height: 1px;
|
|
157
|
+
margin: 8px 12px;
|
|
158
|
+
background: var(--dash-border);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* Top-bar sidebar toggle (shadcn-style) — collapse/expand the sidebar. */
|
|
162
|
+
.topbarToggle {
|
|
163
|
+
appearance: none;
|
|
164
|
+
display: inline-flex;
|
|
165
|
+
align-items: center;
|
|
166
|
+
justify-content: center;
|
|
167
|
+
flex: none;
|
|
168
|
+
width: 30px;
|
|
169
|
+
height: 30px;
|
|
170
|
+
margin-right: 4px;
|
|
171
|
+
padding: 0;
|
|
172
|
+
border: 0;
|
|
173
|
+
border-radius: 7px;
|
|
174
|
+
background: none;
|
|
175
|
+
color: var(--dash-text-muted);
|
|
176
|
+
cursor: pointer;
|
|
177
|
+
}
|
|
178
|
+
.topbarToggle:hover { background: var(--dash-surface); color: var(--dash-text); }
|
|
179
|
+
|
|
180
|
+
/* Collapsed (desktop): hide the sidebar so the content column fills the
|
|
181
|
+
width. Scoped to desktop in the min-width media query below — on mobile the
|
|
182
|
+
sidebar is driven by the hamburger, not the collapse state. */
|
|
183
|
+
|
|
184
|
+
/* Content column (right of the sidebar): a full-width column holding the
|
|
185
|
+
module top bar (spans the whole browser width) above the centred main. */
|
|
186
|
+
.contentCol {
|
|
187
|
+
flex: 1;
|
|
188
|
+
min-width: 0;
|
|
189
|
+
display: flex;
|
|
190
|
+
flex-direction: column;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.main {
|
|
194
|
+
/* Generous cap so the flows table uses the space on wide screens
|
|
195
|
+
instead of wrapping the Feature Name column; still centred and
|
|
196
|
+
readable on ultra-wide displays. */
|
|
197
|
+
width: 100%;
|
|
198
|
+
max-width: 1500px;
|
|
199
|
+
align-self: center;
|
|
200
|
+
padding: 28px 32px 96px;
|
|
201
|
+
box-sizing: border-box;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* Module top bar — every module (Flows, Design systems, Resolution queue)
|
|
205
|
+
gets one so it's always clear where you are. Mirrors the Ziptility desktop
|
|
206
|
+
top-bar: a full-width bar (spans the whole browser width right of the
|
|
207
|
+
sidebar) with the title + optional info and a bottom border. Hidden on
|
|
208
|
+
mobile, where the dedicated .mobileTopBar already shows the module title. */
|
|
209
|
+
.pageTopbar {
|
|
210
|
+
display: flex;
|
|
211
|
+
align-items: center;
|
|
212
|
+
gap: 6px;
|
|
213
|
+
width: 100%;
|
|
214
|
+
box-sizing: border-box;
|
|
215
|
+
margin: 0;
|
|
216
|
+
padding: 18px 32px;
|
|
217
|
+
border-bottom: 1px solid var(--dash-border);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.pageTopbarTitle {
|
|
221
|
+
font-size: 18px;
|
|
222
|
+
font-weight: 600;
|
|
223
|
+
line-height: 1.3;
|
|
224
|
+
margin: 0;
|
|
225
|
+
color: var(--dash-text);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* Mobile drawer backdrop. */
|
|
229
|
+
.scrim {
|
|
230
|
+
position: fixed;
|
|
231
|
+
inset: 0;
|
|
232
|
+
z-index: 35;
|
|
233
|
+
background: rgba(15, 23, 42, 0.45);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* User menu — pill that opens a dropdown with sign-out. */
|
|
237
|
+
.userMenu { position: relative; flex: none; }
|
|
238
|
+
.userPill {
|
|
239
|
+
display: inline-flex;
|
|
240
|
+
align-items: center;
|
|
241
|
+
gap: 10px;
|
|
242
|
+
padding: 6px 12px 6px 6px;
|
|
243
|
+
border: 1px solid var(--dash-border);
|
|
244
|
+
border-radius: 999px;
|
|
245
|
+
background: var(--dash-surface);
|
|
246
|
+
font-size: 13px;
|
|
247
|
+
font: inherit;
|
|
248
|
+
font-size: 13px;
|
|
249
|
+
color: var(--dash-text);
|
|
250
|
+
cursor: pointer;
|
|
251
|
+
}
|
|
252
|
+
.userPill:hover { background: var(--dash-surface-alt, var(--dash-surface)); }
|
|
253
|
+
/* Caret sits flush right (justified to the pill edge), slightly larger
|
|
254
|
+
so it reads as the expand affordance rather than a tiny decoration. */
|
|
255
|
+
.userCaret { margin-left: auto; opacity: 0.7; flex: none; transition: transform 0.15s ease; }
|
|
256
|
+
.userCaretOpen { transform: rotate(180deg); }
|
|
257
|
+
.userPanel {
|
|
258
|
+
position: absolute; top: calc(100% + 6px); right: 0;
|
|
259
|
+
min-width: 200px;
|
|
260
|
+
padding: 6px;
|
|
261
|
+
background: var(--dash-surface);
|
|
262
|
+
border: 1px solid var(--dash-border);
|
|
263
|
+
border-radius: 8px;
|
|
264
|
+
box-shadow: 0 12px 32px rgba(15, 23, 42, 0.18);
|
|
265
|
+
z-index: 50;
|
|
266
|
+
display: flex; flex-direction: column; gap: 2px;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* User pill pinned to the bottom of the sidebar. Full-width trigger;
|
|
270
|
+
its dropdown opens upward (there's no room below at the bottom). */
|
|
271
|
+
.sidebarUser {
|
|
272
|
+
margin-top: auto;
|
|
273
|
+
padding-top: 16px;
|
|
274
|
+
}
|
|
275
|
+
.sidebarUser .userMenu { width: 100%; }
|
|
276
|
+
.sidebarUser .userPill { width: 100%; }
|
|
277
|
+
.sidebarUser .userPanel {
|
|
278
|
+
top: auto;
|
|
279
|
+
bottom: calc(100% + 6px);
|
|
280
|
+
left: 0;
|
|
281
|
+
right: 0;
|
|
282
|
+
}
|
|
283
|
+
.userPanelMeta {
|
|
284
|
+
padding: 6px 10px;
|
|
285
|
+
font-size: 11px;
|
|
286
|
+
color: var(--dash-text-muted);
|
|
287
|
+
border-bottom: 1px solid var(--dash-border);
|
|
288
|
+
margin-bottom: 4px;
|
|
289
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
290
|
+
}
|
|
291
|
+
.userPanelItem {
|
|
292
|
+
display: flex; align-items: center; gap: 6px;
|
|
293
|
+
padding: 7px 10px;
|
|
294
|
+
background: transparent; border: 0;
|
|
295
|
+
color: var(--dash-text);
|
|
296
|
+
font: inherit; font-size: 13px;
|
|
297
|
+
text-align: left;
|
|
298
|
+
border-radius: 6px;
|
|
299
|
+
cursor: pointer;
|
|
300
|
+
}
|
|
301
|
+
.userPanelItem:hover { background: var(--dash-surface-alt, var(--dash-border)); }
|
|
302
|
+
.userPanelLabel {
|
|
303
|
+
padding: 6px 10px 2px;
|
|
304
|
+
font-size: 10.5px; font-weight: 600; letter-spacing: 0.04em;
|
|
305
|
+
text-transform: uppercase;
|
|
306
|
+
color: var(--dash-text-muted);
|
|
307
|
+
}
|
|
308
|
+
.themeToggle {
|
|
309
|
+
display: flex; gap: 2px;
|
|
310
|
+
margin: 0 8px 4px;
|
|
311
|
+
padding: 2px;
|
|
312
|
+
background: var(--dash-surface-alt);
|
|
313
|
+
border: 1px solid var(--dash-border);
|
|
314
|
+
border-radius: 7px;
|
|
315
|
+
}
|
|
316
|
+
.themeOption {
|
|
317
|
+
flex: 1; padding: 5px 6px;
|
|
318
|
+
background: transparent; border: 0; border-radius: 5px;
|
|
319
|
+
color: var(--dash-text-muted);
|
|
320
|
+
font: inherit; font-size: 12px;
|
|
321
|
+
cursor: pointer;
|
|
322
|
+
}
|
|
323
|
+
.themeOption:hover { color: var(--dash-text); }
|
|
324
|
+
.themeOptionActive,
|
|
325
|
+
.themeOptionActive:hover {
|
|
326
|
+
background: var(--dash-surface);
|
|
327
|
+
color: var(--dash-text);
|
|
328
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.12);
|
|
329
|
+
}
|
|
330
|
+
.userAvatar,
|
|
331
|
+
.userAvatarFallback {
|
|
332
|
+
width: 28px; height: 28px;
|
|
333
|
+
border-radius: 999px;
|
|
334
|
+
display: flex; align-items: center; justify-content: center;
|
|
335
|
+
font-size: 11px; font-weight: 600;
|
|
336
|
+
background: var(--dash-link);
|
|
337
|
+
color: white;
|
|
338
|
+
overflow: hidden;
|
|
339
|
+
}
|
|
340
|
+
.userAvatar { object-fit: cover; }
|
|
341
|
+
.userName {
|
|
342
|
+
max-width: 180px;
|
|
343
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
344
|
+
color: var(--dash-text);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* Auth gate overlay — markup in App.tsx <AuthGate/>, styling from the shared
|
|
348
|
+
/auth-gate.css (bf-auth-* classes; loaded via index.html). One stylesheet
|
|
349
|
+
for the dashboard, wireflow, and screen gates. */
|
|
350
|
+
|
|
351
|
+
.title {
|
|
352
|
+
font-size: 17px;
|
|
353
|
+
font-weight: 700;
|
|
354
|
+
margin: 0;
|
|
355
|
+
letter-spacing: -0.01em;
|
|
356
|
+
color: var(--dash-text);
|
|
357
|
+
display: inline-flex;
|
|
358
|
+
align-items: center;
|
|
359
|
+
gap: 10px;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/* Running-release badge under the brand title — text comes from the latest
|
|
363
|
+
CHANGELOG.md heading at build time. Clicking opens the changelog tab. */
|
|
364
|
+
.versionPill {
|
|
365
|
+
display: inline-flex;
|
|
366
|
+
align-items: center;
|
|
367
|
+
margin-top: 6px;
|
|
368
|
+
padding: 2px 8px;
|
|
369
|
+
border: 1px solid var(--dash-border);
|
|
370
|
+
border-radius: 999px;
|
|
371
|
+
background: transparent;
|
|
372
|
+
color: var(--dash-text-muted);
|
|
373
|
+
font: inherit;
|
|
374
|
+
font-size: 11.5px;
|
|
375
|
+
font-weight: 600;
|
|
376
|
+
font-variant-numeric: tabular-nums;
|
|
377
|
+
letter-spacing: 0.01em;
|
|
378
|
+
cursor: pointer;
|
|
379
|
+
}
|
|
380
|
+
.versionPill:hover,
|
|
381
|
+
.versionPill:focus-visible {
|
|
382
|
+
color: var(--dash-text);
|
|
383
|
+
border-color: var(--dash-border-strong);
|
|
384
|
+
background: var(--dash-surface);
|
|
385
|
+
outline: none;
|
|
386
|
+
}
|
|
387
|
+
.versionPillActive {
|
|
388
|
+
color: var(--dash-text);
|
|
389
|
+
border-color: var(--dash-border-strong);
|
|
390
|
+
background: var(--dash-surface);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/* Changelog page — CHANGELOG.md rendered in-app (see src/changelog.tsx). */
|
|
394
|
+
.changelog { max-width: 720px; }
|
|
395
|
+
.changelog a { color: inherit; }
|
|
396
|
+
.changelogNote {
|
|
397
|
+
margin: 10px 0;
|
|
398
|
+
font-size: 13.5px;
|
|
399
|
+
line-height: 1.6;
|
|
400
|
+
color: var(--dash-text-muted);
|
|
401
|
+
}
|
|
402
|
+
.changelogVersion {
|
|
403
|
+
display: flex;
|
|
404
|
+
align-items: baseline;
|
|
405
|
+
gap: 10px;
|
|
406
|
+
margin: 30px 0 4px;
|
|
407
|
+
padding-top: 22px;
|
|
408
|
+
border-top: 1px solid var(--dash-border);
|
|
409
|
+
font-size: 18px;
|
|
410
|
+
font-weight: 700;
|
|
411
|
+
letter-spacing: -0.01em;
|
|
412
|
+
color: var(--dash-text);
|
|
413
|
+
font-variant-numeric: tabular-nums;
|
|
414
|
+
}
|
|
415
|
+
.changelog .changelogVersion:first-of-type {
|
|
416
|
+
margin-top: 14px;
|
|
417
|
+
padding-top: 0;
|
|
418
|
+
border-top: 0;
|
|
419
|
+
}
|
|
420
|
+
.changelogCurrent {
|
|
421
|
+
align-self: center;
|
|
422
|
+
padding: 1px 7px;
|
|
423
|
+
border-radius: 999px;
|
|
424
|
+
background: var(--dash-surface);
|
|
425
|
+
border: 1px solid var(--dash-border-strong);
|
|
426
|
+
color: var(--dash-text);
|
|
427
|
+
font-size: 10.5px;
|
|
428
|
+
font-weight: 700;
|
|
429
|
+
text-transform: uppercase;
|
|
430
|
+
letter-spacing: 0.05em;
|
|
431
|
+
}
|
|
432
|
+
.changelogDate {
|
|
433
|
+
font-size: 12.5px;
|
|
434
|
+
font-weight: 500;
|
|
435
|
+
color: var(--dash-text-muted);
|
|
436
|
+
}
|
|
437
|
+
.changelogGroup {
|
|
438
|
+
margin: 18px 0 6px;
|
|
439
|
+
font-size: 11.5px;
|
|
440
|
+
font-weight: 700;
|
|
441
|
+
text-transform: uppercase;
|
|
442
|
+
letter-spacing: 0.05em;
|
|
443
|
+
color: var(--dash-text-muted);
|
|
444
|
+
}
|
|
445
|
+
.changelogList {
|
|
446
|
+
margin: 0;
|
|
447
|
+
padding-left: 18px;
|
|
448
|
+
font-size: 13.5px;
|
|
449
|
+
line-height: 1.6;
|
|
450
|
+
color: var(--dash-text);
|
|
451
|
+
}
|
|
452
|
+
.changelogList li + li { margin-top: 6px; }
|
|
453
|
+
.changelogList code,
|
|
454
|
+
.changelogNote code {
|
|
455
|
+
padding: 1px 5px;
|
|
456
|
+
border-radius: 4px;
|
|
457
|
+
background: var(--dash-surface);
|
|
458
|
+
border: 1px solid var(--dash-border);
|
|
459
|
+
font-size: 12px;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/* Compact "i" affordance next to the title — replaces the always-visible
|
|
463
|
+
subtitle paragraph. Hover or focus reveals the info panel. */
|
|
464
|
+
.titleInfo {
|
|
465
|
+
position: relative;
|
|
466
|
+
display: inline-flex;
|
|
467
|
+
align-items: center;
|
|
468
|
+
justify-content: center;
|
|
469
|
+
/* Ghost button: 24×24 hit target around a 16×16 circle-info glyph,
|
|
470
|
+
no border at rest. Transparent background; a faint slate surface
|
|
471
|
+
appears only on hover/focus. */
|
|
472
|
+
width: 24px; height: 24px;
|
|
473
|
+
padding: 0;
|
|
474
|
+
border: 0;
|
|
475
|
+
border-radius: 6px;
|
|
476
|
+
background: transparent;
|
|
477
|
+
color: var(--dash-text-muted);
|
|
478
|
+
cursor: help;
|
|
479
|
+
/* Kill the browser's default 2px black focus ring; the hover/focus
|
|
480
|
+
background below is the affordance. */
|
|
481
|
+
outline: none;
|
|
482
|
+
}
|
|
483
|
+
.titleInfo > svg { display: block; flex: none; }
|
|
484
|
+
.titleInfo:hover,
|
|
485
|
+
.titleInfo:focus-visible {
|
|
486
|
+
color: var(--dash-text);
|
|
487
|
+
background: var(--dash-surface);
|
|
488
|
+
outline: none;
|
|
489
|
+
}
|
|
490
|
+
.titleInfo:focus { outline: none; }
|
|
491
|
+
.titleInfoPanel {
|
|
492
|
+
position: absolute;
|
|
493
|
+
top: calc(100% + 8px);
|
|
494
|
+
left: 0;
|
|
495
|
+
width: 360px;
|
|
496
|
+
max-width: calc(100vw - 32px);
|
|
497
|
+
padding: 12px 14px;
|
|
498
|
+
background: var(--dash-surface);
|
|
499
|
+
color: var(--dash-text-muted);
|
|
500
|
+
border: 1px solid var(--dash-border);
|
|
501
|
+
border-radius: 8px;
|
|
502
|
+
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
|
|
503
|
+
font-size: 13px;
|
|
504
|
+
font-weight: 400;
|
|
505
|
+
font-style: normal;
|
|
506
|
+
/* Not `inherit` — the .titleInfo badge is intentionally serif-italic
|
|
507
|
+
(the "i" glyph); the panel must use the dashboard's sans stack. */
|
|
508
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
509
|
+
line-height: 1.5;
|
|
510
|
+
letter-spacing: normal;
|
|
511
|
+
text-transform: none;
|
|
512
|
+
text-align: left;
|
|
513
|
+
z-index: 10;
|
|
514
|
+
display: none;
|
|
515
|
+
}
|
|
516
|
+
/* Transparent bridge spanning the 8px gap between the badge and the
|
|
517
|
+
panel. Without it the pointer drops :hover while crossing the gap and
|
|
518
|
+
the panel closes before you can reach the link inside it. */
|
|
519
|
+
.titleInfoPanel::before {
|
|
520
|
+
content: '';
|
|
521
|
+
position: absolute;
|
|
522
|
+
left: 0;
|
|
523
|
+
right: 0;
|
|
524
|
+
top: -8px;
|
|
525
|
+
height: 8px;
|
|
526
|
+
}
|
|
527
|
+
.titleInfo:hover .titleInfoPanel,
|
|
528
|
+
.titleInfo:focus-visible .titleInfoPanel,
|
|
529
|
+
.titleInfo:focus-within .titleInfoPanel {
|
|
530
|
+
display: block;
|
|
531
|
+
}
|
|
532
|
+
.titleInfoPanel a {
|
|
533
|
+
color: var(--dash-link);
|
|
534
|
+
text-decoration: none;
|
|
535
|
+
}
|
|
536
|
+
.titleInfoPanel a:hover {
|
|
537
|
+
text-decoration: underline;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.sectionTitle {
|
|
541
|
+
font-size: 13px;
|
|
542
|
+
font-weight: 600;
|
|
543
|
+
color: var(--dash-text-muted);
|
|
544
|
+
letter-spacing: 0.04em;
|
|
545
|
+
text-transform: uppercase;
|
|
546
|
+
margin: 0 0 12px;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.dsSection {
|
|
550
|
+
margin-bottom: 40px;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.dsList {
|
|
554
|
+
list-style: none;
|
|
555
|
+
margin: 0;
|
|
556
|
+
padding: 0;
|
|
557
|
+
display: grid;
|
|
558
|
+
gap: 12px;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.dsItem {
|
|
562
|
+
display: grid;
|
|
563
|
+
grid-template-columns: 1fr auto;
|
|
564
|
+
grid-template-rows: auto auto;
|
|
565
|
+
column-gap: 16px;
|
|
566
|
+
row-gap: 12px;
|
|
567
|
+
align-items: center;
|
|
568
|
+
padding: 18px 20px;
|
|
569
|
+
background: var(--dash-surface);
|
|
570
|
+
border: 1px solid var(--dash-border);
|
|
571
|
+
border-radius: 10px;
|
|
572
|
+
font-size: 14px;
|
|
573
|
+
}
|
|
574
|
+
/* No :hover on .dsItem — the card itself isn't interactive; only the
|
|
575
|
+
"Open storybook" link (.dsLink) is. Don't give non-interactive
|
|
576
|
+
containers hover affordances. */
|
|
577
|
+
/* Row 1: name+count on the left, "Open …" on the right.
|
|
578
|
+
Row 2: chips span the full card width. */
|
|
579
|
+
.dsItem > :first-child {
|
|
580
|
+
grid-column: 1;
|
|
581
|
+
grid-row: 1;
|
|
582
|
+
}
|
|
583
|
+
.dsItem .dsLink {
|
|
584
|
+
grid-column: 2;
|
|
585
|
+
grid-row: 1;
|
|
586
|
+
}
|
|
587
|
+
.dsItem .dsComponents {
|
|
588
|
+
grid-column: 1 / -1;
|
|
589
|
+
grid-row: 2;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.dsVersion {
|
|
593
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
594
|
+
font-weight: 600;
|
|
595
|
+
color: var(--dash-text);
|
|
596
|
+
text-decoration: none;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.dsVersion:hover { text-decoration: underline; }
|
|
600
|
+
|
|
601
|
+
.dsCount {
|
|
602
|
+
margin-left: 10px;
|
|
603
|
+
color: var(--dash-text-muted);
|
|
604
|
+
font-size: 12.5px;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.dsComponents {
|
|
608
|
+
display: flex;
|
|
609
|
+
flex-wrap: wrap;
|
|
610
|
+
gap: 6px;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.dsChip {
|
|
614
|
+
display: inline-block;
|
|
615
|
+
padding: 2px 9px;
|
|
616
|
+
font-size: 11.5px;
|
|
617
|
+
background: var(--dash-surface-alt);
|
|
618
|
+
color: var(--dash-text-muted);
|
|
619
|
+
border-radius: 999px;
|
|
620
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
621
|
+
text-decoration: none;
|
|
622
|
+
transition: color 0.15s, background 0.15s;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
a.dsChip:hover {
|
|
626
|
+
color: var(--dash-text);
|
|
627
|
+
background: var(--dash-border);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/* Small numeric badge inside a base-page chip showing how many state
|
|
631
|
+
variants share that base (e.g. `invite-dialog +3`). Sits inline with
|
|
632
|
+
the label so the chip stays a single visual unit. */
|
|
633
|
+
.dsChipBadge {
|
|
634
|
+
display: inline-block;
|
|
635
|
+
margin-left: 6px;
|
|
636
|
+
padding: 0 6px;
|
|
637
|
+
font-size: 10px;
|
|
638
|
+
font-weight: 600;
|
|
639
|
+
background: var(--dash-border);
|
|
640
|
+
color: var(--dash-text);
|
|
641
|
+
border-radius: 999px;
|
|
642
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
.dsLink {
|
|
646
|
+
color: var(--dash-link);
|
|
647
|
+
text-decoration: none;
|
|
648
|
+
font-weight: 500;
|
|
649
|
+
font-size: 13px;
|
|
650
|
+
white-space: nowrap;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.dsLink:hover { text-decoration: underline; }
|
|
654
|
+
|
|
655
|
+
.muted {
|
|
656
|
+
color: var(--dash-text-muted);
|
|
657
|
+
font-size: 14px;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.error {
|
|
661
|
+
color: #b91c1c;
|
|
662
|
+
font-size: 14px;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
.table {
|
|
666
|
+
width: 100%;
|
|
667
|
+
border-collapse: collapse;
|
|
668
|
+
background: var(--dash-surface);
|
|
669
|
+
border: 1px solid var(--dash-border);
|
|
670
|
+
border-radius: 8px;
|
|
671
|
+
overflow: hidden;
|
|
672
|
+
font-size: 14px;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.table thead th {
|
|
676
|
+
text-align: left;
|
|
677
|
+
font-weight: 500;
|
|
678
|
+
background: transparent;
|
|
679
|
+
color: var(--dash-text-muted);
|
|
680
|
+
font-size: 11px;
|
|
681
|
+
letter-spacing: 0.04em;
|
|
682
|
+
text-transform: uppercase;
|
|
683
|
+
padding: 8px 14px;
|
|
684
|
+
border-bottom: 1px solid var(--dash-border);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
.table tbody td {
|
|
688
|
+
padding: 14px 14px;
|
|
689
|
+
border-bottom: 1px solid var(--dash-table-divider);
|
|
690
|
+
/* Top-align so the first line of every column lines up across the row —
|
|
691
|
+
feature names wrap to 2–3 lines, so middle-align left single-line cells
|
|
692
|
+
(F-ID, status, references) floating out of alignment with each other. */
|
|
693
|
+
vertical-align: top;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.table tbody tr:last-child td {
|
|
697
|
+
border-bottom: none;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
.table tbody tr:hover {
|
|
701
|
+
background: var(--dash-table-row-hover);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.colId {
|
|
705
|
+
width: 80px;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
.colDs {
|
|
709
|
+
width: 60px;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
.colStatus {
|
|
713
|
+
width: 160px;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/* Merged References column: DS + Figma stacked vertically.
|
|
717
|
+
Each row is a small flex with a left-aligned icon so the kind of
|
|
718
|
+
reference reads instantly without a separate column header. */
|
|
719
|
+
.colRefs {
|
|
720
|
+
width: 220px;
|
|
721
|
+
}
|
|
722
|
+
.refsCell {
|
|
723
|
+
display: flex;
|
|
724
|
+
flex-direction: column;
|
|
725
|
+
gap: 4px;
|
|
726
|
+
line-height: 1.35;
|
|
727
|
+
}
|
|
728
|
+
.refRow {
|
|
729
|
+
display: inline-flex;
|
|
730
|
+
align-items: center;
|
|
731
|
+
gap: 6px;
|
|
732
|
+
}
|
|
733
|
+
.refIcon {
|
|
734
|
+
flex: none;
|
|
735
|
+
color: var(--dash-text-muted);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
.colComments {
|
|
739
|
+
width: 100px;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
.tsetEmpty {
|
|
743
|
+
color: var(--dash-border-strong);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.tabs {
|
|
747
|
+
display: flex;
|
|
748
|
+
gap: 4px;
|
|
749
|
+
border-bottom: 1px solid var(--dash-border);
|
|
750
|
+
margin-bottom: 20px;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
.tab {
|
|
754
|
+
appearance: none;
|
|
755
|
+
background: none;
|
|
756
|
+
border: none;
|
|
757
|
+
padding: 10px 14px;
|
|
758
|
+
font-size: 14px;
|
|
759
|
+
font-weight: 500;
|
|
760
|
+
color: var(--dash-text-muted);
|
|
761
|
+
cursor: pointer;
|
|
762
|
+
border-bottom: 2px solid transparent;
|
|
763
|
+
margin-bottom: -1px;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
.tab:hover { color: var(--dash-text); }
|
|
767
|
+
|
|
768
|
+
.tabActive {
|
|
769
|
+
color: var(--dash-text);
|
|
770
|
+
border-bottom-color: var(--dash-text);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
.statusBadge {
|
|
774
|
+
display: inline-block;
|
|
775
|
+
padding: 2px 9px;
|
|
776
|
+
font-size: 12px;
|
|
777
|
+
border-radius: 999px;
|
|
778
|
+
font-weight: 500;
|
|
779
|
+
background: #f1f5f9;
|
|
780
|
+
color: #475569;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.statusTodo {
|
|
784
|
+
background: #f1f5f9;
|
|
785
|
+
color: #64748b;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.statusValidate {
|
|
789
|
+
background: #fef3c7;
|
|
790
|
+
color: #92400e;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
.statusWip {
|
|
794
|
+
background: #dbeafe;
|
|
795
|
+
color: #1e40af;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
.statusReady {
|
|
799
|
+
background: #dcfce7;
|
|
800
|
+
color: #166534;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
.colLinks {
|
|
804
|
+
width: 180px;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
.mono {
|
|
808
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
809
|
+
font-size: 12.5px;
|
|
810
|
+
color: var(--dash-text);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
.dsCell {
|
|
814
|
+
color: var(--dash-link);
|
|
815
|
+
text-decoration: none;
|
|
816
|
+
font-family: inherit;
|
|
817
|
+
white-space: nowrap;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
.dsCell:hover { text-decoration: underline; }
|
|
821
|
+
|
|
822
|
+
.featureLink {
|
|
823
|
+
color: var(--dash-link);
|
|
824
|
+
text-decoration: none;
|
|
825
|
+
font-weight: 500;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
.featureLink:hover {
|
|
829
|
+
text-decoration: underline;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/* Version pill next to the F-ID on flows that have more than one version
|
|
833
|
+
(e.g. "v2"). Makes multi-version flows obvious at a glance in the main
|
|
834
|
+
list. Hover title spells out "Version N of M". */
|
|
835
|
+
.versionBadge {
|
|
836
|
+
display: inline-block;
|
|
837
|
+
margin-left: 6px;
|
|
838
|
+
padding: 0 6px;
|
|
839
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
840
|
+
font-size: 10.5px;
|
|
841
|
+
font-weight: 600;
|
|
842
|
+
line-height: 16px;
|
|
843
|
+
color: var(--dash-text-muted);
|
|
844
|
+
background: var(--dash-border);
|
|
845
|
+
border-radius: 999px;
|
|
846
|
+
vertical-align: middle;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/* Version backlink shown under the feature name on a superseding version
|
|
850
|
+
("supersedes F-001"). Subtle — it's provenance, not a primary action. */
|
|
851
|
+
.supersedesLine {
|
|
852
|
+
display: block;
|
|
853
|
+
margin-top: 2px;
|
|
854
|
+
font-size: 11.5px;
|
|
855
|
+
color: var(--dash-text-muted);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
.supersedesLine a {
|
|
859
|
+
color: var(--dash-text-muted);
|
|
860
|
+
text-decoration: none;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
.supersedesLine a:hover { text-decoration: underline; }
|
|
864
|
+
|
|
865
|
+
/* "replaced by …" forward link on an archived row. */
|
|
866
|
+
.replacedByLink {
|
|
867
|
+
color: var(--dash-link);
|
|
868
|
+
text-decoration: none;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
.replacedByLink:hover { text-decoration: underline; }
|
|
872
|
+
|
|
873
|
+
.commentCount {
|
|
874
|
+
display: inline-flex;
|
|
875
|
+
align-items: center;
|
|
876
|
+
gap: 3px;
|
|
877
|
+
padding: 1px 7px 1px 6px;
|
|
878
|
+
font-size: 11.5px;
|
|
879
|
+
font-weight: 500;
|
|
880
|
+
color: #475569;
|
|
881
|
+
background: #f1f5f9;
|
|
882
|
+
border-radius: 999px;
|
|
883
|
+
vertical-align: middle;
|
|
884
|
+
text-decoration: none;
|
|
885
|
+
cursor: pointer;
|
|
886
|
+
transition: background 120ms, color 120ms;
|
|
887
|
+
}
|
|
888
|
+
.commentCount:hover {
|
|
889
|
+
background: #e2e8f0;
|
|
890
|
+
color: #1e293b;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
.commentCount svg {
|
|
894
|
+
opacity: 0.7;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/* `.links` cells gap their inner anchors via inline whitespace + a small
|
|
898
|
+
margin instead of `display: flex`, because a flex `<td>` stops
|
|
899
|
+
participating in the table's row-baseline alignment and pushes its
|
|
900
|
+
contents to the bottom of the row, which made every brief link appear
|
|
901
|
+
to belong to the next row. */
|
|
902
|
+
.links {
|
|
903
|
+
font-size: 12.5px;
|
|
904
|
+
white-space: nowrap;
|
|
905
|
+
}
|
|
906
|
+
.links a + a { margin-left: 12px; }
|
|
907
|
+
|
|
908
|
+
.links a {
|
|
909
|
+
color: var(--dash-text-muted);
|
|
910
|
+
text-decoration: none;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
.links a:hover {
|
|
914
|
+
color: var(--dash-text);
|
|
915
|
+
text-decoration: underline;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/* Dark mode tweaks for the semantic-color pills (status badges). The
|
|
919
|
+
light-bg/dark-text combos look bleached on a dark surface, so invert
|
|
920
|
+
to dark-bg/bright-text variants while keeping the hue. */
|
|
921
|
+
@media (prefers-color-scheme: dark) {
|
|
922
|
+
:global(:root:not([data-theme='light'])) .statusBadge { background: #1e293b; color: #cbd5e1; }
|
|
923
|
+
:global(:root:not([data-theme='light'])) .statusTodo { background: #1e293b; color: #94a3b8; }
|
|
924
|
+
:global(:root:not([data-theme='light'])) .statusValidate { background: #422006; color: #fcd34d; }
|
|
925
|
+
:global(:root:not([data-theme='light'])) .statusWip { background: #1e3a8a; color: #93c5fd; }
|
|
926
|
+
:global(:root:not([data-theme='light'])) .statusReady { background: #14532d; color: #86efac; }
|
|
927
|
+
:global(:root:not([data-theme='light'])) .error { color: #fca5a5; }
|
|
928
|
+
/* Comments pill: in light it's a slate-100 chip; in dark the bright
|
|
929
|
+
pill stands out too much — drop the chip background, keep just the
|
|
930
|
+
icon + number in a muted slate so it blends with the row. */
|
|
931
|
+
:global(:root:not([data-theme='light'])) .commentCount {
|
|
932
|
+
background: transparent;
|
|
933
|
+
color: #94a3b8;
|
|
934
|
+
}
|
|
935
|
+
:global(:root:not([data-theme='light'])) .commentCount:hover {
|
|
936
|
+
background: #1e293b;
|
|
937
|
+
color: #e2e8f0;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
:global(:root[data-theme='dark']) .statusBadge { background: #1e293b; color: #cbd5e1; }
|
|
941
|
+
:global(:root[data-theme='dark']) .statusTodo { background: #1e293b; color: #94a3b8; }
|
|
942
|
+
:global(:root[data-theme='dark']) .commentCount { background: transparent; color: #94a3b8; }
|
|
943
|
+
:global(:root[data-theme='dark']) .commentCount:hover { background: #1e293b; color: #e2e8f0; }
|
|
944
|
+
:global(:root[data-theme='dark']) .statusValidate { background: #422006; color: #fcd34d; }
|
|
945
|
+
:global(:root[data-theme='dark']) .statusWip { background: #1e3a8a; color: #93c5fd; }
|
|
946
|
+
:global(:root[data-theme='dark']) .statusReady { background: #14532d; color: #86efac; }
|
|
947
|
+
:global(:root[data-theme='dark']) .error { color: #fca5a5; }
|
|
948
|
+
|
|
949
|
+
/* On narrow viewports the dashboard reflows from "fits a desktop" to
|
|
950
|
+
"single-column phone-friendly". The page padding shrinks, the tabs
|
|
951
|
+
wrap if needed, and the DS cards collapse from their
|
|
952
|
+
3-column layout to a single column with the chip cloud sitting under
|
|
953
|
+
the title (instead of squeezed in the middle). */
|
|
954
|
+
@media (max-width: 860px) {
|
|
955
|
+
/* Stop the page being a flex ROW here — otherwise the now-visible
|
|
956
|
+
.mobileTopBar becomes a flex item, stretches full-height and squishes
|
|
957
|
+
to content width on the left. Block layout lets the sticky top bar span
|
|
958
|
+
full width on top, with the off-canvas sidebar + content stacked below. */
|
|
959
|
+
.page { display: block; }
|
|
960
|
+
/* Sidebar collapses to an off-canvas drawer; the mobile top bar
|
|
961
|
+
hosts the toggle and the current tab title. */
|
|
962
|
+
.mobileTopBar { display: flex; }
|
|
963
|
+
/* The mobile top bar already shows the module title, so hide the
|
|
964
|
+
desktop module top bar to avoid a duplicate heading. */
|
|
965
|
+
.pageTopbar { display: none; }
|
|
966
|
+
.sidebar {
|
|
967
|
+
position: fixed;
|
|
968
|
+
top: 0;
|
|
969
|
+
bottom: 0;
|
|
970
|
+
left: 0;
|
|
971
|
+
z-index: 40;
|
|
972
|
+
/* Mobile: the sidebar takes over the full screen when toggled open.
|
|
973
|
+
Top padding clears the sticky mobile top bar (which stays above it,
|
|
974
|
+
z-60, so its toggle still closes the sidebar). */
|
|
975
|
+
width: 100%;
|
|
976
|
+
max-width: 100vw;
|
|
977
|
+
background: var(--dash-bg, #fff);
|
|
978
|
+
border-right: 0;
|
|
979
|
+
padding-top: 60px;
|
|
980
|
+
transform: translateX(-100%);
|
|
981
|
+
transition: transform 0.2s ease;
|
|
982
|
+
}
|
|
983
|
+
.sidebarOpen { transform: translateX(0); }
|
|
984
|
+
}
|
|
985
|
+
@media (min-width: 861px) {
|
|
986
|
+
/* Desktop: no mobile top bar, no toggle. */
|
|
987
|
+
.sidebarToggle { display: none; }
|
|
988
|
+
/* Collapsed: hide the sidebar; the content column + full-width top bar fill. */
|
|
989
|
+
.pageCollapsed .sidebar { display: none; }
|
|
990
|
+
/* Lock the shell to the viewport so the sidebar stays full-height (its
|
|
991
|
+
bottom-pinned user pill is always visible) and only the main column
|
|
992
|
+
scrolls — instead of the whole page growing and pushing the user pill
|
|
993
|
+
below the fold. Scoped to desktop; the mobile drawer keeps body scroll. */
|
|
994
|
+
.page { height: 100vh; overflow: hidden; }
|
|
995
|
+
.main { overflow-y: auto; }
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
@media (max-width: 720px) {
|
|
999
|
+
.main { padding: 24px 16px 64px; }
|
|
1000
|
+
.title { font-size: 20px; }
|
|
1001
|
+
.subtitle { font-size: 13px; }
|
|
1002
|
+
|
|
1003
|
+
.tabs { flex-wrap: wrap; gap: 0; }
|
|
1004
|
+
.tab { padding: 8px 10px; font-size: 13px; }
|
|
1005
|
+
|
|
1006
|
+
.dsItem {
|
|
1007
|
+
grid-template-columns: 1fr;
|
|
1008
|
+
grid-template-rows: none;
|
|
1009
|
+
gap: 8px;
|
|
1010
|
+
padding: 12px 14px;
|
|
1011
|
+
}
|
|
1012
|
+
/* Reset the desktop two-row grid placements so children stack
|
|
1013
|
+
vertically in source order on narrow viewports. */
|
|
1014
|
+
.dsItem > :first-child,
|
|
1015
|
+
.dsItem .dsLink,
|
|
1016
|
+
.dsItem .dsComponents {
|
|
1017
|
+
grid-column: auto;
|
|
1018
|
+
grid-row: auto;
|
|
1019
|
+
}
|
|
1020
|
+
.dsItem > :first-child {
|
|
1021
|
+
display: flex; flex-direction: column; gap: 2px;
|
|
1022
|
+
}
|
|
1023
|
+
.dsCount { margin-left: 0; }
|
|
1024
|
+
.dsLink { justify-self: start; }
|
|
1025
|
+
.dsComponents { gap: 4px; }
|
|
1026
|
+
.dsChip { padding: 2px 8px; font-size: 11px; }
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/* Responsive list view: on narrow screens collapse the prototypes table
|
|
1030
|
+
into one stacked card per row. The header row is hidden, each <td>
|
|
1031
|
+
gets a leading "label: " prefix from a data attribute set in the
|
|
1032
|
+
markup, and the row reflows as a flex column. Avoids horizontal scroll
|
|
1033
|
+
on phones/iPads. */
|
|
1034
|
+
@media (max-width: 720px) {
|
|
1035
|
+
.table, .table thead, .table tbody, .table tr, .table td {
|
|
1036
|
+
display: block;
|
|
1037
|
+
width: 100%;
|
|
1038
|
+
}
|
|
1039
|
+
.table thead { display: none; }
|
|
1040
|
+
.table {
|
|
1041
|
+
border: 0; background: transparent;
|
|
1042
|
+
border-radius: 0;
|
|
1043
|
+
}
|
|
1044
|
+
.table tbody tr {
|
|
1045
|
+
background: var(--dash-surface);
|
|
1046
|
+
border: 1px solid var(--dash-border);
|
|
1047
|
+
border-radius: 8px;
|
|
1048
|
+
margin-bottom: 12px;
|
|
1049
|
+
padding: 8px 12px;
|
|
1050
|
+
}
|
|
1051
|
+
.table tbody tr:hover { background: var(--dash-surface); }
|
|
1052
|
+
.table tbody td {
|
|
1053
|
+
padding: 6px 0;
|
|
1054
|
+
border-bottom: none;
|
|
1055
|
+
display: flex;
|
|
1056
|
+
gap: 12px;
|
|
1057
|
+
align-items: baseline;
|
|
1058
|
+
}
|
|
1059
|
+
.table tbody td::before {
|
|
1060
|
+
content: attr(data-label);
|
|
1061
|
+
flex: 0 0 96px;
|
|
1062
|
+
font-size: 11px;
|
|
1063
|
+
text-transform: uppercase;
|
|
1064
|
+
letter-spacing: 0.04em;
|
|
1065
|
+
color: var(--dash-text-muted);
|
|
1066
|
+
font-weight: 600;
|
|
1067
|
+
}
|
|
1068
|
+
/* The feature-name cell is the headline of each card — no label, larger
|
|
1069
|
+
font, sits at top. */
|
|
1070
|
+
.table tbody td.featureNameCell {
|
|
1071
|
+
padding: 4px 0 8px;
|
|
1072
|
+
border-bottom: 1px solid var(--dash-table-divider);
|
|
1073
|
+
margin-bottom: 4px;
|
|
1074
|
+
flex-direction: column;
|
|
1075
|
+
gap: 2px;
|
|
1076
|
+
align-items: flex-start;
|
|
1077
|
+
}
|
|
1078
|
+
.table tbody td.featureNameCell::before { display: none; }
|
|
1079
|
+
.table tbody td.featureNameCell .featureLink { font-size: 15px; }
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/* Draft-features intake (Tyler spec-intake). */
|
|
1083
|
+
.draftForm {
|
|
1084
|
+
display: flex; flex-direction: column; gap: 10px;
|
|
1085
|
+
max-width: 720px;
|
|
1086
|
+
margin-bottom: 28px;
|
|
1087
|
+
padding: 16px;
|
|
1088
|
+
background: var(--dash-surface);
|
|
1089
|
+
border: 1px solid var(--dash-border);
|
|
1090
|
+
border-radius: 10px;
|
|
1091
|
+
}
|
|
1092
|
+
.draftFormTitle { margin: 0 0 4px; font-size: 15px; font-weight: 600; color: var(--dash-text); }
|
|
1093
|
+
.draftFormRow { display: flex; gap: 10px; }
|
|
1094
|
+
.draftFormRow .draftInput { flex: 1; }
|
|
1095
|
+
.draftInput,
|
|
1096
|
+
.draftTextarea {
|
|
1097
|
+
width: 100%; box-sizing: border-box;
|
|
1098
|
+
padding: 8px 10px;
|
|
1099
|
+
border: 1px solid var(--dash-border-strong);
|
|
1100
|
+
border-radius: 6px;
|
|
1101
|
+
font: inherit; font-size: 13px;
|
|
1102
|
+
background: var(--dash-surface-alt); color: var(--dash-text);
|
|
1103
|
+
}
|
|
1104
|
+
.draftTextarea { resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
1105
|
+
.draftSubmit {
|
|
1106
|
+
align-self: flex-start;
|
|
1107
|
+
padding: 8px 16px;
|
|
1108
|
+
background: var(--dash-table-head-bg); color: var(--dash-table-head-text);
|
|
1109
|
+
border: 0; border-radius: 6px;
|
|
1110
|
+
font: inherit; font-size: 13px; font-weight: 600;
|
|
1111
|
+
cursor: pointer;
|
|
1112
|
+
}
|
|
1113
|
+
.draftSubmit:disabled { opacity: 0.6; cursor: default; }
|
|
1114
|
+
.draftSpec {
|
|
1115
|
+
margin: 8px 0 0;
|
|
1116
|
+
padding: 10px 12px;
|
|
1117
|
+
background: var(--dash-code-bg);
|
|
1118
|
+
border-radius: 6px;
|
|
1119
|
+
font-size: 12px; line-height: 1.5;
|
|
1120
|
+
white-space: pre-wrap; word-break: break-word;
|
|
1121
|
+
color: var(--dash-text);
|
|
1122
|
+
max-height: 220px; overflow-y: auto;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
/* Draft markdown editor — Write / Preview toggle. */
|
|
1126
|
+
.draftEditor {
|
|
1127
|
+
border: 1px solid var(--dash-border-strong);
|
|
1128
|
+
border-radius: 6px;
|
|
1129
|
+
overflow: hidden;
|
|
1130
|
+
background: var(--dash-surface-alt);
|
|
1131
|
+
}
|
|
1132
|
+
.draftEditorTabs {
|
|
1133
|
+
display: flex; align-items: center; gap: 2px;
|
|
1134
|
+
padding: 6px 6px 0;
|
|
1135
|
+
border-bottom: 1px solid var(--dash-border);
|
|
1136
|
+
}
|
|
1137
|
+
.draftEditorTab {
|
|
1138
|
+
padding: 6px 12px;
|
|
1139
|
+
background: transparent; border: 0;
|
|
1140
|
+
border-radius: 6px 6px 0 0;
|
|
1141
|
+
color: var(--dash-text-muted);
|
|
1142
|
+
font: inherit; font-size: 12px; font-weight: 500;
|
|
1143
|
+
cursor: pointer;
|
|
1144
|
+
}
|
|
1145
|
+
.draftEditorTab:hover { color: var(--dash-text); }
|
|
1146
|
+
.draftEditorTabActive,
|
|
1147
|
+
.draftEditorTabActive:hover {
|
|
1148
|
+
background: var(--dash-surface);
|
|
1149
|
+
color: var(--dash-text);
|
|
1150
|
+
}
|
|
1151
|
+
.draftEditorHint {
|
|
1152
|
+
margin-left: auto; padding-right: 8px;
|
|
1153
|
+
font-size: 11px; color: var(--dash-text-muted);
|
|
1154
|
+
}
|
|
1155
|
+
.draftEditor .draftTextarea {
|
|
1156
|
+
border: 0; border-radius: 0; background: var(--dash-surface);
|
|
1157
|
+
}
|
|
1158
|
+
.draftPreview,
|
|
1159
|
+
.draftSpec {
|
|
1160
|
+
padding: 14px 16px;
|
|
1161
|
+
background: var(--dash-surface);
|
|
1162
|
+
color: var(--dash-text);
|
|
1163
|
+
font-size: 14px; line-height: 1.6;
|
|
1164
|
+
}
|
|
1165
|
+
.draftSpec {
|
|
1166
|
+
margin: 8px 0 0;
|
|
1167
|
+
border: 1px solid var(--dash-border);
|
|
1168
|
+
border-radius: 6px;
|
|
1169
|
+
max-height: 360px; overflow-y: auto;
|
|
1170
|
+
}
|
|
1171
|
+
.draftPreview { min-height: 200px; max-height: 420px; overflow-y: auto; }
|
|
1172
|
+
.draftPreview :first-child,
|
|
1173
|
+
.draftSpec :first-child { margin-top: 0; }
|
|
1174
|
+
.draftPreview :last-child,
|
|
1175
|
+
.draftSpec :last-child { margin-bottom: 0; }
|
|
1176
|
+
.draftPreview h1, .draftSpec h1 { font-size: 20px; }
|
|
1177
|
+
.draftPreview h2, .draftSpec h2 { font-size: 16px; margin-top: 18px; }
|
|
1178
|
+
.draftPreview h3, .draftSpec h3 { font-size: 14px; margin-top: 14px; }
|
|
1179
|
+
.draftPreview code, .draftSpec code {
|
|
1180
|
+
background: var(--dash-code-bg); padding: 1px 5px; border-radius: 4px;
|
|
1181
|
+
font-size: 0.9em;
|
|
1182
|
+
}
|
|
1183
|
+
.draftPreview pre, .draftSpec pre {
|
|
1184
|
+
background: var(--dash-code-bg); padding: 10px 12px; border-radius: 6px;
|
|
1185
|
+
overflow-x: auto;
|
|
1186
|
+
}
|
|
1187
|
+
.draftPreview a, .draftSpec a { color: var(--dash-link); }
|
|
1188
|
+
|
|
1189
|
+
/* Lean contenteditable rich-text editor (draft specs). */
|
|
1190
|
+
.rte {
|
|
1191
|
+
border: 1px solid var(--dash-border-strong);
|
|
1192
|
+
border-radius: 6px;
|
|
1193
|
+
overflow: hidden;
|
|
1194
|
+
background: var(--dash-surface);
|
|
1195
|
+
}
|
|
1196
|
+
.rteToolbar {
|
|
1197
|
+
display: flex; flex-wrap: wrap; gap: 2px;
|
|
1198
|
+
padding: 6px;
|
|
1199
|
+
border-bottom: 1px solid var(--dash-border);
|
|
1200
|
+
background: var(--dash-surface-alt);
|
|
1201
|
+
}
|
|
1202
|
+
.rteBtn {
|
|
1203
|
+
min-width: 28px; padding: 4px 8px;
|
|
1204
|
+
background: transparent; border: 1px solid transparent;
|
|
1205
|
+
border-radius: 5px;
|
|
1206
|
+
color: var(--dash-text-muted);
|
|
1207
|
+
font: inherit; font-size: 12px; font-weight: 600;
|
|
1208
|
+
cursor: pointer;
|
|
1209
|
+
}
|
|
1210
|
+
.rteBtn:hover { background: var(--dash-surface); color: var(--dash-text); border-color: var(--dash-border); }
|
|
1211
|
+
.rteSurface {
|
|
1212
|
+
padding: 14px 16px;
|
|
1213
|
+
min-height: 240px; max-height: 480px; overflow-y: auto;
|
|
1214
|
+
font-size: 14px; line-height: 1.6;
|
|
1215
|
+
color: var(--dash-text);
|
|
1216
|
+
outline: none;
|
|
1217
|
+
}
|
|
1218
|
+
.rteSurface:empty::before {
|
|
1219
|
+
content: attr(data-placeholder);
|
|
1220
|
+
color: var(--dash-text-muted);
|
|
1221
|
+
}
|
|
1222
|
+
.rteSurface :first-child { margin-top: 0; }
|
|
1223
|
+
.rteSurface h2 { font-size: 16px; margin: 18px 0 6px; }
|
|
1224
|
+
.rteSurface h3 { font-size: 14px; margin: 14px 0 6px; }
|
|
1225
|
+
.rteSurface a { color: var(--dash-link); }
|
|
1226
|
+
.rteSurface ul, .rteSurface ol { padding-left: 22px; }
|
|
1227
|
+
|
|
1228
|
+
/* Full-screen draft composer. */
|
|
1229
|
+
.draftOverlay {
|
|
1230
|
+
position: fixed; inset: 0; z-index: 1000;
|
|
1231
|
+
background: var(--dash-bg);
|
|
1232
|
+
display: flex; flex-direction: column;
|
|
1233
|
+
padding: 20px 24px 24px;
|
|
1234
|
+
}
|
|
1235
|
+
.draftFull {
|
|
1236
|
+
display: flex; flex-direction: column; gap: 12px;
|
|
1237
|
+
width: 100%; max-width: 1000px; height: 100%;
|
|
1238
|
+
margin: 0 auto;
|
|
1239
|
+
}
|
|
1240
|
+
.draftFullHead {
|
|
1241
|
+
display: flex; align-items: center; gap: 10px;
|
|
1242
|
+
flex-wrap: wrap;
|
|
1243
|
+
}
|
|
1244
|
+
.draftFullFid {
|
|
1245
|
+
width: 110px;
|
|
1246
|
+
padding: 8px 10px;
|
|
1247
|
+
border: 1px solid var(--dash-border-strong);
|
|
1248
|
+
border-radius: 6px;
|
|
1249
|
+
font: inherit; font-size: 14px; font-weight: 600;
|
|
1250
|
+
background: var(--dash-surface-alt); color: var(--dash-text);
|
|
1251
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
1252
|
+
}
|
|
1253
|
+
.draftFullName {
|
|
1254
|
+
flex: 1; min-width: 200px;
|
|
1255
|
+
padding: 8px 10px;
|
|
1256
|
+
border: 1px solid var(--dash-border-strong);
|
|
1257
|
+
border-radius: 6px;
|
|
1258
|
+
font: inherit; font-size: 16px; font-weight: 600;
|
|
1259
|
+
background: var(--dash-surface-alt); color: var(--dash-text);
|
|
1260
|
+
}
|
|
1261
|
+
.draftFullActions { display: flex; gap: 8px; margin-left: auto; }
|
|
1262
|
+
.draftCancel {
|
|
1263
|
+
padding: 8px 16px;
|
|
1264
|
+
background: transparent; color: var(--dash-text-muted);
|
|
1265
|
+
border: 1px solid var(--dash-border); border-radius: 6px;
|
|
1266
|
+
font: inherit; font-size: 13px; cursor: pointer;
|
|
1267
|
+
}
|
|
1268
|
+
.draftCancel:hover { color: var(--dash-text); }
|
|
1269
|
+
.draftFullEditor { flex: 1; min-height: 0; display: flex; }
|
|
1270
|
+
.draftFullEditor .rte { flex: 1; display: flex; flex-direction: column; min-height: 0; }
|
|
1271
|
+
.draftFullEditor .rteSurface { flex: 1; max-height: none; }
|
|
1272
|
+
|
|
1273
|
+
/* Loading spinner — shown while the resolution queue (and other async
|
|
1274
|
+
dashboard views) fetch. Ring in the border colour with one arc in the
|
|
1275
|
+
muted text colour. */
|
|
1276
|
+
.spinner {
|
|
1277
|
+
width: 26px;
|
|
1278
|
+
height: 26px;
|
|
1279
|
+
border: 3px solid var(--dash-border);
|
|
1280
|
+
border-top-color: var(--dash-text-muted);
|
|
1281
|
+
border-radius: 50%;
|
|
1282
|
+
animation: dash-spin 0.8s linear infinite;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
@keyframes dash-spin {
|
|
1286
|
+
to { transform: rotate(360deg); }
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
.loadingRow {
|
|
1290
|
+
display: flex;
|
|
1291
|
+
flex-direction: column;
|
|
1292
|
+
align-items: center;
|
|
1293
|
+
justify-content: center;
|
|
1294
|
+
gap: 12px;
|
|
1295
|
+
padding: 48px 0;
|
|
1296
|
+
color: var(--dash-text-muted);
|
|
1297
|
+
font-size: 14px;
|
|
1298
|
+
text-align: center;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
/* Centered, boxed empty state (e.g. "Queue clear"). */
|
|
1302
|
+
.emptyState {
|
|
1303
|
+
display: flex;
|
|
1304
|
+
flex-direction: column;
|
|
1305
|
+
align-items: center;
|
|
1306
|
+
justify-content: center;
|
|
1307
|
+
text-align: center;
|
|
1308
|
+
gap: 10px;
|
|
1309
|
+
padding: 56px 24px;
|
|
1310
|
+
margin-top: 8px;
|
|
1311
|
+
border: 1px solid var(--dash-border);
|
|
1312
|
+
border-radius: 12px;
|
|
1313
|
+
color: var(--dash-text-muted);
|
|
1314
|
+
font-size: 14px;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
.emptyStateIcon { color: var(--dash-text-muted); opacity: 0.6; }
|
|
1318
|
+
|