@felloinnovations/brand-kit 0.5.0 → 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 +110 -0
- package/README.md +14 -6
- package/design-system/01-how-claude-decides.md +5 -3
- package/design-system/03-colour.md +22 -3
- 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 +7 -4
- package/design-system/10-patterns.md +1 -1
- package/design-system/11-page-recipes.md +40 -1
- package/design-system/13-motion.md +4 -4
- package/design-system/15-ai-experiences.md +11 -0
- package/design-system/17-decisions.md +51 -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/CheckRow/README.md +1 -1
- package/design-system/components/CheckRow/preview.html +1 -1
- package/design-system/components/CheckRow/style.css +1 -1
- 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 +69 -5
- package/design-system/components/KanbanBoard/preview.html +129 -0
- package/design-system/components/KanbanBoard/style.css +32 -1
- package/design-system/components/ListDetail/preview.html +1 -1
- package/design-system/components/Matrix/README.md +1 -1
- package/design-system/components/Matrix/preview.html +1 -1
- package/design-system/components/Motion/README.md +2 -2
- package/design-system/components/Motion/preview.html +8 -8
- 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 +12 -7
- package/design-system/components/ScoreHero/README.md +2 -2
- package/design-system/components/ScoreHero/style.css +1 -1
- package/design-system/components/ShareBar/README.md +1 -1
- package/design-system/components/ShareBar/style.css +1 -1
- package/design-system/components/Skeleton/README.md +82 -17
- package/design-system/components/Skeleton/preview.html +84 -2
- package/design-system/components/Skeleton/style.css +9 -14
- 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 +8 -4
- package/design-system/components/Table/preview.html +16 -1
- package/design-system/components/Table/style.css +3 -1
- 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/TickStrip/README.md +1 -1
- package/design-system/components/TickStrip/preview.html +1 -1
- package/design-system/components/TickStrip/style.css +1 -1
- package/design-system/components/Timeline/README.md +4 -2
- 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/Well/README.md +1 -1
- package/design-system/components/Well/style.css +2 -2
- package/design-system/components/_house.css +1 -1
- package/design-system/components/bundle.css +501 -30
- package/design-system/tokens.json +17 -7
- 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 +16 -8
- package/dist/tokens.js +16 -8
- package/package.json +7 -2
- package/scripts/check-drift.mjs +325 -0
- package/scripts/token-model.mjs +2 -1
- package/tokens/_dark.css +3 -2
- package/tokens/_product.css +13 -11
- 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 +23 -15
- package/tokens/tokens.md +56 -50
- package/tokens/tokens.root.css +23 -15
- package/tokens/tokens.ts +17 -9
|
@@ -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); } }
|
|
@@ -47,6 +47,13 @@ A split pair (`ws-btn-split`): two acts in equal halves across the width, the pr
|
|
|
47
47
|
half would be under 160 px, or narrower than either label on its one line, they stack, the primary at the bottom, under
|
|
48
48
|
the thumb.
|
|
49
49
|
|
|
50
|
+
An act with a menu of its other forms (`ws-btn-join`, #22): the act and a caret joined into one shape, the caret
|
|
51
|
+
opening a `Menu` of the same act done another way ("Approve", and behind the caret "Approve and post now", "Approve and
|
|
52
|
+
schedule"). The inner corners are square; a hairline in the words' colour at 35 % parts the halves, 6 in from the top
|
|
53
|
+
and bottom (two `secondary` halves share one edge instead); the caret is a square of the button's height with a 16 px
|
|
54
|
+
chevron (28 at small and medium, 40 large and on touch). Any look, any size; both halves wear the same classes. The
|
|
55
|
+
pair gives to a press as one. Not `ws-btn-split`, which is two different acts side by side.
|
|
56
|
+
|
|
50
57
|
`--wrap`: a label that may be wider than its column wraps in balanced lines (a button's words never wrap otherwise).
|
|
51
58
|
|
|
52
59
|
## States
|
|
@@ -75,7 +82,10 @@ the thumb.
|
|
|
75
82
|
- **Pending under the pointer:** keeps its resting look (no brighter navy, no light, no wash): it refuses the press a
|
|
76
83
|
hover would invite.
|
|
77
84
|
- **Long label:** one line; with `ws-btn--wrap`, wider than its column, it wraps in balanced lines.
|
|
78
|
-
- **Narrow:** the split pair stacks; a lone large button may take the width
|
|
85
|
+
- **Narrow:** the split pair stacks; a lone large button may take the width; a joined act never splits, its act's
|
|
86
|
+
words cut before the caret goes.
|
|
87
|
+
- **Joined, open:** the caret has `aria-expanded="true"` and its menu hangs under it, right edges flush (`Menu`'s
|
|
88
|
+
placement); Escape closes it and returns focus to the caret.
|
|
79
89
|
- **Touch:** boxed sizes at least 40; `link` and `text` 24 tall.
|
|
80
90
|
- **Dark:** the act is the light ink with dark words; its hover eases a step toward its words and draws no light
|
|
81
91
|
(derived: North has no dark values).
|
|
@@ -112,6 +122,11 @@ the thumb.
|
|
|
112
122
|
|
|
113
123
|
## Rules
|
|
114
124
|
|
|
125
|
+
- **A joined act holds the act's other forms, never other acts.** "Approve" with "Approve and post now" behind its
|
|
126
|
+
caret; not "Approve" with "Delete" (that is a `Menu` from a three-dots `IconButton`). The plain act is the one most
|
|
127
|
+
people press. A navy joined act is the surface's one navy act. The caret is named for what it holds ("Other ways to
|
|
128
|
+
approve"), never "More".
|
|
129
|
+
|
|
115
130
|
- Every button is `Button` or `ButtonLink` (`SubmitButton` in a form), never a styled `<button>` (24 Oct 2026, "One way to
|
|
116
131
|
use buttons").
|
|
117
132
|
- One navy (`primary`) button per card, and one for the page; everything else is secondary, well, ghost or words.
|
|
@@ -150,6 +165,18 @@ North's 34 px height is not taken: the Fia frames' 32 / 40 sit on the control la
|
|
|
150
165
|
<button type="button" class="ws-btn ws-btn--lg ws-btn--primary">Review Grow's rewrite</button>
|
|
151
166
|
</div>
|
|
152
167
|
|
|
168
|
+
<!-- an act with a menu of its other forms: the caret opens a Menu -->
|
|
169
|
+
<div class="ws-btn-join">
|
|
170
|
+
<button type="button" class="ws-btn ws-btn--primary">Approve</button>
|
|
171
|
+
<button type="button" class="ws-btn ws-btn--primary" aria-label="Other ways to approve" aria-haspopup="menu" aria-expanded="false" aria-controls="approve-menu">
|
|
172
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
|
|
173
|
+
</button>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="ws-menu" role="menu" id="approve-menu" popover>
|
|
176
|
+
<button type="button" class="ws-menu-item" role="menuitem">Approve and post now</button>
|
|
177
|
+
<button type="button" class="ws-menu-item" role="menuitem">Approve and schedule</button>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
153
180
|
<a href="/leads/priya" class="ws-card ws-card--inset" aria-busy="true" style="position: relative; display: block;">
|
|
154
181
|
Priya Nair <span class="ws-link-pending" aria-hidden="true"></span>
|
|
155
182
|
</a>
|
|
@@ -159,6 +186,6 @@ North's 34 px height is not taken: the Fia frames' 32 / 40 sit on the control la
|
|
|
159
186
|
|
|
160
187
|
`design-system/components/Button/style.css` (in `bundle.css` and `fello.css`): `ws-btn` with `--sm`, `--lg`, `--primary`,
|
|
161
188
|
`--secondary`, `--well`, `--ghost`, `--link`, `--text`, `--wrap`, and the pictured states `is-hover`, `is-pressed`,
|
|
162
|
-
`is-focus`; `ws-btn-split`; `ws-link-pending`. It also scopes the shared hover values `--ws-edge-hover`,
|
|
189
|
+
`is-focus`; `ws-btn-split`; `ws-btn-join`; `ws-link-pending`. It also scopes the shared hover values `--ws-edge-hover`,
|
|
163
190
|
`--ws-act-hover` and `--ws-act-light` for every outlined or navy control (the switch's track too). Touch sizes come from
|
|
164
191
|
the house's touch block; the working dot is the house's `ws-dot`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- @dsCard group="Actions" height=
|
|
1
|
+
<!-- @dsCard group="Actions" height=1200 -->
|
|
2
2
|
<!doctype html>
|
|
3
3
|
<html>
|
|
4
4
|
<head>
|
|
@@ -56,6 +56,38 @@
|
|
|
56
56
|
</div>
|
|
57
57
|
</div>
|
|
58
58
|
|
|
59
|
+
<div class="ws-stack">
|
|
60
|
+
<span class="ws-muted">An act with a menu of its other forms: the act and its caret joined, then the menu open</span>
|
|
61
|
+
<div class="ws-cluster ws-cluster--start">
|
|
62
|
+
<div class="ws-btn-join">
|
|
63
|
+
<button type="button" class="ws-btn ws-btn--primary">Approve</button>
|
|
64
|
+
<button type="button" class="ws-btn ws-btn--primary" aria-label="Other ways to approve" aria-haspopup="menu" aria-expanded="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="ws-btn-join">
|
|
67
|
+
<button type="button" class="ws-btn ws-btn--well">Save draft</button>
|
|
68
|
+
<button type="button" class="ws-btn ws-btn--well" aria-label="Other ways to save" aria-haspopup="menu" aria-expanded="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="ws-btn-join">
|
|
71
|
+
<button type="button" class="ws-btn ws-btn--secondary ws-btn--sm">Export</button>
|
|
72
|
+
<button type="button" class="ws-btn ws-btn--secondary ws-btn--sm" aria-label="Other ways to export" aria-haspopup="menu" aria-expanded="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="ws-btn-join">
|
|
75
|
+
<button type="button" class="ws-btn ws-btn--primary ws-btn--lg">Publish</button>
|
|
76
|
+
<button type="button" class="ws-btn ws-btn--primary ws-btn--lg" aria-label="Other ways to publish" aria-haspopup="menu" aria-expanded="false"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
<div class="ws-stack" style="align-items: flex-start;">
|
|
80
|
+
<div class="ws-btn-join">
|
|
81
|
+
<button type="button" class="ws-btn ws-btn--primary">Approve</button>
|
|
82
|
+
<button type="button" class="ws-btn ws-btn--primary" aria-label="Other ways to approve" aria-haspopup="menu" aria-expanded="true" aria-controls="join-menu"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg></button>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="ws-menu" role="menu" id="join-menu" aria-label="Other ways to approve">
|
|
85
|
+
<button type="button" class="ws-menu-item" role="menuitem">Approve and post now</button>
|
|
86
|
+
<button type="button" class="ws-menu-item" role="menuitem">Approve and schedule</button>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
59
91
|
<div class="ws-stack">
|
|
60
92
|
<span class="ws-muted">Words that act: link (goes somewhere) and text (acts in place), 24 px targets on touch</span>
|
|
61
93
|
<p class="ws-body">Two leads came in overnight. <button type="button" class="ws-btn ws-btn--link">See them on the board</button></p>
|
|
@@ -76,6 +76,27 @@
|
|
|
76
76
|
.ws-btn-split { display: flex; flex-wrap: wrap; gap: var(--space-xs); width: 100%; }
|
|
77
77
|
.ws-btn-split > .ws-btn { flex: 1 1 var(--space-8xl); min-width: fit-content; }
|
|
78
78
|
|
|
79
|
+
/* An act with a menu of its other forms (#22): the act and a caret joined into one shape, the caret opening a Menu of
|
|
80
|
+
the same act done another way ("Approve and post now", "Approve and schedule"). Not ws-btn-split (two different acts
|
|
81
|
+
side by side). The inner corners go square; a hairline in the words' colour at 35 % parts the halves, inset 6 from the
|
|
82
|
+
top and bottom (on navy, the white words' line; two outlined halves share one edge instead); the caret is a square of
|
|
83
|
+
its height with a 16 px chevron (28 at small and medium, 40 large). The pair gives to a press as one: neither half
|
|
84
|
+
moves alone, so the join never opens. Each half takes its own focus ring. */
|
|
85
|
+
.ws-btn-join { display: inline-flex; flex-shrink: 0; max-width: 100%; }
|
|
86
|
+
.ws-btn-join > .ws-btn { --ws-press: 1; }
|
|
87
|
+
.ws-btn-join > .ws-btn:first-child { min-width: 0; border-start-end-radius: 0; border-end-end-radius: 0; }
|
|
88
|
+
.ws-btn-join > .ws-btn:last-child { --ws-btn-pad: var(--space-xxs-plus); border-start-start-radius: 0; border-end-start-radius: 0; }
|
|
89
|
+
.ws-btn-join > .ws-btn--lg:last-child { --ws-btn-pad: var(--space-sm); }
|
|
90
|
+
@media (pointer: coarse) { .ws-btn-join > .ws-btn:last-child { --ws-btn-pad: var(--space-sm); } }
|
|
91
|
+
.ws-btn-join > .ws-btn:last-child > svg { width: var(--ws-icon); height: var(--ws-icon); }
|
|
92
|
+
.ws-btn-join > .ws-btn + .ws-btn::after { content: ""; position: absolute; inset-block: var(--space-xxs-plus); inset-inline-start: 0; width: var(--ws-hairline); background: currentColor; opacity: 0.35; pointer-events: none; }
|
|
93
|
+
.ws-btn-join > .ws-btn--secondary + .ws-btn--secondary { margin-inline-start: calc(var(--ws-hairline) * -1); }
|
|
94
|
+
.ws-btn-join > .ws-btn--secondary + .ws-btn--secondary::after { content: none; }
|
|
95
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
96
|
+
.ws-btn-join { transition: scale var(--ws-spring-press-duration) var(--ws-spring-press); }
|
|
97
|
+
.ws-btn-join:active { scale: var(--ws-press-scale); }
|
|
98
|
+
}
|
|
99
|
+
|
|
79
100
|
/* LinkPending: a link card or a ButtonLink whose page is on its way. Its 1 px navy edge arrives after a beat (a quick
|
|
80
101
|
answer shows nothing) and breathes until the page answers; it leaves at once. The link holds it (position: relative). */
|
|
81
102
|
.ws-link-pending { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; opacity: 0; }
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Things on dates: a month (or a week) of days, Monday first, each day holding its items as rows that open, the steps and
|
|
2
|
+
the view switch over it, today ringed; in a narrow column the same markup reads as an agenda.
|
|
3
|
+
|
|
4
|
+
## Use it when
|
|
5
|
+
|
|
6
|
+
- Work is laid out by day and the day is the point: scheduled posts, campaign tasks, touches, sends, events.
|
|
7
|
+
- A person moves through time (this month, the next, back to today) and opens an item from the day it falls on.
|
|
8
|
+
|
|
9
|
+
## Not for
|
|
10
|
+
|
|
11
|
+
- Choosing one day or a range: use `DateInput`.
|
|
12
|
+
- Whether a day happened (a streak of logged activity): use `StreakCalendar`.
|
|
13
|
+
- A plan's phases and tasks with their start and end: use `PlanTimeline`.
|
|
14
|
+
- One thing's dated milestones: use `Stepper --milestones`. What happened, in order: use `Timeline`.
|
|
15
|
+
|
|
16
|
+
## Anatomy
|
|
17
|
+
|
|
18
|
+
- The bar (`__bar`): the period's name (`ws-section`, "November 2026", "Nov 9 to 15, 2026") on the left; at the right
|
|
19
|
+
the steps (`__steps`: a 28 px previous `IconButton`, a small secondary `Button` "Today", a 28 px next) and the view
|
|
20
|
+
switch (`Segmented`, "Month" and "Week"). It wraps on a narrow column, the switch under the title.
|
|
21
|
+
- The key (`__key`, optional): what each tone means, a mark (`__item-mark` with the item's tone class) and its words in
|
|
22
|
+
the meta words, on fixed 160 px columns so a wrapped key keeps shared edges; the lit period's swatch is `__key-lit`.
|
|
23
|
+
- The weekday names (`__weekdays`): once over the grid, 11 / 13 medium muted, centred over their days, `aria-hidden`.
|
|
24
|
+
- The days (`__days`, an `ol`): seven equal columns, the days abutting on 0.5 px lines (`--ws-edge-card`), 4 px corners
|
|
25
|
+
outside. A day (`__day`): at least 120 tall, 4 inside; its head (`__day-head`) centres the number (`__date`, a
|
|
26
|
+
`time`): 24 px round, 12 / 16 medium navy, figures tabular. A day outside the month (`--outside`): its number muted.
|
|
27
|
+
- Today: `aria-current="date"` on its `time`, a 1 px ring and the number in the salmon's AA step (`--ws-word-needs`,
|
|
28
|
+
semibold), as `DateInput`'s today is.
|
|
29
|
+
- A lit day (`--lit`): a day inside the period the view is about (the launch week, the phase in hand), one quiet tint
|
|
30
|
+
(`--ws-tint-accent`), named in the key.
|
|
31
|
+
- The items (`__items`, a `ul`): rows 2 apart. An item (`__item`, a link or a button with a `title`): 24 tall, 6 inside,
|
|
32
|
+
4 px corners, on the sunk cream; a 6 px dot in its tone, 6 before its words, 12 / 16 navy, one line, whole in its
|
|
33
|
+
`title`. A milestone (`--milestone`): the dot a diamond, the words medium.
|
|
34
|
+
- More (`__more`, a `ws-btn ws-btn--text`): "2 more" under the third item, opening the day (a popover or the week).
|
|
35
|
+
- An empty day in the week view: `__empty`, "Nothing due" in the meta words.
|
|
36
|
+
|
|
37
|
+
## Variants and sizes
|
|
38
|
+
|
|
39
|
+
| Variant | Class | What changes |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| Month | `ws-calendar` | Six weeks of seven days; three items a day, then "N more" |
|
|
42
|
+
| Week | `ws-calendar--week` | One row of seven days at least 200 tall; every item shown |
|
|
43
|
+
| Agenda | (automatic, under 40rem of its own width) | A row a day that holds something (and today): the weekday and number in a 40 px column, the items beside; outside days and empty days hidden, the weekday names and the tint gone |
|
|
44
|
+
|
|
45
|
+
| Tone | Class | Mark |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| First (default) | `__item` | navy (`--color-text-heading`) |
|
|
48
|
+
| Second | `__item--2` | teal (`--ws-chart-finished`) |
|
|
49
|
+
| Third | `__item--3` | purple (`--ws-chart-past-half`) |
|
|
50
|
+
| Waits on the person | `__item--needs` | salmon (`--ws-chart-under-half`) |
|
|
51
|
+
| A milestone | `__item--milestone` | a diamond in the item's tone |
|
|
52
|
+
|
|
53
|
+
A tone token passed as `--tone` on an item wins over its class (a fourth category, a palette's own). The tones follow
|
|
54
|
+
the palette: never a hex.
|
|
55
|
+
|
|
56
|
+
## States
|
|
57
|
+
|
|
58
|
+
- **Today**: ringed in every view; in the agenda it stays even with nothing on it.
|
|
59
|
+
- **Hover** (an item that opens): North's wash over the sunk cream, stronger on press; never a lift.
|
|
60
|
+
- **Focus**: the house ring, drawn inside the item so a day's edge never cuts it.
|
|
61
|
+
- **Loading**: the grid drawn, a `ws-skeleton ws-skeleton--line` or two in each day where items will land.
|
|
62
|
+
- **Empty**: a month with nothing in it is still a grid; say why in one line under the bar ("Nothing scheduled in
|
|
63
|
+
November"). A view with no data at all is an `EmptyState` in the card instead.
|
|
64
|
+
- **Touch**: an item is 40 tall (a boxed control, K10); the steps' icon buttons keep a 40 px hit area.
|
|
65
|
+
- **Dark and the navy ground**: from the tokens; the lines, the sunk cream and the tint all turn.
|
|
66
|
+
|
|
67
|
+
## Behaviour
|
|
68
|
+
|
|
69
|
+
- No script in the kit: the app renders the days and items and wires the steps, "Today", the switch and "N more".
|
|
70
|
+
- Keyboard: items are links or buttons in document order, day after day; the steps and the switch are buttons. The
|
|
71
|
+
title carries `aria-live="polite"`, so a step reads the new period aloud.
|
|
72
|
+
- Screen reader: the days are an ordered list named for the period (`aria-label="November 2026"`); each day's number
|
|
73
|
+
carries its full date in a `ws-sr` span ("Tuesday, November 10"), the weekday names are `aria-hidden`.
|
|
74
|
+
- The agenda is a container query on the calendar's own width (`container: ws-calendar / inline-size`), so a calendar
|
|
75
|
+
in a side panel on a wide screen becomes an agenda too.
|
|
76
|
+
|
|
77
|
+
## Content
|
|
78
|
+
|
|
79
|
+
- The period in the title: "November 2026", "Nov 9 to 15, 2026". Weekday names short ("Mon").
|
|
80
|
+
- An item's words are what happens, in the app's own words ("Email 2: the how-to", "Webinar: the new portal"), never
|
|
81
|
+
its id or a status code; a gate or a launch is a milestone.
|
|
82
|
+
- "N more", never "+N" or "...".
|
|
83
|
+
- No em dashes.
|
|
84
|
+
|
|
85
|
+
## Props
|
|
86
|
+
|
|
87
|
+
| Name | Type | Meaning |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| `view` | `"month" \| "week"` | Which grid |
|
|
90
|
+
| `days` | `{ date, outside?, lit?, items: { id, label, tone?, milestone?, href? }[] }[]` | The days in order, Monday first; 42 for a month, 7 for a week |
|
|
91
|
+
| `today` | `string` | `YYYY-MM-DD`, ringed |
|
|
92
|
+
| `perDay` | `number` | Items shown in a month day before "N more" (3) |
|
|
93
|
+
| `onStep` | `(direction: -1 \| 1 \| 0) => void` | Previous, next, or back to today |
|
|
94
|
+
| `onView` | `(view) => void` | The switch |
|
|
95
|
+
|
|
96
|
+
## Rules
|
|
97
|
+
|
|
98
|
+
- **Monday first**, seven equal columns, every week the same height in a month.
|
|
99
|
+
- **Today is the one salmon mark**: its ring and number in the needs step. An item that waits on the person may take the
|
|
100
|
+
needs tone; nothing else is salmon.
|
|
101
|
+
- **Items are words**: never a block of colour with no label, never a hue on the day itself beyond the one lit period.
|
|
102
|
+
- **A month shows three items a day**, then "N more"; the week shows them all.
|
|
103
|
+
- **Hover is a tint**; nothing lifts, nothing grows.
|
|
104
|
+
|
|
105
|
+
## Source
|
|
106
|
+
|
|
107
|
+
- **Derived from**: `DateInput`'s month grid (Monday first, today's ring in the needs step), `StreakCalendar`'s
|
|
108
|
+
weekday letters, the house item and wash rules, Seth's 0.5 px lines.
|
|
109
|
+
- **Precedent**: 0.5.3 (Nov 2026), from the field: three Marketing OS screens (campaign tasks, the content calendar and
|
|
110
|
+
the touch calendar) laid out work on a hand-built month and week grid for want of one (issue #23).
|
|
111
|
+
|
|
112
|
+
## Markup
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<section class="ws-card ws-card--inset ws-calendar" aria-labelledby="cal-title">
|
|
116
|
+
<div class="ws-calendar__bar">
|
|
117
|
+
<h3 class="ws-section ws-calendar__title" id="cal-title" aria-live="polite">November 2026</h3>
|
|
118
|
+
<div class="ws-calendar__steps">
|
|
119
|
+
<button type="button" class="ws-icon-btn ws-icon-btn--sm" aria-label="Previous month"><svg>...</svg></button>
|
|
120
|
+
<button type="button" class="ws-btn ws-btn--secondary ws-btn--sm">Today</button>
|
|
121
|
+
<button type="button" class="ws-icon-btn ws-icon-btn--sm" aria-label="Next month"><svg>...</svg></button>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="ws-segmented" role="group" aria-label="Calendar view">
|
|
124
|
+
<button type="button" class="ws-segmented__item" aria-pressed="true">Month</button>
|
|
125
|
+
<button type="button" class="ws-segmented__item" aria-pressed="false">Week</button>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="ws-calendar__weekdays" aria-hidden="true"><span>Mon</span><!-- Tue to Sun --></div>
|
|
129
|
+
<ol class="ws-calendar__days" aria-label="November 2026">
|
|
130
|
+
<li class="ws-calendar__day ws-calendar__day--outside">
|
|
131
|
+
<div class="ws-calendar__day-head"><span class="ws-calendar__weekday" aria-hidden="true">Mon</span><time class="ws-calendar__date" datetime="2026-10-26"><span class="ws-sr">Monday, October </span>26</time></div>
|
|
132
|
+
</li>
|
|
133
|
+
<li class="ws-calendar__day">
|
|
134
|
+
<div class="ws-calendar__day-head"><span class="ws-calendar__weekday" aria-hidden="true">Tue</span><time class="ws-calendar__date" datetime="2026-11-10" aria-current="date"><span class="ws-sr">Tuesday, November </span>10</time></div>
|
|
135
|
+
<ul class="ws-calendar__items">
|
|
136
|
+
<li><a class="ws-calendar__item ws-calendar__item--needs" href="#" title="Walk Stephen through the demo"><span>Walk Stephen through the demo</span></a></li>
|
|
137
|
+
<li><a class="ws-calendar__item ws-calendar__item--milestone" href="#" title="Gate 2: copy signed off"><span>Gate 2: copy signed off</span></a></li>
|
|
138
|
+
</ul>
|
|
139
|
+
<button type="button" class="ws-btn ws-btn--text ws-calendar__more">2 more</button>
|
|
140
|
+
</li>
|
|
141
|
+
</ol>
|
|
142
|
+
</section>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Styles
|
|
146
|
+
|
|
147
|
+
`design-system/components/Calendar/style.css`, in `bundle.css` and `fello.css` as `.ws-calendar`. Every value is a
|
|
148
|
+
token; the tones follow the palette. The agenda is a container query (`ws-calendar`, under 40rem).
|