@felloinnovations/brand-kit 0.5.1 → 0.5.3
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/AGENTS.md +29 -9
- package/CHANGELOG.md +86 -0
- package/README.md +10 -4
- package/design-system/01-how-claude-decides.md +5 -3
- package/design-system/03-colour.md +19 -1
- package/design-system/05-space-and-layout.md +2 -1
- package/design-system/08-choosing-components.md +20 -4
- package/design-system/09-charts.md +6 -3
- package/design-system/11-page-recipes.md +40 -1
- package/design-system/13-motion.md +1 -1
- package/design-system/15-ai-experiences.md +11 -0
- package/design-system/17-decisions.md +41 -0
- package/design-system/components/AppShell/README.md +89 -21
- package/design-system/components/AppShell/preview.html +148 -3
- package/design-system/components/AppShell/style.css +47 -0
- package/design-system/components/Avatar/README.md +6 -3
- package/design-system/components/Avatar/preview.html +2 -1
- package/design-system/components/Avatar/style.css +3 -0
- package/design-system/components/Banner/README.md +9 -5
- package/design-system/components/Banner/preview.html +9 -0
- package/design-system/components/Banner/style.css +3 -0
- package/design-system/components/Button/README.md +29 -2
- package/design-system/components/Button/preview.html +33 -1
- package/design-system/components/Button/style.css +21 -0
- package/design-system/components/Calendar/README.md +148 -0
- package/design-system/components/Calendar/preview.html +449 -0
- package/design-system/components/Calendar/style.css +61 -0
- package/design-system/components/Card/README.md +13 -0
- package/design-system/components/Card/preview.html +23 -0
- package/design-system/components/Card/style.css +19 -0
- package/design-system/components/ChatCard/README.md +74 -3
- package/design-system/components/ChatCard/preview.html +126 -25
- package/design-system/components/ChatCard/style.css +6 -0
- package/design-system/components/Chip/README.md +16 -7
- package/design-system/components/Chip/preview.html +25 -1
- package/design-system/components/Chip/style.css +5 -0
- package/design-system/components/Combobox/README.md +10 -4
- package/design-system/components/Combobox/preview.html +19 -4
- package/design-system/components/Combobox/style.css +7 -1
- package/design-system/components/CommentThread/README.md +138 -0
- package/design-system/components/CommentThread/preview.html +134 -0
- package/design-system/components/CommentThread/style.css +41 -0
- package/design-system/components/DateInput/README.md +38 -4
- package/design-system/components/DateInput/preview.html +50 -2
- package/design-system/components/DateInput/style.css +6 -0
- package/design-system/components/Dialog/README.md +26 -3
- package/design-system/components/Dialog/preview.html +80 -0
- package/design-system/components/Dialog/style.css +28 -3
- package/design-system/components/EmptyState/README.md +1 -1
- package/design-system/components/Fold/README.md +21 -0
- package/design-system/components/Fold/preview.html +44 -1
- package/design-system/components/Fold/style.css +30 -0
- package/design-system/components/InlineEdit/README.md +116 -0
- package/design-system/components/InlineEdit/preview.html +135 -0
- package/design-system/components/InlineEdit/style.css +34 -0
- package/design-system/components/Input/README.md +20 -1
- package/design-system/components/Input/preview.html +24 -0
- package/design-system/components/Input/style.css +13 -1
- package/design-system/components/KanbanBoard/README.md +68 -4
- package/design-system/components/KanbanBoard/preview.html +129 -0
- package/design-system/components/KanbanBoard/style.css +31 -0
- package/design-system/components/PlanTimeline/README.md +144 -0
- package/design-system/components/PlanTimeline/preview.html +109 -0
- package/design-system/components/PlanTimeline/style.css +64 -0
- package/design-system/components/README.md +11 -6
- package/design-system/components/Skeleton/README.md +66 -2
- package/design-system/components/Skeleton/preview.html +83 -1
- package/design-system/components/Starter/README.md +1 -1
- package/design-system/components/Starter/preview.html +1 -1
- package/design-system/components/Stepper/README.md +81 -5
- package/design-system/components/Stepper/preview.html +64 -1
- package/design-system/components/Stepper/style.css +29 -0
- package/design-system/components/StreakCalendar/README.md +1 -0
- package/design-system/components/Table/README.md +7 -3
- package/design-system/components/Table/preview.html +15 -0
- package/design-system/components/Table/style.css +2 -0
- package/design-system/components/TextDiff/README.md +96 -0
- package/design-system/components/TextDiff/preview.html +42 -0
- package/design-system/components/TextDiff/style.css +21 -0
- package/design-system/components/Timeline/README.md +3 -1
- package/design-system/components/TrendLine/README.md +38 -7
- package/design-system/components/TrendLine/preview.html +140 -3
- package/design-system/components/TrendLine/style.css +12 -2
- package/design-system/components/bundle.css +483 -7
- package/design-system/tokens.json +15 -1
- package/dist/fello-core.css +2 -2
- package/dist/fello.css +2 -2
- package/dist/fello.scoped.css +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/scoped.js +2 -2
- package/dist/tokens.d.ts +8 -0
- package/dist/tokens.js +8 -0
- package/package.json +7 -2
- package/scripts/check-drift.mjs +325 -0
- package/scripts/token-model.mjs +2 -1
- package/tokens/_dark.css +2 -0
- package/tokens/_product.css +2 -0
- package/tokens/palettes.ts +1 -1
- package/tokens/shadcn-compat.css +1 -1
- package/tokens/shadcn-compat.hsl.css +1 -1
- package/tokens/tokens.css +14 -1
- package/tokens/tokens.md +53 -46
- package/tokens/tokens.root.css +14 -1
- package/tokens/tokens.ts +9 -1
|
@@ -5,7 +5,8 @@ nav to a drawer.
|
|
|
5
5
|
## Use it when
|
|
6
6
|
|
|
7
7
|
- The product's work happens on pages a person moves between: a pipeline, a report, a list, settings.
|
|
8
|
-
- There are more destinations than fit a flat list, and they fall into a few groups (Reports, Settings)
|
|
8
|
+
- There are more destinations than fit a flat list, and they fall into a few groups (Reports, Settings), or a page has
|
|
9
|
+
a few pages of its own (Onboarding: Board, Stalled, Graduated) that fold under it as a section.
|
|
9
10
|
- Every page needs the same place for its name, its acts and a way to fold the nav away.
|
|
10
11
|
- A team member needs to see who they are signed in as, pinned at the foot of the nav.
|
|
11
12
|
|
|
@@ -33,11 +34,24 @@ nav to a drawer.
|
|
|
33
34
|
- Rows (`__rows`, `__row`): 28 tall (`--ws-control-h-sm`, 40 on touch), 2 apart, 10 px sides, 6 px corners. A 20 px
|
|
34
35
|
icon box (the 16 px icon inside it), 4 px, then the name (`__name`) in 13 / 18 medium body grey, truncated with
|
|
35
36
|
its full words in `title`. Rows are real links (`<a href>`).
|
|
37
|
+
- Section (`__section`): a row that folds its pages open in place (North's "Sidebar sections open / close"). A
|
|
38
|
+
`<details>` whose `<summary>` wears `__row`: the icon, the name, a 16 px chevron at the end (`__chevron`, in the
|
|
39
|
+
resting mark grey) that turns down as it opens. Its pages are a `__rows` list one level in (the icon box and its
|
|
40
|
+
gap, 24), so their words start on the section's words, 46 from the nav's side; they need no icon of their own.
|
|
41
|
+
One level only: a section never holds a section.
|
|
42
|
+
- Folded section (`__fly` and `__panel`): in the 64 px rail a section cannot open, so the `<details>` leaves and a
|
|
43
|
+
`<button>` row (`__row __fly`, the section's icon, its name in `title`) takes its place, opening the section's
|
|
44
|
+
pages as a `Menu` panel beside the rail (`ws-menu __panel`, a native popover): its top on the button's, 8 past the
|
|
45
|
+
rail's edge, flipped up when the room below runs out. The panel names the section in its eyebrow (`__eyebrow`, on
|
|
46
|
+
the words a `ws-menu-item` row starts on), then its pages as `ws-menu-item` links. Both are in the markup; the nav
|
|
47
|
+
shows one or the other.
|
|
36
48
|
- Count (`__count`): Seth's navy pill, 18 tall, at least 18 wide, 11 px semibold figures in the act fill's word
|
|
37
49
|
colour (`--ws-on-act`).
|
|
38
50
|
- The person (`__foot`): pinned at the foot under a full-width 1 px rule, never scrolled away, 12 above and 6 below,
|
|
39
51
|
on the rows' edges (22 in at the left, 18 at the right: the groups' 12 / 8 and a row's 10). A 28 px avatar, 10 px,
|
|
40
|
-
then the name 13 / 16 medium navy (`__foot-name`) over the role in `ws-muted`.
|
|
52
|
+
then the name 13 / 16 medium navy (`__foot-name`) over the role in `ws-muted`. When the person opens their
|
|
53
|
+
account menu (a profile, "View as", sign out), the avatar and words sit in a button (`__me`) that opens a `Menu`:
|
|
54
|
+
a row's 10 px sides and 6 px corners and its wash, the foot's height and the avatar's 22 px edge unchanged.
|
|
41
55
|
- **Stage** (`__stage`): `--ws-glass` with a 4 px blur (`--ws-glass-blur`), a 1 px `--ws-edge-stage` edge, 16 px corners
|
|
42
56
|
(`--ws-radius-panel`), 13 from the window's top (`--ws-stage-inset-top`), 12 from its right and bottom
|
|
43
57
|
(`--ws-stage-inset`), no gap to the nav (the nav's own 8 px is the gap). `--wash` gives it the Fia frames' cream
|
|
@@ -62,7 +76,8 @@ nav to a drawer.
|
|
|
62
76
|
the folded rail).
|
|
63
77
|
- **Touch**: the search box is 40 tall and its words 16, so a phone or tablet never zooms into it on focus.
|
|
64
78
|
- In fello.css: `.ws-app-shell` (+ `--folded`, `--wash`), `__nav`, `__head`, `__wordmark`, `__groups`, `__group`,
|
|
65
|
-
`__eyebrow`, `__rows`, `__row` (+ `--current`), `__name`, `__count`, `
|
|
79
|
+
`__eyebrow`, `__rows`, `__row` (+ `--current`), `__name`, `__count`, `__section`, `__chevron`, `__fly`, `__panel`,
|
|
80
|
+
`__foot`, `__me`, `__foot-words`, `__foot-name`,
|
|
66
81
|
`__stage`, `__bar` (+ `--scrolled`), `__title`, `__acts`, `__search`, `__menu`, `__body` (+ `--wide`); the 0.4
|
|
67
82
|
rail markup `__rail` and `__mark` (+ `--current`, `--wordmark`) still draws, and so does a 0.4 nav (wordmark, group
|
|
68
83
|
names, rows and person straight in `__nav`, no `__head` or `__groups`): the nav itself scrolls then, with the
|
|
@@ -73,7 +88,7 @@ nav to a drawer.
|
|
|
73
88
|
| Variant | Width | Shows |
|
|
74
89
|
| --- | --- | --- |
|
|
75
90
|
| Open (default) | 232 px | The head, grouped rows with names and counts, the person |
|
|
76
|
-
| Folded (`--folded`) | 64 px | The same nav: 40 px rows of icons, the current one on its pill; names faded (still read aloud), counts gone, each row's name and count in its `title` (a pointer's tooltip, a screen reader's description), group names and rules gone, the avatar centred |
|
|
91
|
+
| Folded (`--folded`) | 64 px | The same nav: 40 px rows of icons, the current one on its pill; names faded (still read aloud), counts gone, each row's name and count in its `title` (a pointer's tooltip, a screen reader's description), group names and rules gone, the avatar centred; each section a button that opens its pages in a panel beside the rail |
|
|
77
92
|
| Phone (under 60rem of the shell) | none | The bar's menu button opens the same nav in a drawer at the drawer's width; the stage goes edge to edge, 16 px sides |
|
|
78
93
|
|
|
79
94
|
| Body | Content width | Use |
|
|
@@ -95,6 +110,13 @@ nav to a drawer.
|
|
|
95
110
|
- **Needs you**: the navy count pill. A bare number, its meaning in `.ws-sr` words inside it ("7 waiting"), never
|
|
96
111
|
`aria-label` on the span (ARIA does not let a plain span carry a label).
|
|
97
112
|
- **Long name**: truncates on one line, the full name in `title`.
|
|
113
|
+
- **Section open / closed**: the chevron points right when closed and down when open, turning on the fold spring as
|
|
114
|
+
the pages fold in (the house's fold on every kit `<details>`). Whether a section starts open is the app's: open the
|
|
115
|
+
one that holds the current page.
|
|
116
|
+
- **Closed on the current page**: a closed section that holds the current page wears the current pill itself (the
|
|
117
|
+
white pill, its icon salmon, its words at salmon's AA step), so the page is never lost behind a fold; opened, the
|
|
118
|
+
page takes the pill back. Folded, the section's button does the same, and the panel marks the page as the current
|
|
119
|
+
menu row.
|
|
98
120
|
- **Folded**: only the icons show, so every row carries its name (and its count, "Pipeline, 7 waiting") in `title`;
|
|
99
121
|
in React, a `Tooltip` on hover and focus.
|
|
100
122
|
- **Many rows**: the groups scroll; the head and the person stay. In 0.4 markup (no `__groups`) the nav scrolls and the
|
|
@@ -113,7 +135,11 @@ nav to a drawer.
|
|
|
113
135
|
- Keyboard: rows are links in document order; the fold button carries `aria-expanded` and toggles `--folded` on the
|
|
114
136
|
shell. The phone's drawer is a `Dialog` (`ws-dialog--drawer`) holding the same `__nav` markup; the head's button
|
|
115
137
|
becomes its close.
|
|
116
|
-
-
|
|
138
|
+
- Sections: the `<summary>` is the disclosure (Enter or Space opens and closes it, its open state read aloud). Folded,
|
|
139
|
+
the section's button opens its panel (Enter or Space, or a click; the browser tells a screen reader it is expanded),
|
|
140
|
+
Tab goes on into the panel's pages, and Escape or a click away closes it, the focus back on the button. Arrow keys
|
|
141
|
+
between the panel's rows are the app's, as on every `Menu`.
|
|
142
|
+
- Pointer: hover only tints. Nothing opens on hover, a folded section's panel included.
|
|
117
143
|
- Motion (North): the fold trades width over `--ws-duration-shift` on `--ws-ease-shift`; the rows, the current pill
|
|
118
144
|
and the fold button ride the nav's edge as it moves (nothing jumps to the middle and back), names fade over
|
|
119
145
|
`--ws-duration-hover`, washes ease in over 200 ms, the bar's edge over `--ws-duration-settle`. The current pill
|
|
@@ -137,7 +163,10 @@ nav to a drawer.
|
|
|
137
163
|
|
|
138
164
|
- A group's name says what kind of page it holds ("Reports"), sentence case, two words at most. The first group can
|
|
139
165
|
go without one.
|
|
140
|
-
- A row's name is the page's own name, sentence case.
|
|
166
|
+
- A row's name is the page's own name, sentence case. A section's name is the page its pages belong to
|
|
167
|
+
("Onboarding"), and its pages say only what they add ("Board", "Stalled"), never the section's name again.
|
|
168
|
+
- A way out of the app (a full-screen app inside another: "Back to Turbo") is the last group's one row, a back chevron
|
|
169
|
+
for its icon, above the person; never in the foot and never at the top.
|
|
141
170
|
- The bar names the page once, exactly as its row does; no second heading repeats it in the body.
|
|
142
171
|
- No em dashes; a count is a bare number.
|
|
143
172
|
|
|
@@ -146,9 +175,9 @@ nav to a drawer.
|
|
|
146
175
|
| Name | Type | Meaning |
|
|
147
176
|
| --- | --- | --- |
|
|
148
177
|
| `groups` | `{ name?: string; items: NavRow[] }[]` | The nav's groups, in order |
|
|
149
|
-
| `NavRow` | `{ label, icon, href, current?, count
|
|
150
|
-
| `person` | `{ name, role, initials }` | The foot |
|
|
151
|
-
| `folded` | `boolean` | The 64 px rail |
|
|
178
|
+
| `NavRow` | `{ label, icon, href, current?, count?, items?: NavRow[] }` | One row: its words, its 16 px icon, whether it is the current page, its count; with `items` it is a section (its own `href` unused), one level only |
|
|
179
|
+
| `person` | `{ name, role, initials, menu?: ReactNode }` | The foot; with `menu`, the person is a button that opens it |
|
|
180
|
+
| `folded` | `boolean` | The 64 px rail (the app keeps the person's choice, in storage, and sets `--folded`) |
|
|
152
181
|
| `bar` | `ReactNode` | The bar's content: a title, a `Breadcrumb` or a `PageBar`, and its acts |
|
|
153
182
|
| `wash` | `boolean` | The cream stage instead of glass |
|
|
154
183
|
|
|
@@ -157,15 +186,20 @@ nav to a drawer.
|
|
|
157
186
|
- **One navy button per page**: the bar's primary is the page's one; nothing in the body adds a second.
|
|
158
187
|
- **The body is the page**: it brings the 32 px gutter; a page inside never adds its own padding or a `.ws-page`.
|
|
159
188
|
- **The person never scrolls away** and the head never moves: only the groups scroll (K11).
|
|
160
|
-
- **Real links, `aria-current="page"`**: never a clickable `<li>`, never colour alone.
|
|
189
|
+
- **Real links, `aria-current="page"`**: never a clickable `<li>`, never colour alone. A section is a `<summary>` (and,
|
|
190
|
+
folded, a `<button>`), never a link that also folds.
|
|
191
|
+
- **Sections or groups, by what they hold**: a group names a kind of page ("Reports") and is always open; a section is
|
|
192
|
+
one page's own pages and folds. A group can hold sections; a section never holds a section.
|
|
161
193
|
- **Seth's tokens only**: no raw colour, size or corner; dark comes from the tokens (D12).
|
|
162
194
|
|
|
163
195
|
## Source
|
|
164
196
|
|
|
165
197
|
**Derived from**: Seth's North app (`style1.css` 22 to 112, 531 to 560; the 232 px sidebar, its 28 px rows, rules,
|
|
166
|
-
navy count, white current pill, the 64 px rail, the glass stage and the frame
|
|
167
|
-
32 px gutter (D4), and Revenue OS faults K1, K2, K3, K11 and K12.
|
|
168
|
-
|
|
198
|
+
navy count, white current pill, the 64 px rail, the glass stage and the frame; "Sidebar sections open / close" on the
|
|
199
|
+
fold spring), the Fia frames' 56 px thread header and 32 px gutter (D4), and Revenue OS faults K1, K2, K3, K11 and K12.
|
|
200
|
+
Sections, the folded section's panel, the person's menu and the way out come from Customer Value's own rail
|
|
201
|
+
(`CvRail`, Hemanth, 10 Nov 2026), so the app can drop it. **Precedent**: kit 0.4's AppShell, upgraded in place 4 Nov
|
|
202
|
+
2026 (the 0.4 markup still draws).
|
|
169
203
|
|
|
170
204
|
## Markup
|
|
171
205
|
|
|
@@ -195,15 +229,48 @@ navy count, white current pill, the 64 px rail, the glass stage and the frame),
|
|
|
195
229
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 13V8M8 13V3M13 13V9.5"/></svg>
|
|
196
230
|
<span class="ws-app-shell__name">Performance</span>
|
|
197
231
|
</a></li>
|
|
232
|
+
<!-- A section: its pages fold open in place; folded, the button opens them beside the rail -->
|
|
233
|
+
<li>
|
|
234
|
+
<details class="ws-app-shell__section" open>
|
|
235
|
+
<summary class="ws-app-shell__row">
|
|
236
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="12" height="10" rx="1.5"/><path d="M2 6.5h12"/></svg>
|
|
237
|
+
<span class="ws-app-shell__name">Onboarding</span>
|
|
238
|
+
<svg class="ws-app-shell__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.6 5.2L9.4 8l-2.8 2.8"/></svg>
|
|
239
|
+
</summary>
|
|
240
|
+
<ul class="ws-app-shell__rows">
|
|
241
|
+
<li><a href="/onboarding" class="ws-app-shell__row"><span class="ws-app-shell__name">Board</span></a></li>
|
|
242
|
+
<li><a href="/onboarding/stalled" class="ws-app-shell__row"><span class="ws-app-shell__name">Stalled</span></a></li>
|
|
243
|
+
</ul>
|
|
244
|
+
</details>
|
|
245
|
+
<button type="button" class="ws-app-shell__row ws-app-shell__fly" popovertarget="nav-onboarding" title="Onboarding">
|
|
246
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="12" height="10" rx="1.5"/><path d="M2 6.5h12"/></svg>
|
|
247
|
+
<span class="ws-app-shell__name">Onboarding</span>
|
|
248
|
+
</button>
|
|
249
|
+
<div id="nav-onboarding" class="ws-menu ws-app-shell__panel" popover aria-label="Onboarding">
|
|
250
|
+
<p class="ws-app-shell__eyebrow">Onboarding</p>
|
|
251
|
+
<ul>
|
|
252
|
+
<li><a href="/onboarding" class="ws-menu-item">Board</a></li>
|
|
253
|
+
<li><a href="/onboarding/stalled" class="ws-menu-item">Stalled</a></li>
|
|
254
|
+
</ul>
|
|
255
|
+
</div>
|
|
256
|
+
</li>
|
|
198
257
|
</ul>
|
|
199
258
|
</div>
|
|
200
259
|
</div>
|
|
201
260
|
<div class="ws-app-shell__foot">
|
|
202
|
-
|
|
203
|
-
<
|
|
204
|
-
<span class="ws-
|
|
205
|
-
<span class="ws-
|
|
206
|
-
|
|
261
|
+
<!-- A static person is the avatar and words straight in the foot; with an account menu, a button: -->
|
|
262
|
+
<button type="button" class="ws-app-shell__me" popovertarget="nav-me" aria-haspopup="menu">
|
|
263
|
+
<span class="ws-avatar ws-avatar--sm ws-avatar--plain">PN</span>
|
|
264
|
+
<span class="ws-app-shell__foot-words">
|
|
265
|
+
<span class="ws-app-shell__foot-name">Priya Nair</span>
|
|
266
|
+
<span class="ws-muted">Team lead</span>
|
|
267
|
+
</span>
|
|
268
|
+
</button>
|
|
269
|
+
<div id="nav-me" class="ws-menu" popover role="menu" aria-label="Your account">
|
|
270
|
+
<a href="/profile" class="ws-menu-item" role="menuitem">Your profile</a>
|
|
271
|
+
<div class="ws-menu-line" role="separator"></div>
|
|
272
|
+
<button type="button" class="ws-menu-item" role="menuitem">Sign out</button>
|
|
273
|
+
</div>
|
|
207
274
|
</div>
|
|
208
275
|
</nav>
|
|
209
276
|
<div class="ws-app-shell__stage">
|
|
@@ -229,12 +296,13 @@ navy count, white current pill, the 64 px rail, the glass stage and the frame),
|
|
|
229
296
|
|
|
230
297
|
## Styles
|
|
231
298
|
|
|
232
|
-
`AppShell/style.css`, with `IconButton`, `Button` and `Avatar` for the bar's acts and the person
|
|
233
|
-
house's). Tokens: `--ws-frame`,
|
|
299
|
+
`AppShell/style.css`, with `IconButton`, `Button` and `Avatar` for the bar's acts and the person, and `Menu` for a
|
|
300
|
+
folded section's panel and the person's menu (`ws-muted` is the house's). Tokens: `--ws-frame`,
|
|
234
301
|
`--ws-glass`, `--ws-glass-blur`, `--ws-stage-wash`, `--ws-edge-stage`, `--ws-stage-inset-top`, `--ws-stage-inset`,
|
|
235
302
|
`--ws-sidebar-width`, `--ws-header-h`, `--ws-control-h-sm`, `--ws-touch`, `--ws-field`, `--ws-field-ink`,
|
|
236
303
|
`--ws-focus-width`, `--ws-focus-color`, `--ws-focus-offset`, `--ws-radius-row`, `--ws-radius-control`,
|
|
237
304
|
`--ws-radius-panel`, `--ws-hover`, `--ws-hover-strong`, `--ws-float-rim`, `--ws-shade`, `--ws-ink`,
|
|
238
305
|
`--color-surface`, `--color-text-brand-strong`, `--ws-chart-under-half`, `--ws-on-act`, `--ws-edge-card`,
|
|
239
|
-
`--ws-hairline`, `--ws-chart-card-w`, `--ws-gap-component`, `--ws-gap-group`, `--ws-text-value`, `--ws-duration-shift`, `--ws-ease-shift
|
|
306
|
+
`--ws-hairline`, `--ws-chart-card-w`, `--ws-gap-component`, `--ws-gap-group`, `--ws-text-value`, `--ws-duration-shift`, `--ws-ease-shift`, `--ws-mark-muted`, `--ws-icon-lg`, `--ws-spring-fold`,
|
|
307
|
+
`--ws-spring-fold-duration`. The shell sets `--ws-gutter` to `--space-xl` (32)
|
|
240
308
|
for everything inside it.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- @dsCard group="Navigation" height=
|
|
1
|
+
<!-- @dsCard group="Navigation" height=3552 -->
|
|
2
2
|
<!doctype html>
|
|
3
3
|
<html>
|
|
4
4
|
<head>
|
|
@@ -116,6 +116,148 @@
|
|
|
116
116
|
</div></div>
|
|
117
117
|
</div>
|
|
118
118
|
|
|
119
|
+
<div class="ws-stack">
|
|
120
|
+
<span class="ws-muted">Sections: a row that folds its pages open in place, the chevron turning, the pages one level in. A closed section holding the current page wears its pill. A way out of the app is the last group’s row; the person opens the account menu</span>
|
|
121
|
+
<div style="width: 1240px; max-width: 100%; height: 640px; border-radius: var(--ws-radius-panel); overflow: hidden;"><div class="ws-app-shell">
|
|
122
|
+
<nav class="ws-app-shell__nav" aria-label="Main">
|
|
123
|
+
<div class="ws-app-shell__head">
|
|
124
|
+
<span class="ws-app-shell__wordmark">Customer Value</span>
|
|
125
|
+
<button type="button" class="ws-icon-btn" aria-label="Fold the menu" aria-expanded="true">
|
|
126
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2.5" width="12" height="11" rx="2"/><path d="M6 2.5v11"/></svg>
|
|
127
|
+
</button>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="ws-app-shell__groups">
|
|
130
|
+
<div class="ws-app-shell__group">
|
|
131
|
+
<ul class="ws-app-shell__rows">
|
|
132
|
+
<li><a href="#" class="ws-app-shell__row" onclick="return false;"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7.5 8 3l5 4.5V13H3Z"/></svg><span class="ws-app-shell__name">Home</span></a></li>
|
|
133
|
+
<li><a href="#" class="ws-app-shell__row" onclick="return false;"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 4h10M3 8h10M3 12h6"/></svg><span class="ws-app-shell__name">Calvin</span><span class="ws-app-shell__count">2<span class="ws-sr"> waiting</span></span></a></li>
|
|
134
|
+
</ul>
|
|
135
|
+
</div>
|
|
136
|
+
<div class="ws-app-shell__group">
|
|
137
|
+
<h2 class="ws-app-shell__eyebrow">Book</h2>
|
|
138
|
+
<ul class="ws-app-shell__rows">
|
|
139
|
+
<li><a href="#" class="ws-app-shell__row" onclick="return false;"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="5.6" r="2.4"/><path d="M3 13c0-2.5 2.2-4 5-4s5 1.5 5 4"/></svg><span class="ws-app-shell__name">Accounts</span></a></li>
|
|
140
|
+
<li>
|
|
141
|
+
<details class="ws-app-shell__section" open>
|
|
142
|
+
<summary class="ws-app-shell__row"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="12" height="10" rx="1.5"/><path d="M2 6.5h12"/></svg><span class="ws-app-shell__name">Onboarding</span><svg class="ws-app-shell__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.6 5.2L9.4 8l-2.8 2.8"/></svg></summary>
|
|
143
|
+
<ul class="ws-app-shell__rows">
|
|
144
|
+
<li><a href="#" class="ws-app-shell__row" aria-current="page" onclick="return false;"><span class="ws-app-shell__name">Board</span></a></li>
|
|
145
|
+
<li><a href="#" class="ws-app-shell__row" onclick="return false;"><span class="ws-app-shell__name">Stalled</span><span class="ws-app-shell__count">12<span class="ws-sr"> stalled</span></span></a></li>
|
|
146
|
+
<li><a href="#" class="ws-app-shell__row" onclick="return false;"><span class="ws-app-shell__name">Graduated</span></a></li>
|
|
147
|
+
</ul>
|
|
148
|
+
</details>
|
|
149
|
+
</li>
|
|
150
|
+
<li>
|
|
151
|
+
<details class="ws-app-shell__section">
|
|
152
|
+
<summary class="ws-app-shell__row"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 5.5h10v7H3Z"/><path d="M5.5 5.5V3.5h5v2"/></svg><span class="ws-app-shell__name">Tickets</span><svg class="ws-app-shell__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.6 5.2L9.4 8l-2.8 2.8"/></svg></summary>
|
|
153
|
+
<ul class="ws-app-shell__rows">
|
|
154
|
+
<li><a href="#" class="ws-app-shell__row" onclick="return false;"><span class="ws-app-shell__name">Open</span></a></li>
|
|
155
|
+
<li><a href="#" class="ws-app-shell__row" onclick="return false;"><span class="ws-app-shell__name">Waiting on the customer</span></a></li>
|
|
156
|
+
<li><a href="#" class="ws-app-shell__row" onclick="return false;"><span class="ws-app-shell__name">Closed</span></a></li>
|
|
157
|
+
</ul>
|
|
158
|
+
</details>
|
|
159
|
+
</li>
|
|
160
|
+
</ul>
|
|
161
|
+
</div>
|
|
162
|
+
<div class="ws-app-shell__group">
|
|
163
|
+
<h2 class="ws-app-shell__eyebrow">Team</h2>
|
|
164
|
+
<ul class="ws-app-shell__rows">
|
|
165
|
+
<li><a href="#" class="ws-app-shell__row" onclick="return false;"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 13V8M8 13V3M13 13V9.5"/></svg><span class="ws-app-shell__name">Performance</span></a></li>
|
|
166
|
+
<li><a href="#" class="ws-app-shell__row" onclick="return false;"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6" r="2.5"/><circle cx="11.5" cy="7" r="1.8"/><path d="M1.5 13c0-2.2 2-3.5 4.5-3.5s4.5 1.3 4.5 3.5M10.5 10c1.8 0 3.5.9 3.5 2.6"/></svg><span class="ws-app-shell__name">My team</span></a></li>
|
|
167
|
+
</ul>
|
|
168
|
+
</div>
|
|
169
|
+
<div class="ws-app-shell__group">
|
|
170
|
+
<ul class="ws-app-shell__rows">
|
|
171
|
+
<li><a href="#" class="ws-app-shell__row" onclick="return false;"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9.5 4 5.5 8l4 4"/></svg><span class="ws-app-shell__name">Back to Turbo</span></a></li>
|
|
172
|
+
</ul>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="ws-app-shell__foot">
|
|
176
|
+
<button type="button" class="ws-app-shell__me" popovertarget="shell-me" aria-haspopup="menu">
|
|
177
|
+
<span class="ws-avatar ws-avatar--sm ws-avatar--plain">HD</span>
|
|
178
|
+
<span class="ws-app-shell__foot-words">
|
|
179
|
+
<span class="ws-app-shell__foot-name">Halli Doig</span>
|
|
180
|
+
<span class="ws-muted">Value delivery lead</span>
|
|
181
|
+
</span>
|
|
182
|
+
</button>
|
|
183
|
+
<div id="shell-me" class="ws-menu" popover role="menu" aria-label="Your account">
|
|
184
|
+
<a href="#" class="ws-menu-item" role="menuitem" onclick="return false;">Your profile</a>
|
|
185
|
+
<a href="#" class="ws-menu-item" role="menuitem" onclick="return false;">View as someone else</a>
|
|
186
|
+
<div class="ws-menu-line" role="separator"></div>
|
|
187
|
+
<button type="button" class="ws-menu-item" role="menuitem" popovertarget="shell-me" popovertargetaction="hide">Sign out</button>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
</nav>
|
|
191
|
+
<div class="ws-app-shell__stage">
|
|
192
|
+
<header class="ws-app-shell__bar">
|
|
193
|
+
<button type="button" class="ws-icon-btn ws-app-shell__menu" aria-label="Open the menu">
|
|
194
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 4.5h11M2.5 8h11M2.5 11.5h11"/></svg>
|
|
195
|
+
</button>
|
|
196
|
+
<h1 class="ws-app-shell__title">Onboarding</h1>
|
|
197
|
+
</header>
|
|
198
|
+
<main class="ws-app-shell__body">
|
|
199
|
+
<section class="ws-card ws-card--inset ws-stack" aria-labelledby="shell-s1"><h2 class="ws-title" id="shell-s1">Twelve onboardings have stalled</h2><p class="ws-body">The oldest has waited 54 days for its first meeting.</p></section>
|
|
200
|
+
</main>
|
|
201
|
+
</div>
|
|
202
|
+
</div></div>
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
<div class="ws-stack">
|
|
206
|
+
<span class="ws-muted">Folded, a section’s row opens its pages as a menu panel beside the rail: it names the section, the current page marked, Escape or a click away closes it and the focus goes back to the row. The row stands in for the current page it holds</span>
|
|
207
|
+
<div style="width: 1040px; max-width: 100%; height: 460px; border-radius: var(--ws-radius-panel); overflow: hidden;"><div class="ws-app-shell ws-app-shell--folded">
|
|
208
|
+
<nav class="ws-app-shell__nav" aria-label="Main">
|
|
209
|
+
<div class="ws-app-shell__head">
|
|
210
|
+
<span class="ws-app-shell__wordmark">Customer Value</span>
|
|
211
|
+
<button type="button" class="ws-icon-btn" aria-label="Open the menu" aria-expanded="false">
|
|
212
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2.5" width="12" height="11" rx="2"/><path d="M6 2.5v11"/></svg>
|
|
213
|
+
</button>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="ws-app-shell__groups">
|
|
216
|
+
<div class="ws-app-shell__group">
|
|
217
|
+
<ul class="ws-app-shell__rows">
|
|
218
|
+
<li><a href="#" class="ws-app-shell__row" title="Home" onclick="return false;"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7.5 8 3l5 4.5V13H3Z"/></svg><span class="ws-app-shell__name">Home</span></a></li>
|
|
219
|
+
<li><a href="#" class="ws-app-shell__row" title="Accounts" onclick="return false;"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="5.6" r="2.4"/><path d="M3 13c0-2.5 2.2-4 5-4s5 1.5 5 4"/></svg><span class="ws-app-shell__name">Accounts</span></a></li>
|
|
220
|
+
<li>
|
|
221
|
+
<details class="ws-app-shell__section">
|
|
222
|
+
<summary class="ws-app-shell__row"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="12" height="10" rx="1.5"/><path d="M2 6.5h12"/></svg><span class="ws-app-shell__name">Onboarding</span><svg class="ws-app-shell__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.6 5.2L9.4 8l-2.8 2.8"/></svg></summary>
|
|
223
|
+
<ul class="ws-app-shell__rows">
|
|
224
|
+
<li><a href="#" class="ws-app-shell__row" aria-current="page" onclick="return false;"><span class="ws-app-shell__name">Board</span></a></li>
|
|
225
|
+
<li><a href="#" class="ws-app-shell__row" onclick="return false;"><span class="ws-app-shell__name">Stalled</span></a></li>
|
|
226
|
+
</ul>
|
|
227
|
+
</details>
|
|
228
|
+
<button type="button" class="ws-app-shell__row ws-app-shell__fly" popovertarget="shell-fly-onboarding" title="Onboarding"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="12" height="10" rx="1.5"/><path d="M2 6.5h12"/></svg><span class="ws-app-shell__name">Onboarding</span></button>
|
|
229
|
+
<div id="shell-fly-onboarding" class="ws-menu ws-app-shell__panel" popover aria-label="Onboarding">
|
|
230
|
+
<p class="ws-app-shell__eyebrow">Onboarding</p>
|
|
231
|
+
<ul>
|
|
232
|
+
<li><a href="#" class="ws-menu-item" aria-current="page" onclick="return false;">Board</a></li>
|
|
233
|
+
<li><a href="#" class="ws-menu-item" onclick="return false;">Stalled</a></li>
|
|
234
|
+
<li><a href="#" class="ws-menu-item" onclick="return false;">Graduated</a></li>
|
|
235
|
+
</ul>
|
|
236
|
+
</div>
|
|
237
|
+
</li>
|
|
238
|
+
<li><a href="#" class="ws-app-shell__row" title="Tickets" onclick="return false;"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 5.5h10v7H3Z"/><path d="M5.5 5.5V3.5h5v2"/></svg><span class="ws-app-shell__name">Tickets</span></a></li>
|
|
239
|
+
</ul>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
<div class="ws-app-shell__foot">
|
|
243
|
+
<span class="ws-avatar ws-avatar--sm ws-avatar--plain">HD</span>
|
|
244
|
+
<span class="ws-app-shell__foot-words"><span class="ws-app-shell__foot-name">Halli Doig</span><span class="ws-muted">Value delivery lead</span></span>
|
|
245
|
+
</div>
|
|
246
|
+
</nav>
|
|
247
|
+
<div class="ws-app-shell__stage">
|
|
248
|
+
<header class="ws-app-shell__bar">
|
|
249
|
+
<button type="button" class="ws-icon-btn ws-app-shell__menu" aria-label="Open the menu">
|
|
250
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 4.5h11M2.5 8h11M2.5 11.5h11"/></svg>
|
|
251
|
+
</button>
|
|
252
|
+
<h1 class="ws-app-shell__title">Onboarding board</h1>
|
|
253
|
+
</header>
|
|
254
|
+
<main class="ws-app-shell__body">
|
|
255
|
+
<section class="ws-card ws-card--inset ws-stack" aria-labelledby="shell-f1"><h2 class="ws-title" id="shell-f1">Twelve onboardings have stalled</h2><p class="ws-body">The oldest has waited 54 days for its first meeting.</p></section>
|
|
256
|
+
</main>
|
|
257
|
+
</div>
|
|
258
|
+
</div></div>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
119
261
|
<div class="ws-stack">
|
|
120
262
|
<span class="ws-muted">Folded to the 64 px rail (the same nav, names faded but read aloud and in each row’s title), the Fia frames’ cream stage (--wash), the thread header as the bar with its edge once scrolled, the body loading</span>
|
|
121
263
|
<div style="width: 1040px; max-width: 100%; height: 420px; border-radius: var(--ws-radius-panel); overflow: hidden;"><div class="ws-app-shell ws-app-shell--folded ws-app-shell--wash">
|
|
@@ -369,10 +511,13 @@
|
|
|
369
511
|
<script>
|
|
370
512
|
/* The demo's wiring: a click makes the row the current page (an app's router does this; the pill slides on its own). */
|
|
371
513
|
document.addEventListener('click', (e) => {
|
|
372
|
-
const row = e.target.closest('.ws-app-shell__row');
|
|
514
|
+
const row = e.target.closest('a.ws-app-shell__row');
|
|
373
515
|
if (!row) return;
|
|
374
|
-
for (const r of row.closest('.ws-app-shell__nav').querySelectorAll('.ws-app-shell__row')) r === row ? r.setAttribute('aria-current', 'page') : r.removeAttribute('aria-current');
|
|
516
|
+
for (const r of row.closest('.ws-app-shell__nav').querySelectorAll('a.ws-app-shell__row')) r === row ? r.setAttribute('aria-current', 'page') : r.removeAttribute('aria-current');
|
|
375
517
|
});
|
|
518
|
+
/* The demo opens the folded section's panel so it shows at rest (in an app the row's click opens it). */
|
|
519
|
+
const fly = document.querySelector('.ws-app-shell__fly');
|
|
520
|
+
try { document.getElementById('shell-fly-onboarding').showPopover({ source: fly }); } catch {}
|
|
376
521
|
</script>
|
|
377
522
|
</body>
|
|
378
523
|
</html>
|
|
@@ -37,6 +37,35 @@
|
|
|
37
37
|
/* A row's count: Seth's navy pill, 18 tall, its figure on the act fill's word colour. */
|
|
38
38
|
.ws-app-shell__count { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; min-width: calc(var(--space-md) + var(--space-3xs)); height: calc(var(--space-md) + var(--space-3xs)); padding: 0 var(--space-xxs); border-radius: var(--radius-full); background: var(--color-text-heading); color: var(--ws-on-act); font-size: var(--ws-text-micro); line-height: 1; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
39
39
|
|
|
40
|
+
/* A section: a row that folds its pages open in place (North "Sidebar sections open / close", the house's fold on every
|
|
41
|
+
kit <details>). The row is the <summary>, wearing __row, its chevron at the end turning down as it opens; its pages sit
|
|
42
|
+
one level in, the icon box and its gap (24), so their words start on the section's words. A closed section that holds
|
|
43
|
+
the current page wears the current pill itself, so the page is never lost behind a fold. */
|
|
44
|
+
.ws-app-shell__section > summary { list-style: none; }
|
|
45
|
+
.ws-app-shell__section > summary::-webkit-details-marker { display: none; }
|
|
46
|
+
.ws-app-shell__section > .ws-app-shell__rows { margin-inline-start: calc(var(--ws-icon-lg) + var(--space-xxs)); padding-block-start: var(--space-3xs); }
|
|
47
|
+
.ws-app-shell__row > .ws-app-shell__chevron { color: var(--ws-mark-muted); }
|
|
48
|
+
.ws-app-shell__section[open] > summary > .ws-app-shell__chevron { transform: rotate(90deg); }
|
|
49
|
+
.ws-app-shell__section:not([open]):has(.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"])) > summary { border-radius: var(--ws-radius-control); background: var(--color-surface); box-shadow: var(--ws-float-rim), 0 var(--ws-line) var(--ws-ring) color-mix(in srgb, var(--ws-shade) 6%, transparent), 0 0 0 var(--ws-hairline) color-mix(in srgb, var(--ws-shade) 4%, transparent); color: var(--color-text-brand-strong); }
|
|
50
|
+
.ws-app-shell__section:not([open]):has(.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"])) > summary > svg:first-child { color: var(--ws-chart-under-half); }
|
|
51
|
+
/* Folded, a section cannot open in 64 px: its row (__fly, a button) opens its pages as a menu panel beside the rail
|
|
52
|
+
(__panel, a native popover: the top layer, light dismiss, Escape back to the button). The <details> leaves the rail.
|
|
53
|
+
The panel names the section (its eyebrow, which the rail hides) over its pages as menu rows. */
|
|
54
|
+
.ws-app-shell__fly { display: none; }
|
|
55
|
+
.ws-app-shell--folded .ws-app-shell__section { display: none; }
|
|
56
|
+
.ws-app-shell--folded .ws-app-shell__fly { display: flex; }
|
|
57
|
+
.ws-app-shell--folded .ws-app-shell__fly:has(+ .ws-app-shell__panel :is([aria-current="page"], .ws-menu-item--current)) { border-radius: var(--ws-radius-control); background: var(--color-surface); box-shadow: var(--ws-float-rim), 0 var(--ws-line) var(--ws-ring) color-mix(in srgb, var(--ws-shade) 6%, transparent), 0 0 0 var(--ws-hairline) color-mix(in srgb, var(--ws-shade) 4%, transparent); color: var(--color-text-brand-strong); }
|
|
58
|
+
.ws-app-shell--folded .ws-app-shell__fly:has(+ .ws-app-shell__panel :is([aria-current="page"], .ws-menu-item--current)) > svg { color: var(--ws-chart-under-half); }
|
|
59
|
+
.ws-app-shell--folded .ws-app-shell__panel .ws-app-shell__eyebrow { display: block; }
|
|
60
|
+
.ws-app-shell__panel > .ws-app-shell__eyebrow { margin: 0; }
|
|
61
|
+
.ws-app-shell__panel > ul { display: flex; flex-direction: column; gap: var(--space-3xs); }
|
|
62
|
+
/* Beside the rail, its top on the button's, 8 past the rail's edge (the button sits 12 inside it); it flips up when the
|
|
63
|
+
room below runs out. It grows from that corner. */
|
|
64
|
+
.ws-app-shell__panel { --ws-menu-from: 0px; transform-origin: top left; }
|
|
65
|
+
@supports (position-area: block-end) {
|
|
66
|
+
.ws-app-shell__panel.ws-menu[popover] { margin: 0 var(--space-xs) 0 calc(var(--space-sm) + var(--space-xs)); position-area: inline-end span-block-end; justify-self: auto; position-try-fallbacks: flip-block; }
|
|
67
|
+
}
|
|
68
|
+
|
|
40
69
|
/* The person, pinned at the foot under a full-width rule. */
|
|
41
70
|
.ws-app-shell__foot { flex-shrink: 0; display: flex; align-items: center; gap: var(--space-xs-plus); margin-top: auto; padding: var(--space-sm) var(--space-xs) var(--space-xxs-plus) var(--space-sm); border-top: var(--ws-line) solid var(--ws-edge-card); }
|
|
42
71
|
.ws-app-shell__foot > :first-child { margin-inline-start: var(--space-xs-plus); }
|
|
@@ -48,6 +77,14 @@
|
|
|
48
77
|
.ws-app-shell__nav:not(:has(> .ws-app-shell__groups)) > :is(.ws-app-shell__wordmark, .ws-app-shell__foot) { position: sticky; z-index: 1; background: var(--ws-frame); background-attachment: fixed; }
|
|
49
78
|
.ws-app-shell__nav:not(:has(> .ws-app-shell__groups)) > .ws-app-shell__wordmark { top: 0; min-height: calc(var(--ws-header-h) + var(--ws-line)); margin-inline: 0; padding: var(--ws-line) var(--space-xs) 0 calc(var(--space-sm) + var(--space-xs-plus)); }
|
|
50
79
|
.ws-app-shell__nav:not(:has(> .ws-app-shell__groups)) > .ws-app-shell__foot { bottom: 0; padding-bottom: var(--space-sm-plus); }
|
|
80
|
+
/* The person as a button (__me) that opens the account menu (Menu, above it once it flips): the foot keeps its height and
|
|
81
|
+
edges, the button takes a row's 10 sides and 6 px corners, so the avatar stays 22 in and the wash reads as a row's. */
|
|
82
|
+
.ws-app-shell__me { display: flex; flex: 1; align-items: center; gap: var(--space-xs-plus); min-width: 0; margin-block: calc(var(--space-3xs) * -1); padding: var(--space-3xs) var(--space-xs-plus); border: 0; border-radius: var(--ws-radius-row); background: none; font: inherit; color: inherit; text-align: start; cursor: pointer; }
|
|
83
|
+
.ws-app-shell__foot > .ws-app-shell__me { margin-inline: 0; }
|
|
84
|
+
.ws-app-shell__me:focus-visible { outline-offset: calc(var(--ws-focus-width) * -1); }
|
|
85
|
+
@media (hover: hover) { .ws-app-shell__me:hover { background: var(--ws-hover); } }
|
|
86
|
+
.ws-app-shell__me:active { background: var(--ws-hover-strong); }
|
|
87
|
+
.ws-app-shell--folded .ws-app-shell__me { flex: 0 0 auto; gap: 0; }
|
|
51
88
|
.ws-app-shell__foot-words { display: flex; flex-direction: column; min-width: 0; }
|
|
52
89
|
.ws-app-shell__foot-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--ws-text-control); line-height: var(--space-md); font-weight: 500; color: var(--color-text-heading); }
|
|
53
90
|
|
|
@@ -137,6 +174,9 @@
|
|
|
137
174
|
:is(.ws-app-shell__groups, .ws-app-shell__nav:not(:has(> .ws-app-shell__groups))) { position: relative; anchor-scope: --ws-app-shell-current; }
|
|
138
175
|
:is(.ws-app-shell__groups, .ws-app-shell__nav:not(:has(> .ws-app-shell__groups)))::before { content: ""; position: absolute; position-anchor: --ws-app-shell-current; inset: anchor(top) anchor(right) anchor(bottom) anchor(left); border-radius: var(--ws-radius-control); background: var(--color-surface); box-shadow: var(--ws-float-rim), 0 var(--ws-line) var(--ws-ring) color-mix(in srgb, var(--ws-shade) 6%, transparent), 0 0 0 var(--ws-hairline) color-mix(in srgb, var(--ws-shade) 4%, transparent); opacity: 0; pointer-events: none; }
|
|
139
176
|
.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"]) { anchor-name: --ws-app-shell-current; }
|
|
177
|
+
/* A closed section, or a folded section's button, stands in for the current page it holds (the page itself has no box). */
|
|
178
|
+
.ws-app-shell__section:not([open]):has(.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"])) > summary,
|
|
179
|
+
.ws-app-shell--folded .ws-app-shell__fly:has(+ .ws-app-shell__panel :is([aria-current="page"], .ws-menu-item--current)) { anchor-name: --ws-app-shell-current; }
|
|
140
180
|
.ws-app-shell__eyebrow { position: relative; } /* a group's name stays over the pill as it passes, as the rows do */
|
|
141
181
|
}
|
|
142
182
|
|
|
@@ -145,10 +185,17 @@
|
|
|
145
185
|
.ws-app-shell > .ws-app-shell__nav { transition: width var(--ws-duration-shift) var(--ws-ease-shift); }
|
|
146
186
|
.ws-app-shell :is(.ws-app-shell__wordmark, .ws-app-shell__name, .ws-app-shell__foot-words) { transition: opacity var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
147
187
|
.ws-app-shell__row { transition: background-color var(--ws-duration-hover) var(--ws-ease-soft), color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
188
|
+
.ws-app-shell__me { transition: background-color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
189
|
+
.ws-app-shell__chevron { transition: transform var(--ws-spring-fold-duration) var(--ws-spring-fold); }
|
|
190
|
+
@supports (position-area: block-end) {
|
|
191
|
+
.ws-app-shell__panel.ws-menu[popover]:not(:popover-open) { margin-block-start: 0; }
|
|
192
|
+
@starting-style { .ws-app-shell__panel.ws-menu[popover]:popover-open { margin-block-start: 0; } }
|
|
193
|
+
}
|
|
148
194
|
.ws-app-shell__bar { transition: box-shadow var(--ws-duration-settle) var(--ws-ease-soft); }
|
|
149
195
|
@supports (anchor-name: --a) {
|
|
150
196
|
:is(.ws-app-shell__groups, .ws-app-shell__nav:not(:has(> .ws-app-shell__groups))):is(:hover, :focus-within)::before { opacity: 1; transition: inset var(--ws-spring-nav-duration) var(--ws-spring-nav); }
|
|
151
197
|
:is(.ws-app-shell__groups, .ws-app-shell__nav:not(:has(> .ws-app-shell__groups))):is(:hover, :focus-within) .ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"]) { background: none; box-shadow: none; }
|
|
198
|
+
.ws-app-shell__groups:is(:hover, :focus-within) :is(.ws-app-shell__section:not([open]):has(.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"])) > summary, .ws-app-shell__fly:has(+ .ws-app-shell__panel :is([aria-current="page"], .ws-menu-item--current))) { background: none; box-shadow: none; }
|
|
152
199
|
.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"]) { transition: color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
153
200
|
}
|
|
154
201
|
}
|
|
@@ -17,7 +17,7 @@ never coloured, never an app's face.
|
|
|
17
17
|
|
|
18
18
|
- Shape: a disc, `radius-full`, `overflow: hidden` so a photo never spills past its edge.
|
|
19
19
|
- Ground: `--ws-surface-sunk`, with navy initials (`--color-text-heading`) semibold, sized to the disc (12 to
|
|
20
|
-
24 px across the sizes below: 12, 14, 14, 16, 24).
|
|
20
|
+
24 px across the sizes below: 11, 12, 14, 14, 16, 24).
|
|
21
21
|
- A photo: an `<img alt="">` inside the disc, after the initials, covering it (`object-fit: cover`). The initials stay
|
|
22
22
|
under it, so the disc is never empty: while it loads, and if it never does. With `data-state="loading"` (the React
|
|
23
23
|
component sets it, or a page script until `load`) it waits hidden, then fades in over 200 ms (`--ws-duration-gentle`
|
|
@@ -26,13 +26,14 @@ never coloured, never an app's face.
|
|
|
26
26
|
- `dim`: for someone invited who has not joined yet: the initials in the muted word (`--color-text-muted`, AA on every
|
|
27
27
|
ground), a photo at 60% opacity. The disc itself never fades, so its initials never drop under AA.
|
|
28
28
|
- In fello.css: `.ws-avatar` is the 32 px disc with the ring (it mostly sits on a cream card); `--plain` drops the
|
|
29
|
-
ring, `--dim` dims it, `--sm`, `--deck`, `--panel` and `--xl` size it. A stack is `.ws-avatar-stack` with
|
|
29
|
+
ring, `--dim` dims it, `--xs`, `--sm`, `--deck`, `--panel` and `--xl` size it. A stack is `.ws-avatar-stack` with
|
|
30
30
|
`.ws-avatar-stack__more` on the "+N" disc (tabular figures, so "+9" and "+12" sit alike).
|
|
31
31
|
|
|
32
32
|
## Variants and sizes
|
|
33
33
|
|
|
34
34
|
| Size token | Px | Where |
|
|
35
35
|
| --- | --- | --- |
|
|
36
|
+
| `xs` | 20 | A face inside a chip, a dense picker's row, a task row's assignee: a line of words' height (#16) |
|
|
36
37
|
| `sm` | 28 | A dense row (a list's rows, a stack's own faces) |
|
|
37
38
|
| `md` | 32 | A card's or a list's default face |
|
|
38
39
|
| `deck` | 48 | A card in a "go through" deck |
|
|
@@ -74,7 +75,7 @@ steps `sm` 28, `md` 32, `deck` 48, `panel` 56, `xl` 64. This README and preview
|
|
|
74
75
|
| --- | --- | --- |
|
|
75
76
|
| `name` | `string` | Used for the initials and the alt text context |
|
|
76
77
|
| `photo` | `string \| null` | A URL to their picture, or `null` for initials only |
|
|
77
|
-
| `size` | `'sm' \| 'md' \| 'deck' \| 'panel' \| 'xl'` | The disc's size |
|
|
78
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'deck' \| 'panel' \| 'xl'` | The disc's size |
|
|
78
79
|
| `dim` | `boolean` | Muted initials (a photo at 60%), for someone invited who has not joined |
|
|
79
80
|
| `ring` | `boolean` | The hairline edge for a cream card |
|
|
80
81
|
|
|
@@ -100,6 +101,8 @@ ring for a cream card. **Precedent**: the first product built on the system.
|
|
|
100
101
|
|
|
101
102
|
```html
|
|
102
103
|
<span class="ws-avatar ws-avatar--sm" aria-hidden="true">PN</span>
|
|
104
|
+
<!-- extra small: inside a chip or a dense row -->
|
|
105
|
+
<span class="ws-avatar ws-avatar--xs" aria-hidden="true">PN</span>
|
|
103
106
|
|
|
104
107
|
<span class="ws-avatar ws-avatar--plain" aria-hidden="true">DS<img alt="" src="/people/dana.jpg"></span>
|
|
105
108
|
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
<div class="ws-stage ws-stage--column">
|
|
12
12
|
|
|
13
13
|
<div class="ws-stack" style="align-items: flex-start;">
|
|
14
|
-
<span class="ws-muted">Sizes: sm 28, md 32, deck 48, panel 56, xl 64 (initials until a photo loads)</span>
|
|
14
|
+
<span class="ws-muted">Sizes: xs 20, sm 28, md 32, deck 48, panel 56, xl 64 (initials until a photo loads)</span>
|
|
15
15
|
<div class="ws-cluster ws-cluster--group" style="align-items: flex-end;">
|
|
16
|
+
<div class="ws-stack ws-stack--tight" style="align-items: center;"><span class="ws-avatar ws-avatar--plain ws-avatar--xs" aria-hidden="true">PN</span><span class="ws-muted">xs</span></div>
|
|
16
17
|
<div class="ws-stack ws-stack--tight" style="align-items: center;"><span class="ws-avatar ws-avatar--plain ws-avatar--sm" aria-hidden="true">PN</span><span class="ws-muted">sm</span></div>
|
|
17
18
|
<div class="ws-stack ws-stack--tight" style="align-items: center;"><span class="ws-avatar ws-avatar--plain" aria-hidden="true">PN</span><span class="ws-muted">md</span></div>
|
|
18
19
|
<div class="ws-stack ws-stack--tight" style="align-items: center;"><span class="ws-avatar ws-avatar--plain ws-avatar--deck" aria-hidden="true">PN</span><span class="ws-muted">deck</span></div>
|
|
@@ -10,5 +10,8 @@
|
|
|
10
10
|
house fade of the whole disc left them at 3.7:1); a photo in it fades to 60 %. */
|
|
11
11
|
.ws-avatar--dim { opacity: 1; color: var(--color-text-muted); }
|
|
12
12
|
.ws-avatar--dim > img { opacity: 0.6; }
|
|
13
|
+
/* Extra small (#16): 20, a line of words' height, for a face inside a chip, a dense picker's row or a task row's
|
|
14
|
+
assignee: initials 11 px semibold. A stack of them overlaps 4, as every stack. */
|
|
15
|
+
.ws-avatar--xs { width: calc(var(--space-md) + var(--space-xxs)); height: calc(var(--space-md) + var(--space-xxs)); font-size: var(--ws-text-micro); }
|
|
13
16
|
/* "+N" in a stack: the same disc, tabular figures, so +9 and +12 sit alike. */
|
|
14
17
|
.ws-avatar-stack__more { font-variant-numeric: tabular-nums; }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
A full-width notice for a state of the whole page or card, in one of
|
|
1
|
+
A full-width notice for a state of the whole page or card, in one of five quiet tones, never a loud one.
|
|
2
2
|
|
|
3
3
|
## Use it when
|
|
4
4
|
|
|
@@ -30,13 +30,17 @@ A full-width notice for a state of the whole page or card, in one of four quiet
|
|
|
30
30
|
| `neutral` | `--ws-surface-sunk` | `--color-text-body` / muted | a plain status ("as of Friday") |
|
|
31
31
|
| `needs` | `--ws-tint-needs` | `--ws-word-needs` | offline, a trial ending, something to fix |
|
|
32
32
|
| `positive` | `--ws-tint-live` | `--ws-word-positive` | a job finished, an import succeeded |
|
|
33
|
-
| `working` | `--ws-card-tint-blue` | `--color-text-interactive` | still checking, still running |
|
|
33
|
+
| `working` | `--ws-card-tint-blue` | `--color-text-interactive` | still checking, still running; an info status |
|
|
34
|
+
| `alert` | `--ws-tint-alert` | `--ws-word-alert` | something is wrong now: a post that did not publish, a sync that failed; an error status |
|
|
35
|
+
|
|
36
|
+
An app whose words are error, warning, success and info maps them onto these tones (Colour, Status sets): error is
|
|
37
|
+
`alert`, warning is `needs`, success is `positive`, info is `working`.
|
|
34
38
|
|
|
35
39
|
There is one size; it spans whatever width its container gives it.
|
|
36
40
|
|
|
37
41
|
## States
|
|
38
42
|
|
|
39
|
-
- **The
|
|
43
|
+
- **The five tones:** as the table above, each shown with its own icon, words and act.
|
|
40
44
|
- **With a close act:** the `x` icon button sits at the far right.
|
|
41
45
|
- **Long words / narrow:** the words wrap; the mark and the acts stay on the first line's band.
|
|
42
46
|
- **Dismissed:** the banner is removed entirely; nothing is drawn in its place.
|
|
@@ -59,7 +63,7 @@ There is one size; it spans whatever width its container gives it.
|
|
|
59
63
|
|
|
60
64
|
| Name | Type | Meaning |
|
|
61
65
|
| --- | --- | --- |
|
|
62
|
-
| `tone` | `'neutral' \| 'needs' \| 'positive' \| 'working'` | the look, default `neutral` |
|
|
66
|
+
| `tone` | `'neutral' \| 'needs' \| 'positive' \| 'working' \| 'alert'` | the look, default `neutral` |
|
|
63
67
|
| `icon` | `IconName` | the 16 px icon at the left |
|
|
64
68
|
| `children` | `ReactNode` | the words |
|
|
65
69
|
| `action` | `ReactNode` | the `text` or `well` small button at the right |
|
|
@@ -95,5 +99,5 @@ from the system, 26 Oct 2026.
|
|
|
95
99
|
|
|
96
100
|
## Styles
|
|
97
101
|
|
|
98
|
-
`design-system/components/Banner/style.css`: `ws-banner` with `--neutral`, `--needs`, `--positive`, `--working`;
|
|
102
|
+
`design-system/components/Banner/style.css`: `ws-banner` with `--neutral`, `--needs`, `--positive`, `--working`, `--alert`;
|
|
99
103
|
`__mark`, `__words`, `__lead`, `__rest`.
|
|
@@ -49,6 +49,15 @@
|
|
|
49
49
|
</div>
|
|
50
50
|
</div>
|
|
51
51
|
|
|
52
|
+
<div class="ws-stack">
|
|
53
|
+
<span class="ws-muted">Alert: an error status, something wrong now</span>
|
|
54
|
+
<div class="ws-banner ws-banner--alert" role="alert">
|
|
55
|
+
<svg class="ws-banner__mark" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="8" cy="8" r="6"/><path d="M8 5v3.5M8 11h.01"/></svg>
|
|
56
|
+
<span class="ws-banner__words"><span class="ws-banner__lead">The post did not publish.</span> <span class="ws-banner__rest">LinkedIn turned it down at 9:02.</span></span>
|
|
57
|
+
<button type="button" class="ws-btn ws-btn--text">Try again</button>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
52
61
|
<p class="ws-muted">Dismissed: the banner is removed entirely, nothing left in its place.</p>
|
|
53
62
|
|
|
54
63
|
</div>
|
|
@@ -16,4 +16,7 @@
|
|
|
16
16
|
.ws-banner--positive .ws-banner__mark, .ws-banner--positive .ws-banner__lead { color: var(--ws-word-positive); }
|
|
17
17
|
.ws-banner--working { background: var(--ws-card-tint-blue); }
|
|
18
18
|
.ws-banner--working .ws-banner__mark, .ws-banner--working .ws-banner__lead { color: var(--color-text-interactive); }
|
|
19
|
+
/* An error status (#15): something wrong now, the alert red's tint and words. */
|
|
20
|
+
.ws-banner--alert { background: var(--ws-tint-alert); }
|
|
21
|
+
.ws-banner--alert .ws-banner__mark, .ws-banner--alert .ws-banner__lead { color: var(--ws-word-alert); }
|
|
19
22
|
@media (pointer: coarse) { .ws-banner { --ws-banner-band: var(--ws-touch); } }
|