@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
|
@@ -16,7 +16,13 @@
|
|
|
16
16
|
(never cut, never scrolled sideways); one line is 32 tall. */
|
|
17
17
|
.ws-combobox__option { display: block; padding: calc((var(--ws-combobox-row, var(--ws-control-h)) - var(--space-md) - var(--space-xxs)) / 2) var(--space-sm); border-radius: var(--ws-radius-row); font-size: var(--ws-text-body); line-height: calc(var(--space-md) + var(--space-xxs)); cursor: pointer; }
|
|
18
18
|
@media (hover: hover) { .ws-combobox__option:hover { background: var(--ws-hover); } }
|
|
19
|
-
|
|
19
|
+
/* Two marks that never look alike (#19): the keyboard's row is the sunk cream; the chosen row carries Menu's tick, 16 px
|
|
20
|
+
navy at its end, centred on its first line, so a list that opens on the current value and is then walked with the
|
|
21
|
+
arrows says which is which. A list of several choices (aria-multiselectable) ticks each one. The tick is a mask, so
|
|
22
|
+
the app's markup does not change. */
|
|
23
|
+
.ws-combobox__option--active { background: var(--ws-surface-sunk); }
|
|
24
|
+
.ws-combobox__option[aria-selected="true"] { --ws-combobox-tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E"); position: relative; padding-inline-end: calc(var(--space-sm) + var(--ws-icon) + var(--space-xs)); }
|
|
25
|
+
.ws-combobox__option[aria-selected="true"]::after { content: ""; position: absolute; top: calc((var(--ws-combobox-row, var(--ws-control-h)) - var(--ws-icon)) / 2); right: var(--space-sm); width: var(--ws-icon); height: var(--ws-icon); background: var(--color-text-heading); -webkit-mask: var(--ws-combobox-tick) center / contain no-repeat; mask: var(--ws-combobox-tick) center / contain no-repeat; }
|
|
20
26
|
.ws-combobox__match { font-weight: 500; color: var(--color-text-heading); }
|
|
21
27
|
/* pre-wrap keeps the rest's leading space (" Street") and still wraps a long one. */
|
|
22
28
|
.ws-combobox__rest { color: var(--color-text-body); white-space: pre-wrap; }
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
People commenting on a thing: remarks anchored to a piece of content, with replies, @mentions, an open or resolved
|
|
2
|
+
state with a count, and a small composer.
|
|
3
|
+
|
|
4
|
+
## Use it when
|
|
5
|
+
|
|
6
|
+
- People review something together where it lives: a brief's section, a line of copy, a task, a design.
|
|
7
|
+
- A remark points at a part of the thing (a quoted stretch) or at the whole of it (a task's comments).
|
|
8
|
+
|
|
9
|
+
## Not for
|
|
10
|
+
|
|
11
|
+
- Talking to an agent: `ChatTurn`, `ChatCard` and `Composer`.
|
|
12
|
+
- A history of what happened to a thing: `Timeline`.
|
|
13
|
+
- A change to text to accept or not: `TextDiff`.
|
|
14
|
+
|
|
15
|
+
## Anatomy
|
|
16
|
+
|
|
17
|
+
- **The list** (`ws-comments`, a `<section>` with its name): the head (`__head`) holds "Comments" (`ws-section`), the
|
|
18
|
+
open count in the meta words ("2 open") and, at the right, a text act for the resolved ones ("Show 1 resolved").
|
|
19
|
+
Then the threads (`__list`, an `<ol>`), open first, newest first; then the composer.
|
|
20
|
+
- **A thread** (`ws-comment`, an `<li>`): divided from the next by the full-width hairline (`--ws-divider`), 12 above
|
|
21
|
+
and below.
|
|
22
|
+
- What it is on (`__anchor`, when it points at a stretch): the stretch's words quoted on the sunk plate, two lines
|
|
23
|
+
at most, 13 / 16; the whole quote a button that brings the content into view and marks it. A hidden "On: " names it.
|
|
24
|
+
- The first remark, then its replies (`__replies`, one level only), each a `__remark`.
|
|
25
|
+
- **A remark** (`__remark`): a 28 px `Avatar` (`ws-avatar--sm`), 8 from everything else in the words' column:
|
|
26
|
+
- The head (`__head`, 28 tall, on the avatar's line): the name (`__name`, 13 / 18 medium navy, one line), when
|
|
27
|
+
(`ws-meta`: "2h ago", "Yesterday", "Nov 8"), "Edited" when it was; at the end, the remark's own more act (an extra
|
|
28
|
+
small `IconButton`: Edit and Delete in a `Menu`), on the person's own remarks only.
|
|
29
|
+
- The words (`__body`, `ws-body`), line breaks kept.
|
|
30
|
+
- The acts (`__acts`, the meta size, 12 apart): Reply, and Resolve on a thread's first remark. Quiet text buttons.
|
|
31
|
+
- **A mention** (`ws-mention`): the person's name with its @, medium, in the link's mid blue. **A mention of you**
|
|
32
|
+
(`ws-mention--you`): the needs word on the needs tint, since it waits on you.
|
|
33
|
+
- **The composer** (`__composer`): the house `TextArea` (its label hidden but present), then a foot (`__composer-foot`)
|
|
34
|
+
with a hint in the meta words and the acts at the end: "Comment" is the list's one navy button; a reply's composer
|
|
35
|
+
takes Cancel (ghost) and Reply (secondary), so a page never holds two navy acts.
|
|
36
|
+
- **The mention picker**: typing @ opens a `Menu` as a listbox under the field, the people who can see the thing, each
|
|
37
|
+
a row with their avatar; the keyboard's row on the sunk ground (`ws-menu-item--current`). The field is its combobox
|
|
38
|
+
(`aria-expanded`, `aria-controls`, `aria-activedescendant`).
|
|
39
|
+
- **On the content** (`ws-comment-mark`, a `<mark>`): the stretch a thread is on wears the blue card tint
|
|
40
|
+
(`--ws-card-tint-blue`); a step stronger while its thread is the open one (`aria-current="true"`).
|
|
41
|
+
|
|
42
|
+
## States
|
|
43
|
+
|
|
44
|
+
- **Open thread**: as above.
|
|
45
|
+
- **Current** (`aria-current="true"` on the thread): the thread on the blue card tint, its mark on the content a step
|
|
46
|
+
stronger; the list scrolls it into view.
|
|
47
|
+
- **Resolved** (`ws-comment--resolved`): hidden behind "Show 1 resolved" until asked for; shown, its words muted, a
|
|
48
|
+
meta line "Resolved by Dana Okafor, Nov 9" and Reopen in its acts; its mark leaves the content.
|
|
49
|
+
- **Replying**: the reply's composer opens under that thread's last reply, focused, the person replied to already
|
|
50
|
+
mentioned when it is someone else.
|
|
51
|
+
- **Saving**: the remark shows at once with "Sending" in its meta; a failed send keeps the words in the composer with
|
|
52
|
+
the error under it and a Retry act.
|
|
53
|
+
- **Deleted**: a remark with replies keeps its place as "This comment was deleted." in the meta words; one without
|
|
54
|
+
replies goes.
|
|
55
|
+
- **Empty**: "No comments yet. Mention someone with @ to ask them about this task." in the meta words, then the
|
|
56
|
+
composer.
|
|
57
|
+
- **Read only**: no composer, no Reply, Resolve or more acts.
|
|
58
|
+
- **Long name**: one line, cut with an ellipsis, the full name in `title`. Long words wrap anywhere.
|
|
59
|
+
- **Dark**: the tints, the plate and the hairline take their dark values.
|
|
60
|
+
|
|
61
|
+
## Behaviour
|
|
62
|
+
|
|
63
|
+
- Keyboard: Tab moves through each thread's quote, acts and more act in order. In the composer, Enter is a new line
|
|
64
|
+
and Cmd or Ctrl Enter sends; in the picker, up and down move, Enter picks, Escape closes the list (a second Escape
|
|
65
|
+
leaves the field).
|
|
66
|
+
- Pressing a quote brings the content into view and marks it current; pressing a mark on the content opens its
|
|
67
|
+
thread in the list. Each moves focus to the other's start.
|
|
68
|
+
- Resolve and Reopen act at once with an Undo in the confirmation (Patterns: undo beats confirm). Delete asks first
|
|
69
|
+
(`ConfirmDialog`), since others may have answered.
|
|
70
|
+
- Motion: a new remark rises in (`ws-arrive`); a resolved thread folds away (`ws-unfold`). Still under reduced motion.
|
|
71
|
+
|
|
72
|
+
## Content
|
|
73
|
+
|
|
74
|
+
- Times are relative under a day ("2h ago"), then the day ("Yesterday", "Nov 8").
|
|
75
|
+
- The composer's placeholder says how to mention: "Add a comment. Type @ to mention someone."
|
|
76
|
+
- No em dashes; the person's own words are shown as they wrote them.
|
|
77
|
+
|
|
78
|
+
## Rules
|
|
79
|
+
|
|
80
|
+
- One level of replies. A reply to a reply joins the same thread, mentioning who it answers.
|
|
81
|
+
- Mentions are names in mid blue, never a navy pill; only a mention of you takes a ground, the needs tint.
|
|
82
|
+
- One navy act in the list: the composer's Comment.
|
|
83
|
+
- The mark on the content is a tint, never an underline: an underline at rest is not a kit look.
|
|
84
|
+
|
|
85
|
+
## Source
|
|
86
|
+
|
|
87
|
+
- **Derived from**: `Avatar`, `TextArea`, `Menu` (as a listbox), `Plate`'s sunk ground, the needs tint for what waits on
|
|
88
|
+
you, the blue card tint (`Chip`'s middle rating ground), Patterns' undo over confirm.
|
|
89
|
+
- **Precedent**: Marketing OS (ContentOS): task comments, brief comments and the review comments panel, composer and
|
|
90
|
+
tip, each hand-built from tokens (issue #26, Madan, 10 Nov 2026).
|
|
91
|
+
|
|
92
|
+
## Markup
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<section class="ws-comments" aria-labelledby="comments-title">
|
|
96
|
+
<div class="ws-comments__head">
|
|
97
|
+
<div class="ws-cluster"><h3 class="ws-section" id="comments-title">Comments</h3><span class="ws-meta">2 open</span></div>
|
|
98
|
+
<button type="button" class="ws-btn ws-btn--text">Show 1 resolved</button>
|
|
99
|
+
</div>
|
|
100
|
+
<ol class="ws-comments__list">
|
|
101
|
+
<li class="ws-comment" aria-current="true">
|
|
102
|
+
<button type="button" class="ws-comment__anchor"><span class="ws-sr">On: </span><span>Spring is still the busiest season</span></button>
|
|
103
|
+
<div class="ws-comment__remark">
|
|
104
|
+
<span class="ws-avatar ws-avatar--plain ws-avatar--sm" aria-hidden="true">DO</span>
|
|
105
|
+
<div class="ws-comment__head">
|
|
106
|
+
<span class="ws-comment__name">Dana Okafor</span>
|
|
107
|
+
<span class="ws-meta">2h ago</span>
|
|
108
|
+
</div>
|
|
109
|
+
<p class="ws-body ws-comment__body"><span class="ws-mention">@Sam Rivera</span> can we back "busiest" with last spring's numbers?</p>
|
|
110
|
+
<div class="ws-comment__acts">
|
|
111
|
+
<button type="button" class="ws-btn ws-btn--text">Reply</button>
|
|
112
|
+
<button type="button" class="ws-btn ws-btn--text">Resolve</button>
|
|
113
|
+
</div>
|
|
114
|
+
<ol class="ws-comment__replies">
|
|
115
|
+
<li class="ws-comment__remark">...</li>
|
|
116
|
+
</ol>
|
|
117
|
+
</div>
|
|
118
|
+
</li>
|
|
119
|
+
</ol>
|
|
120
|
+
<div class="ws-comments__composer">
|
|
121
|
+
<label class="ws-sr" for="new-comment">New comment</label>
|
|
122
|
+
<textarea class="ws-textarea" id="new-comment" placeholder="Add a comment. Type @ to mention someone."></textarea>
|
|
123
|
+
<div class="ws-comments__composer-foot">
|
|
124
|
+
<span class="ws-meta">Whoever you mention gets a message</span>
|
|
125
|
+
<button type="button" class="ws-btn ws-btn--primary">Comment</button>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</section>
|
|
129
|
+
|
|
130
|
+
<!-- On the content -->
|
|
131
|
+
<p class="ws-body"><mark class="ws-comment-mark" aria-current="true">Spring is still the busiest season</mark> for listings.</p>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Styles
|
|
135
|
+
|
|
136
|
+
`design-system/components/CommentThread/style.css`: `ws-comments` (`__head`, `__list`, `__composer`,
|
|
137
|
+
`__composer-foot`), `ws-comment` (`--resolved`, `__anchor`, `__remark`, `__head`, `__name`, `__body`, `__acts`,
|
|
138
|
+
`__replies`), `ws-mention` (`--you`), `ws-comment-mark`.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
<!-- @dsCard group="People" height=760 -->
|
|
2
|
+
<!doctype html>
|
|
3
|
+
<html>
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<title>CommentThread</title>
|
|
7
|
+
<link rel="stylesheet" href="../../tokens.css">
|
|
8
|
+
<link rel="stylesheet" href="../bundle.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div class="ws-stage">
|
|
12
|
+
<div class="ws-stack" style="width: 340px;">
|
|
13
|
+
<span class="ws-muted">On the content: the stretch a thread is on, a step stronger while that thread is open</span>
|
|
14
|
+
<div class="ws-card ws-card--pad-md ws-stack">
|
|
15
|
+
<h3 class="ws-section">Headline</h3>
|
|
16
|
+
<p class="ws-body"><mark class="ws-comment-mark" aria-current="true">Spring is still the busiest season for listings in Ohio</mark>, and buyers who get pre-approved by February <mark class="ws-comment-mark">close first</mark>.</p>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="ws-stack" style="width: 380px;">
|
|
21
|
+
<span class="ws-muted">The threads beside it: open first, a reply one level in, a mention of you on the needs tint, one resolved</span>
|
|
22
|
+
<div class="ws-card ws-card--pad-md">
|
|
23
|
+
<section class="ws-comments" aria-labelledby="ct-title">
|
|
24
|
+
<div class="ws-comments__head">
|
|
25
|
+
<div class="ws-cluster">
|
|
26
|
+
<h3 class="ws-section" id="ct-title">Comments</h3>
|
|
27
|
+
<span class="ws-meta">2 open</span>
|
|
28
|
+
</div>
|
|
29
|
+
<button type="button" class="ws-btn ws-btn--text">Show 1 resolved</button>
|
|
30
|
+
</div>
|
|
31
|
+
<ol class="ws-comments__list">
|
|
32
|
+
<li class="ws-comment" aria-current="true">
|
|
33
|
+
<button type="button" class="ws-comment__anchor"><span class="ws-sr">On: </span><span>Spring is still the busiest season for listings in Ohio</span></button>
|
|
34
|
+
<div class="ws-comment__remark">
|
|
35
|
+
<span class="ws-avatar ws-avatar--plain ws-avatar--sm" aria-hidden="true">DO</span>
|
|
36
|
+
<div class="ws-comment__head">
|
|
37
|
+
<span class="ws-comment__name">Dana Okafor</span>
|
|
38
|
+
<span class="ws-meta">2h ago</span>
|
|
39
|
+
<button type="button" class="ws-icon-btn ws-icon-btn--xs" aria-label="More for Dana's comment"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><circle cx="8" cy="3.6" r="0.9" fill="currentColor"/><circle cx="8" cy="8" r="0.9" fill="currentColor"/><circle cx="8" cy="12.4" r="0.9" fill="currentColor"/></svg></button>
|
|
40
|
+
</div>
|
|
41
|
+
<p class="ws-body ws-comment__body"><span class="ws-mention">@Sam Rivera</span> can we back "busiest" with last spring's numbers?</p>
|
|
42
|
+
<div class="ws-comment__acts">
|
|
43
|
+
<button type="button" class="ws-btn ws-btn--text">Reply</button>
|
|
44
|
+
<button type="button" class="ws-btn ws-btn--text">Resolve</button>
|
|
45
|
+
</div>
|
|
46
|
+
<ol class="ws-comment__replies">
|
|
47
|
+
<li class="ws-comment__remark">
|
|
48
|
+
<span class="ws-avatar ws-avatar--plain ws-avatar--sm" aria-hidden="true">SR</span>
|
|
49
|
+
<div class="ws-comment__head">
|
|
50
|
+
<span class="ws-comment__name">Sam Rivera</span>
|
|
51
|
+
<span class="ws-meta">1h ago</span>
|
|
52
|
+
<span class="ws-meta">Edited</span>
|
|
53
|
+
</div>
|
|
54
|
+
<p class="ws-body ws-comment__body">Listings rose 18% from March to May. <span class="ws-mention ws-mention--you">@Amit Bhandary</span> fine to cite the MLS?</p>
|
|
55
|
+
<div class="ws-comment__acts">
|
|
56
|
+
<button type="button" class="ws-btn ws-btn--text">Reply</button>
|
|
57
|
+
</div>
|
|
58
|
+
</li>
|
|
59
|
+
</ol>
|
|
60
|
+
</div>
|
|
61
|
+
</li>
|
|
62
|
+
<li class="ws-comment">
|
|
63
|
+
<button type="button" class="ws-comment__anchor"><span class="ws-sr">On: </span><span>close first</span></button>
|
|
64
|
+
<div class="ws-comment__remark">
|
|
65
|
+
<span class="ws-avatar ws-avatar--plain ws-avatar--sm" aria-hidden="true">PN</span>
|
|
66
|
+
<div class="ws-comment__head">
|
|
67
|
+
<span class="ws-comment__name">Priya Nair</span>
|
|
68
|
+
<span class="ws-meta">Yesterday</span>
|
|
69
|
+
</div>
|
|
70
|
+
<p class="ws-body ws-comment__body">Too strong for a headline. "Tend to close first"?</p>
|
|
71
|
+
<div class="ws-comment__acts">
|
|
72
|
+
<button type="button" class="ws-btn ws-btn--text">Reply</button>
|
|
73
|
+
<button type="button" class="ws-btn ws-btn--text">Resolve</button>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</li>
|
|
77
|
+
<li class="ws-comment ws-comment--resolved">
|
|
78
|
+
<div class="ws-comment__remark">
|
|
79
|
+
<span class="ws-avatar ws-avatar--plain ws-avatar--sm ws-avatar--dim" aria-hidden="true">TL</span>
|
|
80
|
+
<div class="ws-comment__head">
|
|
81
|
+
<span class="ws-comment__name">Theo Lang</span>
|
|
82
|
+
<span class="ws-meta">Nov 8</span>
|
|
83
|
+
</div>
|
|
84
|
+
<p class="ws-body ws-comment__body">Swap the stock photo for one of an Ohio street.</p>
|
|
85
|
+
<p class="ws-meta">Resolved by Dana Okafor, Nov 9</p>
|
|
86
|
+
<div class="ws-comment__acts">
|
|
87
|
+
<button type="button" class="ws-btn ws-btn--text">Reopen</button>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</li>
|
|
91
|
+
</ol>
|
|
92
|
+
<div class="ws-comments__composer">
|
|
93
|
+
<label class="ws-sr" for="ct-new">New comment</label>
|
|
94
|
+
<textarea class="ws-textarea" id="ct-new" placeholder="Add a comment. Type @ to mention someone."></textarea>
|
|
95
|
+
<div class="ws-comments__composer-foot">
|
|
96
|
+
<span class="ws-meta">Whoever you mention gets a message</span>
|
|
97
|
+
<button type="button" class="ws-btn ws-btn--primary">Comment</button>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</section>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div class="ws-stack" style="width: 340px;">
|
|
105
|
+
<span class="ws-muted">Mentioning: typing @ opens the people who can see it, the keyboard's row on the sunk ground</span>
|
|
106
|
+
<div class="ws-card ws-card--pad-md ws-comments__composer">
|
|
107
|
+
<label class="ws-sr" for="ct-reply">Reply to Dana</label>
|
|
108
|
+
<textarea class="ws-textarea ws-textarea--typing" id="ct-reply" role="combobox" aria-expanded="true" aria-controls="ct-people" aria-activedescendant="ct-p1">Thanks, @sa</textarea>
|
|
109
|
+
<div class="ws-menu" role="listbox" id="ct-people" aria-label="People">
|
|
110
|
+
<div class="ws-menu-item ws-menu-item--current" role="option" id="ct-p1" aria-selected="true"><span class="ws-avatar ws-avatar--plain ws-avatar--sm" aria-hidden="true">SR</span>Sam Rivera</div>
|
|
111
|
+
<div class="ws-menu-item" role="option" id="ct-p2"><span class="ws-avatar ws-avatar--plain ws-avatar--sm" aria-hidden="true">SK</span>Sara Kim</div>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="ws-comments__composer-foot">
|
|
114
|
+
<span class="ws-meta">Enter picks, Escape closes the list</span>
|
|
115
|
+
<div class="ws-cluster">
|
|
116
|
+
<button type="button" class="ws-btn ws-btn--ghost">Cancel</button>
|
|
117
|
+
<button type="button" class="ws-btn ws-btn--secondary">Reply</button>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="ws-stack" style="width: 340px;">
|
|
124
|
+
<span class="ws-muted">No comments yet</span>
|
|
125
|
+
<div class="ws-card ws-card--pad-md">
|
|
126
|
+
<section class="ws-comments" aria-label="Comments">
|
|
127
|
+
<h3 class="ws-section">Comments</h3>
|
|
128
|
+
<p class="ws-meta">No comments yet. Mention someone with @ to ask them about this task.</p>
|
|
129
|
+
</section>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</body>
|
|
134
|
+
</html>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* ---- CommentThread: people commenting on a thing (a brief's section, a line of copy, a task). Threads in a list,
|
|
2
|
+
divided by the full-width hairline: the quote of what each is on, its first remark, its replies one level in under the
|
|
3
|
+
words' column, the acts (Reply, Resolve) in the meta size. A remark is a 28 px avatar beside its name, its time and
|
|
4
|
+
its words. A mention of someone is their name in the link's mid blue; a mention of you is on the needs tint, since it
|
|
5
|
+
waits on you. On the content, a commented stretch wears the blue card tint, a step stronger while its thread is the
|
|
6
|
+
one open. ---- */
|
|
7
|
+
.ws-comments { display: flex; flex-direction: column; gap: var(--space-sm); min-width: 0; }
|
|
8
|
+
.ws-comments__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-xs); }
|
|
9
|
+
.ws-comments__list { display: flex; flex-direction: column; }
|
|
10
|
+
.ws-comment { display: flex; flex-direction: column; gap: var(--space-xs); padding: var(--space-sm) 0; border-top: var(--ws-divider); }
|
|
11
|
+
.ws-comment:last-child { border-bottom: var(--ws-divider); }
|
|
12
|
+
/* The thread open beside its content: the blue card tint, the rows' 12 px sides so its words do not touch the edge. */
|
|
13
|
+
.ws-comment[aria-current="true"] { margin: 0 calc(var(--space-sm) * -1); padding: var(--space-sm); border-radius: var(--ws-radius-control); background: var(--ws-card-tint-blue); }
|
|
14
|
+
/* What the thread is on: its words quoted on the sunk plate (as ChatCard quotes a message), two lines at most, the
|
|
15
|
+
whole quote a button that brings the content into view. */
|
|
16
|
+
.ws-comment__anchor { display: block; width: 100%; padding: var(--space-xxs) var(--space-xs); border: 0; border-radius: var(--ws-radius-control); background: var(--ws-surface-sunk); color: var(--color-text-body); font: inherit; font-size: var(--ws-text-meta); line-height: var(--space-md); text-align: start; cursor: pointer; transition: background-color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
17
|
+
.ws-comment__anchor > span { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; }
|
|
18
|
+
@media (pointer: coarse) { .ws-comment__anchor { min-height: var(--ws-touch); } }
|
|
19
|
+
@media (hover: hover) { .ws-comment__anchor:hover { background-image: linear-gradient(var(--ws-hover), var(--ws-hover)); } }
|
|
20
|
+
/* A remark: the avatar, then everything else in the words' column. */
|
|
21
|
+
.ws-comment__remark { display: grid; grid-template-columns: var(--ws-control-h-sm) minmax(0, 1fr); column-gap: var(--space-xs); row-gap: var(--space-xxs); }
|
|
22
|
+
.ws-comment__remark > :not(.ws-avatar) { grid-column: 2; }
|
|
23
|
+
.ws-comment__head { display: flex; align-items: center; gap: var(--space-xs); min-height: var(--ws-control-h-sm); min-width: 0; }
|
|
24
|
+
.ws-comment__name { overflow: hidden; color: var(--color-text-heading); font-size: var(--ws-text-control); font-weight: 500; line-height: var(--ws-leading-control); text-overflow: ellipsis; white-space: nowrap; }
|
|
25
|
+
.ws-comment__head > .ws-icon-btn { margin-inline-start: auto; }
|
|
26
|
+
.ws-comment__body { overflow-wrap: anywhere; white-space: pre-line; }
|
|
27
|
+
.ws-comment__acts { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); font-size: var(--ws-text-meta); line-height: var(--space-md); }
|
|
28
|
+
.ws-comment__replies { display: flex; flex-direction: column; gap: var(--space-sm); padding-top: var(--space-xs); }
|
|
29
|
+
/* Resolved: the words step back to muted; a meta line says who resolved it and when, Reopen in the acts. */
|
|
30
|
+
.ws-comment--resolved .ws-comment__body { color: var(--color-text-muted); }
|
|
31
|
+
/* Mentions inside a remark's words or the composer's preview. */
|
|
32
|
+
.ws-mention { color: var(--color-text-interactive); font-weight: 500; }
|
|
33
|
+
.ws-mention--you { padding: 0 var(--space-3xs); border-radius: calc(var(--ws-radius-control) / 2); background: var(--ws-tint-needs); color: var(--ws-word-needs); }
|
|
34
|
+
/* The composer: the house textarea, then a foot with the hint and its acts. */
|
|
35
|
+
.ws-comments__composer { display: flex; flex-direction: column; gap: var(--space-xs); }
|
|
36
|
+
.ws-comments__composer-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-xs); }
|
|
37
|
+
/* The acts keep to the end even when the hint wraps above them. */
|
|
38
|
+
.ws-comments__composer-foot > :last-child { margin-inline-start: auto; }
|
|
39
|
+
/* On the content: the stretch a thread is on. */
|
|
40
|
+
.ws-comment-mark { border-radius: calc(var(--ws-radius-control) / 2); background: var(--ws-card-tint-blue); color: inherit; }
|
|
41
|
+
.ws-comment-mark[aria-current="true"] { background: color-mix(in srgb, var(--ws-rating-middle) 20%, var(--color-surface)); }
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
A date field that works on its own, with a popover month grid for picking a day by eye instead of typing it
|
|
1
|
+
A date field that works on its own, with a popover month grid for picking a day by eye instead of typing it; the same
|
|
2
|
+
shell holds a time of day, and a date with a time.
|
|
2
3
|
|
|
3
4
|
## Use it when
|
|
4
5
|
|
|
5
6
|
- A form needs a single date (when a call happened, when an offer was sent, a listing's close date).
|
|
6
7
|
- The person may prefer to type the date (a native field, keyboard first) or pick it visually (a calendar).
|
|
7
8
|
- A range between two dates needs a visible span (a report's window, a listing's active dates).
|
|
9
|
+
- Something happens at a time of day (`type="time"`: when a follow-up goes out), or on a day at a time
|
|
10
|
+
(`type="datetime-local"`: when a post, an email or a blog is published).
|
|
8
11
|
|
|
9
12
|
## Not for
|
|
10
13
|
|
|
11
|
-
- A time of day with no date: a dedicated time field is listed as later, alongside this one, until built.
|
|
12
14
|
- A day within the last few weeks shown as a history, not a choice: use `StreakCalendar`.
|
|
13
15
|
- A short, known set of dates shown as options: use `Select` or `Segmented`.
|
|
14
16
|
|
|
@@ -20,6 +22,14 @@ A date field that works on its own, with a popover month grid for picking a day
|
|
|
20
22
|
- Picker: a `ws-menu` card (cream, `--ws-radius-card`, `--ws-shadow-lift`) holding a month grid.
|
|
21
23
|
- Header: 32 px row (`--ws-control-h`), the month name 14 px medium navy, two `xs` ghost `IconButton`s (24 px) at the
|
|
22
24
|
ends for the previous and next month.
|
|
25
|
+
- A far month or year (`__jump`, #20): where a date can be months or years away (bounds that run for decades), the
|
|
26
|
+
month name becomes two small selects (`Select --sm`, Seth's chip select, 28 tall, 12 / 16 medium navy), the month
|
|
27
|
+
and the year, 4 apart between the arrows. Native, so the list types ahead ("2045"), and on touch they are the
|
|
28
|
+
platform's own sheet, 40 tall. A picker for this month and the next few keeps the plain month name.
|
|
29
|
+
- Foot (`__foot`), under `Menu`'s line (`ws-menu-line`): "Today" at the start, which chooses today and closes, and
|
|
30
|
+
"Clear" at the end, only when the date may be left empty, which empties the field and closes. Both `Button ghost
|
|
31
|
+
small` (28 tall, navy words). A required date has no Clear; a picker that opens on this month has no need of
|
|
32
|
+
Today, but keeps it when the field is often far from today.
|
|
23
33
|
- Day letters: 11 px (`--ws-text-micro`), muted, once above the grid, exactly as `StreakCalendar`'s own day-letter row.
|
|
24
34
|
- Day cells: 32 px (`--ws-control-h`), `--ws-radius-control` (4 px) corners. This is a squarer cell than
|
|
25
35
|
`StreakCalendar`'s pill-shaped day cells, because a picker is a month grid for choosing one day, not a strip
|
|
@@ -44,6 +54,9 @@ A date field that works on its own, with a popover month grid for picking a day
|
|
|
44
54
|
| --- | --- | --- |
|
|
45
55
|
| Single date | one chosen day, navy fill | when a call happened, an offer date |
|
|
46
56
|
| Range | two chosen ends with `--ws-surface-sunk` between | a report's window, active listing dates |
|
|
57
|
+
| Time | `type="time"`, a 16 px navy clock at the right; the platform's own picker | when a reminder or a send goes out |
|
|
58
|
+
| Date and time | `type="datetime-local"`, the calendar mark; the platform's own picker | when a post, an email or a blog is published |
|
|
59
|
+
| Jump and foot | the month and the year as selects (`__jump`), Today and Clear under a line (`__foot`) | a plan's dates years apart, an optional milestone date |
|
|
47
60
|
|
|
48
61
|
There is one size; the field expands to 40 px on a touch screen, where the platform's native date sheet may also appear.
|
|
49
62
|
|
|
@@ -52,6 +65,8 @@ There is one size; the field expands to 40 px on a touch screen, where the platf
|
|
|
52
65
|
- **Field alone:** the native input and its calendar icon, no picker open.
|
|
53
66
|
- **Open picker:** the month grid, including today's ring, a muted out-of-month day and a past-disabled day.
|
|
54
67
|
- **A range:** two navy ends with the sunk fill between them.
|
|
68
|
+
- **Jump and foot:** the head's two selects say the shown month and year; choosing one redraws the grid there and keeps
|
|
69
|
+
the focus on the select. Today and Clear act at once and close the picker; focus returns to the field.
|
|
55
70
|
- **Focus:** the kit's focus ring, 2 px navy, 2 px out, as `Input`'s (a field's edge darkens under the pointer).
|
|
56
71
|
- **Error:** the salmon's AA step as a 1 px edge with a message below, exactly as `Input`'s error state.
|
|
57
72
|
- **Hover (a mouse only):** a day takes the navy wash.
|
|
@@ -65,7 +80,8 @@ There is one size; the field expands to 40 px on a touch screen, where the platf
|
|
|
65
80
|
- Keyboard: the native field works on its own (typing digits, native keyboard entry) with no picker at all, which is
|
|
66
81
|
the "native first" half of this component; when the picker is open, arrow keys move between day cells, Enter chooses
|
|
67
82
|
the active cell, Escape closes the picker and returns focus to the field, and the header's buttons (or Page Up/Down)
|
|
68
|
-
move months
|
|
83
|
+
move months; with `__jump`, Tab reaches the month and the year selects after the previous arrow, then the next arrow,
|
|
84
|
+
the grid, Today and Clear.
|
|
69
85
|
- Pointer / touch: clicking a day cell chooses it; the field's own calendar icon opens the picker.
|
|
70
86
|
- Motion: the picker opens as `Menu` does, growing from its top left corner, under the field's left edge (D7).
|
|
71
87
|
- Reduced motion: the picker appears without its rise.
|
|
@@ -89,6 +105,13 @@ There is one size; the field expands to 40 px on a touch screen, where the platf
|
|
|
89
105
|
|
|
90
106
|
## Rules
|
|
91
107
|
|
|
108
|
+
- **A time, and a date with a time, are the native field in this shell** (#27): `Input`'s box, the kit's mark, the
|
|
109
|
+
platform's picker (a wheel or a list on a phone, typed segments on a desktop). No kit picker draws hours: a time is
|
|
110
|
+
typed or wheeled faster than it is picked by eye. A date with a time that needs the month grid (a date far off, a
|
|
111
|
+
range) is two fields side by side in one row, the date first: a `DateInput` and a time, each with its own label.
|
|
112
|
+
- The time's value is the person's own time zone unless the field says otherwise: when it is not (a send in the
|
|
113
|
+
recipient's zone), the zone is named in the label or the hint ("Send at, Eastern"), never left to guess.
|
|
114
|
+
|
|
92
115
|
- 08-choosing-components.md previously read: "Pick a date or a time | a native input until a picker is built | listed
|
|
93
116
|
as later." This is that picker arriving (26 Oct 2026): the native field stays the whole story on its own, and the
|
|
94
117
|
picker is an enhancement, never a replacement.
|
|
@@ -116,6 +139,11 @@ addition decided from the system, 26 Oct 2026 (filling the "listed as later" lin
|
|
|
116
139
|
<div class="ws-date-input__head">
|
|
117
140
|
<button type="button" class="ws-icon-btn ws-icon-btn--xs" aria-label="Previous month">...</button>
|
|
118
141
|
<span class="ws-date-input__month">October 2026</span>
|
|
142
|
+
<!-- or, to jump far: the month and the year as two small selects -->
|
|
143
|
+
<!-- <span class="ws-date-input__jump">
|
|
144
|
+
<span class="ws-select-wrap"><select class="ws-select ws-select--sm" aria-label="Month">...</select><svg>...</svg></span>
|
|
145
|
+
<span class="ws-select-wrap"><select class="ws-select ws-select--sm" aria-label="Year">...</select><svg>...</svg></span>
|
|
146
|
+
</span> -->
|
|
119
147
|
<button type="button" class="ws-icon-btn ws-icon-btn--xs" aria-label="Next month">...</button>
|
|
120
148
|
</div>
|
|
121
149
|
<div class="ws-date-input__grid">
|
|
@@ -128,6 +156,12 @@ addition decided from the system, 26 Oct 2026 (filling the "listed as later" lin
|
|
|
128
156
|
<button type="button" class="ws-date-input__cell ws-date-input__cell--today" aria-current="date">26</button>
|
|
129
157
|
<button type="button" class="ws-date-input__cell ws-date-input__cell--muted">1</button>
|
|
130
158
|
</div>
|
|
159
|
+
<!-- the foot: Today, and Clear for a date that may be left empty -->
|
|
160
|
+
<div class="ws-menu-line" role="separator"></div>
|
|
161
|
+
<div class="ws-date-input__foot">
|
|
162
|
+
<button type="button" class="ws-btn ws-btn--ghost ws-btn--sm">Today</button>
|
|
163
|
+
<button type="button" class="ws-btn ws-btn--ghost ws-btn--sm">Clear</button>
|
|
164
|
+
</div>
|
|
131
165
|
</div>
|
|
132
166
|
|
|
133
167
|
<!-- a range: two navy ends, the sunk band between them -->
|
|
@@ -153,5 +187,5 @@ addition decided from the system, 26 Oct 2026 (filling the "listed as later" lin
|
|
|
153
187
|
|
|
154
188
|
## Styles
|
|
155
189
|
|
|
156
|
-
`design-system/components/DateInput/style.css`: `ws-date-input`, `__icon`, `__picker`, `__head`, `__month`, `__grid`,
|
|
190
|
+
`design-system/components/DateInput/style.css`: `ws-date-input`, `__icon`, `__picker`, `__head`, `__month`, `__jump`, `__foot`, `__grid`,
|
|
157
191
|
`__letter`, `__cell` and its `--muted`, `--today`, `--chosen`, `--disabled`, `--range`, `--range-start`, `--range-end`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- @dsCard group="Fields" height=
|
|
1
|
+
<!-- @dsCard group="Fields" height=360 -->
|
|
2
2
|
<!doctype html>
|
|
3
3
|
<html>
|
|
4
4
|
<head>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<div class="ws-cluster ws-cluster--component ws-cluster--start">
|
|
13
13
|
|
|
14
14
|
<div class="ws-stack">
|
|
15
|
-
<span class="ws-muted">
|
|
15
|
+
<span class="ws-muted">Fields: a date, in error, a time, a date with a time</span>
|
|
16
16
|
<div class="ws-stack ws-stack--component">
|
|
17
17
|
<label class="ws-field">
|
|
18
18
|
<span class="ws-label">Call date</span>
|
|
@@ -36,6 +36,20 @@
|
|
|
36
36
|
Pick a day after the offer was sent
|
|
37
37
|
</p>
|
|
38
38
|
</div>
|
|
39
|
+
<label class="ws-field">
|
|
40
|
+
<span class="ws-label">Send at</span>
|
|
41
|
+
<span class="ws-date-input">
|
|
42
|
+
<input class="ws-input" type="time" value="09:30">
|
|
43
|
+
<svg class="ws-date-input__icon" 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="5.5"/><path d="M8 5v3l2 1.5"/></svg>
|
|
44
|
+
</span>
|
|
45
|
+
</label>
|
|
46
|
+
<label class="ws-field">
|
|
47
|
+
<span class="ws-label">Publish on</span>
|
|
48
|
+
<span class="ws-date-input">
|
|
49
|
+
<input class="ws-input" type="datetime-local" value="2026-11-12T09:30">
|
|
50
|
+
<svg class="ws-date-input__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="10" rx="1.5"/><path d="M2.5 6.5h11M5 2v3M11 2v3"/></svg>
|
|
51
|
+
</span>
|
|
52
|
+
</label>
|
|
39
53
|
</div>
|
|
40
54
|
</div>
|
|
41
55
|
|
|
@@ -65,6 +79,40 @@
|
|
|
65
79
|
</div>
|
|
66
80
|
</div>
|
|
67
81
|
|
|
82
|
+
<div class="ws-stack" style="min-width: min(100%, 320px);">
|
|
83
|
+
<span class="ws-muted">A far date: month and year selects; Today, Clear</span>
|
|
84
|
+
<div class="ws-menu ws-date-input__picker" role="dialog" aria-label="Choose a publish date">
|
|
85
|
+
<div class="ws-date-input__head">
|
|
86
|
+
<button type="button" class="ws-icon-btn ws-icon-btn--xs" aria-label="Previous month"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 3L6 8l4 5"/></svg></button>
|
|
87
|
+
<span class="ws-date-input__jump">
|
|
88
|
+
<span class="ws-select-wrap"><select class="ws-select ws-select--sm" aria-label="Month"><option>January</option><option>February</option><option>March</option><option>April</option><option>May</option><option>June</option><option>July</option><option>August</option><option>September</option><option selected>October</option><option>November</option><option>December</option></select><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></span>
|
|
89
|
+
<span class="ws-select-wrap"><select class="ws-select ws-select--sm" aria-label="Year"><option>2024</option><option>2025</option><option selected>2026</option><option>2027</option><option>2028</option></select><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></span>
|
|
90
|
+
</span>
|
|
91
|
+
<button type="button" class="ws-icon-btn ws-icon-btn--xs" aria-label="Next month"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3l4 5-4 5"/></svg></button>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="ws-date-input__grid">
|
|
94
|
+
<span class="ws-date-input__letter">M</span><span class="ws-date-input__letter">T</span><span class="ws-date-input__letter">W</span><span class="ws-date-input__letter">T</span><span class="ws-date-input__letter">F</span><span class="ws-date-input__letter">S</span><span class="ws-date-input__letter">S</span>
|
|
95
|
+
<button type="button" class="ws-date-input__cell ws-date-input__cell--muted ws-date-input__cell--disabled" aria-disabled="true">28</button>
|
|
96
|
+
<button type="button" class="ws-date-input__cell ws-date-input__cell--muted ws-date-input__cell--disabled" aria-disabled="true">29</button>
|
|
97
|
+
<button type="button" class="ws-date-input__cell ws-date-input__cell--muted ws-date-input__cell--disabled" aria-disabled="true">30</button>
|
|
98
|
+
<button type="button" class="ws-date-input__cell">1</button><button type="button" class="ws-date-input__cell">2</button><button type="button" class="ws-date-input__cell">3</button><button type="button" class="ws-date-input__cell">4</button>
|
|
99
|
+
<button type="button" class="ws-date-input__cell">5</button><button type="button" class="ws-date-input__cell">6</button><button type="button" class="ws-date-input__cell">7</button><button type="button" class="ws-date-input__cell">8</button><button type="button" class="ws-date-input__cell">9</button><button type="button" class="ws-date-input__cell">10</button><button type="button" class="ws-date-input__cell">11</button>
|
|
100
|
+
<button type="button" class="ws-date-input__cell">12</button><button type="button" class="ws-date-input__cell">13</button><button type="button" class="ws-date-input__cell">14</button><button type="button" class="ws-date-input__cell">15</button><button type="button" class="ws-date-input__cell">16</button><button type="button" class="ws-date-input__cell">17</button><button type="button" class="ws-date-input__cell">18</button>
|
|
101
|
+
<button type="button" class="ws-date-input__cell">19</button><button type="button" class="ws-date-input__cell">20</button><button type="button" class="ws-date-input__cell">21</button>
|
|
102
|
+
<button type="button" class="ws-date-input__cell ws-date-input__cell--chosen" aria-pressed="true">22</button>
|
|
103
|
+
<button type="button" class="ws-date-input__cell">23</button><button type="button" class="ws-date-input__cell">24</button><button type="button" class="ws-date-input__cell">25</button>
|
|
104
|
+
<button type="button" class="ws-date-input__cell ws-date-input__cell--today" aria-current="date">26</button>
|
|
105
|
+
<button type="button" class="ws-date-input__cell">27</button><button type="button" class="ws-date-input__cell">28</button><button type="button" class="ws-date-input__cell">29</button><button type="button" class="ws-date-input__cell">30</button><button type="button" class="ws-date-input__cell">31</button>
|
|
106
|
+
<button type="button" class="ws-date-input__cell ws-date-input__cell--muted">1</button>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="ws-menu-line" role="separator"></div>
|
|
109
|
+
<div class="ws-date-input__foot">
|
|
110
|
+
<button type="button" class="ws-btn ws-btn--ghost ws-btn--sm">Today</button>
|
|
111
|
+
<button type="button" class="ws-btn ws-btn--ghost ws-btn--sm">Clear</button>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
68
116
|
<div class="ws-stack">
|
|
69
117
|
<span class="ws-muted">A range: Oct 12 to Oct 18, sunk fill between two navy ends</span>
|
|
70
118
|
<div class="ws-menu ws-date-input__picker" role="dialog" aria-label="Choose a date range">
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
.ws-menu.ws-date-input__picker { transform-origin: top left; }
|
|
13
13
|
.ws-date-input__head { display: flex; align-items: center; justify-content: space-between; height: var(--ws-control-h); padding: 0 var(--space-xxs); }
|
|
14
14
|
.ws-date-input__month { font-size: var(--ws-text-body); line-height: calc(var(--space-md) + var(--space-xxs)); font-weight: 500; color: var(--color-text-heading); }
|
|
15
|
+
/* A far month or year (#20): the month words become two small selects between the arrows (Seth's chip select), so a
|
|
16
|
+
date years away is one choice, and the native list types ahead ("2045"). */
|
|
17
|
+
.ws-date-input__jump { display: flex; align-items: center; gap: var(--space-xxs); min-width: 0; }
|
|
18
|
+
.ws-date-input__jump > .ws-select-wrap { align-self: center; }
|
|
19
|
+
/* The foot, under Menu's line: Today at the start, Clear at the end for a date that may be left empty. */
|
|
20
|
+
.ws-date-input__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-xs); padding: 0 var(--space-xxs) var(--space-3xs); }
|
|
15
21
|
.ws-date-input__grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--space-3xs) 0; padding: var(--space-xxs); }
|
|
16
22
|
.ws-date-input__letter { text-align: center; font-size: var(--ws-text-micro); line-height: var(--space-md); color: var(--color-text-muted); }
|
|
17
23
|
.ws-date-input__cell { position: relative; display: flex; align-items: center; justify-content: center; height: var(--ws-control-h); padding: 0; border: 0; border-radius: var(--ws-radius-control); background: none; font-size: var(--ws-text-control); line-height: var(--ws-leading-control); color: var(--color-text-heading); font-variant-numeric: tabular-nums; cursor: pointer; transition: background-color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
@@ -41,10 +41,29 @@ A modal, built on the browser's own `<dialog>`: a short task in the centre of th
|
|
|
41
41
|
| `ws-dialog` | In the centre: rises 14 px out of .98 as the scrim fades in |
|
|
42
42
|
| `ws-dialog--drawer` | 440 wide (`--ws-thing-width`), floating 12 in from the window's top, right and bottom edges with 12 px corners; slides in from the right |
|
|
43
43
|
| `ws-dialog--drawer ws-dialog--drawer-start` | The same from the left edge |
|
|
44
|
+
| `ws-dialog--mounted` (with any of the above) | The pane an app mounts as a `div` (Radix Dialog, a portal): fixed over the window on `z-modal`, centred (a drawer keeps its edge), with the same entrance and exit as keyframes. Its scrim is `ws-dialog__scrim`, placed just before it |
|
|
45
|
+
|
|
46
|
+
### Mounted by the app
|
|
47
|
+
|
|
48
|
+
Use the native `<dialog>` where you can: it needs no script. When a library mounts the dialog instead (Radix, for its
|
|
49
|
+
focus trap, or tests under jsdom, which has no `showModal()`), put `ws-dialog ws-dialog--mounted` on the pane and
|
|
50
|
+
`ws-dialog__scrim` on the overlay rendered just before it. Drop the library's own centring (`fixed`, `top-1/2`,
|
|
51
|
+
`-translate-*`): the kit places the pane, and the motion moves it.
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<Dialog.Portal>
|
|
55
|
+
<Dialog.Overlay className="ws-dialog__scrim" />
|
|
56
|
+
<Dialog.Content className="ws-dialog ws-dialog--mounted">{/* ws-head, ws-dialog__content, ws-band */}</Dialog.Content>
|
|
57
|
+
</Dialog.Portal>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Radix marks both `data-state="closed"` as they close and waits for their animations before it unmounts them, so the exit
|
|
61
|
+
plays with no code. Without Radix, set `data-leaving` on both and remove them once their animations end (Motion, "An
|
|
62
|
+
element the app mounts"); under reduced motion there are none, so they go at once.
|
|
44
63
|
|
|
45
64
|
## States
|
|
46
65
|
|
|
47
|
-
- **Closed**: not shown; the dialog element is not open.
|
|
66
|
+
- **Closed**: not shown; the dialog element is not open (mounted: not in the page).
|
|
48
67
|
- **Opening** (North's window, js@540954): the scrim fades in over `--ws-duration-hover` (.2 s); a centre dialog grows
|
|
49
68
|
14 px up out of .98 (`--ws-open-scale-pane`) on the settle spring (`--ws-spring-settle`, .32 / .1); a drawer slides
|
|
50
69
|
in from its edge over `--ws-duration-grow` (.55 s) on `--ws-ease-grow` (North's "Card grows open"). Then its details
|
|
@@ -72,9 +91,13 @@ A modal, built on the browser's own `<dialog>`: a short task in the centre of th
|
|
|
72
91
|
element itself (the scrim).
|
|
73
92
|
- Touch: the close and the acts are 40 tall.
|
|
74
93
|
- Motion: the browser's own `[open]` change with `@starting-style`, `overlay` and `display` kept for the exit, so no
|
|
75
|
-
script is needed. North's numbers: rise 14, exit 8, scale .98.
|
|
94
|
+
script is needed. North's numbers: rise 14, exit 8, scale .98. A mounted pane plays the same moves as keyframes: on
|
|
95
|
+
mount, and on `data-state="closed"` or `data-leaving`; its scrim fades in over `--ws-duration-hover` and out over
|
|
96
|
+
`--ws-duration-exit`.
|
|
97
|
+
- Focus, mounted: the library traps it and gives it back (Radix does both); a pane of the app's own takes `tabindex="-1"`,
|
|
98
|
+
takes focus on open, closes on Escape and on a click on the scrim, and returns focus to what opened it.
|
|
76
99
|
- Reduced motion: it appears and goes at once; so does a browser without `@starting-style`.
|
|
77
|
-
- The preview's last
|
|
100
|
+
- The preview's last two stacks are live: open the dialog and the drawer, natively and mounted.
|
|
78
101
|
- Menus inside it open in the top layer (see `Menu`), so the pane never cuts them.
|
|
79
102
|
|
|
80
103
|
## Content
|