@estiva-app/ui 0.24.0 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ListColumn.d.ts +41 -0
- package/dist/ListColumn.d.ts.map +1 -0
- package/dist/Select.d.ts +1 -0
- package/dist/Select.d.ts.map +1 -1
- package/dist/Toolbar.d.ts +18 -0
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +65 -38
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/registry.json +164 -4
- package/src/AppShell.mdx +8 -1
- package/src/AttachmentCard.mdx +3 -1
- package/src/Avatar.mdx +7 -2
- package/src/Banner.mdx +4 -5
- package/src/Breadcrumb.mdx +2 -0
- package/src/Button.mdx +3 -2
- package/src/Button.tsx +2 -2
- package/src/Card.mdx +5 -1
- package/src/Checkbox.mdx +1 -0
- package/src/ChipInput.mdx +1 -1
- package/src/CollapsibleSection.mdx +3 -3
- package/src/ConfirmDialog.mdx +2 -2
- package/src/ContainerHeader.mdx +12 -3
- package/src/DialogShell.mdx +1 -1
- package/src/Divider.mdx +3 -0
- package/src/EditableText.mdx +6 -1
- package/src/EmptyState.mdx +2 -2
- package/src/Field.mdx +3 -4
- package/src/Form.mdx +1 -1
- package/src/IconButton.mdx +1 -1
- package/src/IdentityMenu.mdx +1 -1
- package/src/InlineChip.mdx +3 -0
- package/src/Kbd.mdx +2 -2
- package/src/Link.mdx +3 -1
- package/src/ListColumn.mdx +87 -0
- package/src/ListColumn.stories.tsx +136 -0
- package/src/ListColumn.test.tsx +50 -0
- package/src/ListColumn.tsx +63 -0
- package/src/Menu.mdx +2 -0
- package/src/MenuItem.mdx +3 -0
- package/src/NavItem.mdx +4 -0
- package/src/Person.mdx +3 -0
- package/src/PersonTrigger.mdx +2 -1
- package/src/PersonTrigger.tsx +1 -1
- package/src/Popover.mdx +2 -0
- package/src/Rail.mdx +3 -0
- package/src/RailItem.mdx +4 -0
- package/src/RailItem.tsx +1 -1
- package/src/Reaction.mdx +1 -0
- package/src/Reaction.tsx +1 -1
- package/src/ScrollArea.mdx +3 -0
- package/src/SectionHeader.mdx +5 -1
- package/src/SectionHeader.tsx +1 -1
- package/src/SectionLabel.mdx +6 -2
- package/src/Select.mdx +3 -2
- package/src/Select.test.tsx +12 -0
- package/src/Select.tsx +4 -3
- package/src/Skeleton.mdx +5 -1
- package/src/Skeleton.tsx +1 -1
- package/src/TextInput.mdx +1 -0
- package/src/TextInput.tsx +1 -1
- package/src/Toolbar.mdx +9 -2
- package/src/Toolbar.stories.tsx +19 -2
- package/src/Toolbar.test.tsx +24 -1
- package/src/Toolbar.tsx +22 -0
- package/src/Tooltip.mdx +2 -1
- package/src/Tooltip.test.tsx +22 -0
- package/src/Tooltip.tsx +1 -1
- package/src/TopBar.mdx +8 -1
- package/src/heights.test.tsx +78 -0
- package/src/index.ts +2 -1
- package/src/registry/registry.test.ts +16 -8
package/package.json
CHANGED
package/registry.json
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
"kind": "package",
|
|
5
5
|
"repo": "estiva-ui",
|
|
6
6
|
"package": "@estiva-app/ui",
|
|
7
|
-
"packageVersion": "0.24.
|
|
8
|
-
"exports":
|
|
9
|
-
"typeExports":
|
|
7
|
+
"packageVersion": "0.24.1",
|
|
8
|
+
"exports": 84,
|
|
9
|
+
"typeExports": 94,
|
|
10
10
|
"files": null
|
|
11
11
|
},
|
|
12
12
|
"storybook": {
|
|
@@ -397,7 +397,7 @@
|
|
|
397
397
|
"kind": "component",
|
|
398
398
|
"importPath": "@estiva-app/ui",
|
|
399
399
|
"sourceFile": "src/Button.tsx",
|
|
400
|
-
"purpose": "The push-button for an action: a verb, clicked once — \"Create\", \"Save\", \"Delete
|
|
400
|
+
"purpose": "The push-button for an action: a verb, clicked once — \"Create\", \"Save\", \"Delete\".",
|
|
401
401
|
"purposeFrom": "page",
|
|
402
402
|
"props": [
|
|
403
403
|
{
|
|
@@ -2395,6 +2395,81 @@
|
|
|
2395
2395
|
"docPage": "src/Link.mdx",
|
|
2396
2396
|
"app": null
|
|
2397
2397
|
},
|
|
2398
|
+
{
|
|
2399
|
+
"name": "ListColumn",
|
|
2400
|
+
"repo": "estiva-ui",
|
|
2401
|
+
"kind": "component",
|
|
2402
|
+
"importPath": "@estiva-app/ui",
|
|
2403
|
+
"sourceFile": "src/ListColumn.tsx",
|
|
2404
|
+
"purpose": "The list column of a page: 290px wide with a line on its right, its name in a `ContainerHeader`, and the list under it, which scrolls.",
|
|
2405
|
+
"purposeFrom": "page",
|
|
2406
|
+
"props": [
|
|
2407
|
+
{
|
|
2408
|
+
"name": "title",
|
|
2409
|
+
"takes": "anything",
|
|
2410
|
+
"required": true,
|
|
2411
|
+
"note": "The column's name, in its header. A node for a title that is more than words."
|
|
2412
|
+
},
|
|
2413
|
+
{
|
|
2414
|
+
"name": "chevron",
|
|
2415
|
+
"takes": "true/false",
|
|
2416
|
+
"required": false,
|
|
2417
|
+
"note": "A chevron after the title, as `ContainerHeader` draws it."
|
|
2418
|
+
},
|
|
2419
|
+
{
|
|
2420
|
+
"name": "actions",
|
|
2421
|
+
"takes": "anything",
|
|
2422
|
+
"required": false,
|
|
2423
|
+
"note": "The column's own actions, at the header's right — a `Toolbar` of `ToolbarButton`s, or one `IconButton`."
|
|
2424
|
+
},
|
|
2425
|
+
{
|
|
2426
|
+
"name": "above",
|
|
2427
|
+
"takes": "anything",
|
|
2428
|
+
"required": false,
|
|
2429
|
+
"note": "A row between the header and the list that stays put while the list scrolls — a field that adds to the list."
|
|
2430
|
+
},
|
|
2431
|
+
{
|
|
2432
|
+
"name": "spacing",
|
|
2433
|
+
"takes": "rows | sections",
|
|
2434
|
+
"required": false,
|
|
2435
|
+
"note": "The room between rows. `rows` (2px) for a list of one kind of row; `sections` (4px) for groups with labels and dividers of their own."
|
|
2436
|
+
},
|
|
2437
|
+
{
|
|
2438
|
+
"name": "collapsed",
|
|
2439
|
+
"takes": "true/false",
|
|
2440
|
+
"required": false,
|
|
2441
|
+
"note": "Closes the column with the rail: it narrows to nothing and fades."
|
|
2442
|
+
},
|
|
2443
|
+
{
|
|
2444
|
+
"name": "children",
|
|
2445
|
+
"takes": "anything",
|
|
2446
|
+
"required": true,
|
|
2447
|
+
"note": "The rows."
|
|
2448
|
+
},
|
|
2449
|
+
{
|
|
2450
|
+
"name": "className",
|
|
2451
|
+
"takes": "text",
|
|
2452
|
+
"required": false,
|
|
2453
|
+
"note": "Placement only."
|
|
2454
|
+
}
|
|
2455
|
+
],
|
|
2456
|
+
"variants": [
|
|
2457
|
+
{
|
|
2458
|
+
"prop": "spacing",
|
|
2459
|
+
"values": [
|
|
2460
|
+
"rows",
|
|
2461
|
+
"sections"
|
|
2462
|
+
]
|
|
2463
|
+
}
|
|
2464
|
+
],
|
|
2465
|
+
"ownsBehaviours": [],
|
|
2466
|
+
"status": "stable",
|
|
2467
|
+
"migrationStage": null,
|
|
2468
|
+
"docsId": "layout-listcolumn--docs",
|
|
2469
|
+
"storyId": "layout-listcolumn--default",
|
|
2470
|
+
"docPage": "src/ListColumn.mdx",
|
|
2471
|
+
"app": null
|
|
2472
|
+
},
|
|
2398
2473
|
{
|
|
2399
2474
|
"name": "Menu",
|
|
2400
2475
|
"repo": "estiva-ui",
|
|
@@ -4383,6 +4458,91 @@
|
|
|
4383
4458
|
"docPage": null,
|
|
4384
4459
|
"app": null
|
|
4385
4460
|
},
|
|
4461
|
+
{
|
|
4462
|
+
"name": "ToolbarLink",
|
|
4463
|
+
"repo": "estiva-ui",
|
|
4464
|
+
"kind": "component",
|
|
4465
|
+
"importPath": "@estiva-app/ui",
|
|
4466
|
+
"sourceFile": "src/Toolbar.tsx",
|
|
4467
|
+
"purpose": "An `IconButton` that is a link, and an item of a `Toolbar`: the arrow keys reach it like the buttons beside it.",
|
|
4468
|
+
"purposeFrom": "comment",
|
|
4469
|
+
"props": [
|
|
4470
|
+
{
|
|
4471
|
+
"name": "href",
|
|
4472
|
+
"takes": "text",
|
|
4473
|
+
"required": true,
|
|
4474
|
+
"note": null
|
|
4475
|
+
},
|
|
4476
|
+
{
|
|
4477
|
+
"name": "ref",
|
|
4478
|
+
"takes": "Ref<HTMLAnchorElement>",
|
|
4479
|
+
"required": false,
|
|
4480
|
+
"note": null
|
|
4481
|
+
},
|
|
4482
|
+
{
|
|
4483
|
+
"name": "variant",
|
|
4484
|
+
"takes": "muted | outlined | primary | current | resolve",
|
|
4485
|
+
"required": false,
|
|
4486
|
+
"note": "`current` takes the colour of what it sits in — the ✕ on a `Banner` wears the banner's tone."
|
|
4487
|
+
},
|
|
4488
|
+
{
|
|
4489
|
+
"name": "glow",
|
|
4490
|
+
"takes": "true/false",
|
|
4491
|
+
"required": false,
|
|
4492
|
+
"note": "A glow in Signal — the send arrow while there is something to send."
|
|
4493
|
+
},
|
|
4494
|
+
{
|
|
4495
|
+
"name": "tooltip",
|
|
4496
|
+
"takes": "text",
|
|
4497
|
+
"required": false,
|
|
4498
|
+
"note": null
|
|
4499
|
+
},
|
|
4500
|
+
{
|
|
4501
|
+
"name": "tooltipShortcut",
|
|
4502
|
+
"takes": "text",
|
|
4503
|
+
"required": false,
|
|
4504
|
+
"note": "A key hint drawn as the `Kbd` chip inside the tooltip — for a button whose only other affordance is a keyboard shortcut."
|
|
4505
|
+
},
|
|
4506
|
+
{
|
|
4507
|
+
"name": "tooltipPlacement",
|
|
4508
|
+
"takes": "top | bottom",
|
|
4509
|
+
"required": false,
|
|
4510
|
+
"note": null
|
|
4511
|
+
},
|
|
4512
|
+
{
|
|
4513
|
+
"name": "children",
|
|
4514
|
+
"takes": "anything",
|
|
4515
|
+
"required": true,
|
|
4516
|
+
"note": "The icon: 16px, stroke 1.5."
|
|
4517
|
+
}
|
|
4518
|
+
],
|
|
4519
|
+
"variants": [
|
|
4520
|
+
{
|
|
4521
|
+
"prop": "variant",
|
|
4522
|
+
"values": [
|
|
4523
|
+
"muted",
|
|
4524
|
+
"outlined",
|
|
4525
|
+
"primary",
|
|
4526
|
+
"current",
|
|
4527
|
+
"resolve"
|
|
4528
|
+
]
|
|
4529
|
+
},
|
|
4530
|
+
{
|
|
4531
|
+
"prop": "tooltipPlacement",
|
|
4532
|
+
"values": [
|
|
4533
|
+
"top",
|
|
4534
|
+
"bottom"
|
|
4535
|
+
]
|
|
4536
|
+
}
|
|
4537
|
+
],
|
|
4538
|
+
"ownsBehaviours": [],
|
|
4539
|
+
"status": "stable",
|
|
4540
|
+
"migrationStage": null,
|
|
4541
|
+
"docsId": "primitives-toolbar--docs",
|
|
4542
|
+
"storyId": "primitives-toolbar--default",
|
|
4543
|
+
"docPage": null,
|
|
4544
|
+
"app": null
|
|
4545
|
+
},
|
|
4386
4546
|
{
|
|
4387
4547
|
"name": "ToolbarSeparator",
|
|
4388
4548
|
"repo": "estiva-ui",
|
package/src/AppShell.mdx
CHANGED
|
@@ -52,7 +52,6 @@ flex column. A page is a flex child of `main`, and says one of two things:
|
|
|
52
52
|
Never `h-full`: the frame's content box is *at least* the viewport's
|
|
53
53
|
height and grows with a tall page — which is what lets the bar know the
|
|
54
54
|
page changed — so a percentage height has nothing to resolve against.
|
|
55
|
-
Measured on all four of Ship's pages (2026-09-09).
|
|
56
55
|
|
|
57
56
|
```tsx
|
|
58
57
|
import { AppShell, Banner, IdentityMenu, Rail, RailItem } from '@estiva-app/ui'
|
|
@@ -78,6 +77,14 @@ import { AppShell, Banner, IdentityMenu, Rail, RailItem } from '@estiva-app/ui'
|
|
|
78
77
|
- In the Signal theme the floating frame draws the control-room dot grid on
|
|
79
78
|
its ground, behind the card. Nothing to add: the preset makes it.
|
|
80
79
|
|
|
80
|
+
- In the floating manner the bar takes the top 52px; the card sits 16px from
|
|
81
|
+
the right and the bottom, with 16px corners, beside the 64px rail.
|
|
82
|
+
- In the floating manner the card owns `overflow-hidden`, so anything in it
|
|
83
|
+
that can grow sits in a **ScrollArea** — a list column most of all, which
|
|
84
|
+
**ListColumn** draws with its own. Without one it is cut off at the fold.
|
|
85
|
+
- **Planned:** `SplitLayout`, resizable panels, in `@estiva-app/ui/layout`
|
|
86
|
+
(migration stage 8). It may take the list and side columns' widths.
|
|
87
|
+
|
|
81
88
|
## What it owns
|
|
82
89
|
|
|
83
90
|
It does these by itself. Write none of them around it.
|
package/src/AttachmentCard.mdx
CHANGED
|
@@ -53,10 +53,12 @@ import { AttachmentCard } from '@estiva-app/ui'
|
|
|
53
53
|
- The name truncates. When it is cut off, the full name is on hover; a name
|
|
54
54
|
that fits, and a size, show nothing extra. `noteHint` is always on hover, since
|
|
55
55
|
it says more than the line.
|
|
56
|
-
- The remove control is
|
|
56
|
+
- The remove control is a 20px round badge on the card's corner, a button
|
|
57
57
|
on Base UI's `Button` like InputChip's ✕ — not an `IconButton`, the 24px
|
|
58
58
|
square that fills on hover.
|
|
59
59
|
|
|
60
|
+
- **Planned:** a Lightbox that opens a picture full screen, in migration stage 7.
|
|
61
|
+
|
|
60
62
|
## What it owns
|
|
61
63
|
|
|
62
64
|
It does these by itself. Write none of them around it.
|
package/src/Avatar.mdx
CHANGED
|
@@ -39,7 +39,7 @@ import { Avatar } from '@estiva-app/ui'
|
|
|
39
39
|
two words, skipping words that open with a symbol. An unnamed person is
|
|
40
40
|
the silhouette, not a `?` — a question mark reads as an error badge.
|
|
41
41
|
- The initials are centred on the capitals themselves — cap height to
|
|
42
|
-
baseline — not on the line of text around them
|
|
42
|
+
baseline — not on the line of text around them. A single face can
|
|
43
43
|
still sit up to half a pixel off: the screen rounds text to its pixel grid
|
|
44
44
|
wherever the tile lands.
|
|
45
45
|
- The colour behind initials is per person, chosen from the name, the same
|
|
@@ -54,7 +54,7 @@ import { Avatar } from '@estiva-app/ui'
|
|
|
54
54
|
**A face is silent by default**, and that is the important half: almost every
|
|
55
55
|
face in the suite sits beside the name it belongs to — in a `Person`, in a
|
|
56
56
|
`MenuItem`'s `leading`, in a row — and a picture that spoke there would say the
|
|
57
|
-
name twice. It used to
|
|
57
|
+
name twice. It used to: a `Person`
|
|
58
58
|
inside a button announced **"AD Ana Duarte"** (the initials read as text), and
|
|
59
59
|
with a picture **"Ana Duarte Ana Duarte"**.
|
|
60
60
|
|
|
@@ -68,6 +68,11 @@ a stack of members — and the tile becomes one image with one name.
|
|
|
68
68
|
|
|
69
69
|
<Canvas of={AvatarStories.Palette} />
|
|
70
70
|
|
|
71
|
+
- **Planned:** a Byline part — face · name · time — in migration stage 7.
|
|
72
|
+
|
|
73
|
+
- `initialsFor(name)` and `hueFor(name)` give the same initials and colour where
|
|
74
|
+
the part itself cannot be drawn.
|
|
75
|
+
|
|
71
76
|
## What it owns
|
|
72
77
|
|
|
73
78
|
It does these by itself. Write none of them around it.
|
package/src/Banner.mdx
CHANGED
|
@@ -27,7 +27,7 @@ line, one of four tones, gone when there is nothing to say.
|
|
|
27
27
|
- Something that floats over the page and must **not** fade → still
|
|
28
28
|
**Toast**, with `durationMs: 0` and an action. A banner is the strip
|
|
29
29
|
under the header at the app's full width; a floating pill is not one, even
|
|
30
|
-
a permanent one
|
|
30
|
+
a permanent one.
|
|
31
31
|
- A standing property of one thing → **Chip** on that thing.
|
|
32
32
|
- More than one action, or a second line of text — that is a different
|
|
33
33
|
component (not in the package yet).
|
|
@@ -45,9 +45,8 @@ import { Banner } from '@estiva-app/ui'
|
|
|
45
45
|
- One line of text, `body-2`. If it needs a **second line**, it has outgrown
|
|
46
46
|
this component.
|
|
47
47
|
- `icon` puts a 16px icon before the line and `action` one small muted Button
|
|
48
|
-
at the end of it, both in the tone
|
|
49
|
-
the
|
|
50
|
-
and the text truncates. One action at most.
|
|
48
|
+
at the end of it, both in the tone. With either, the banner is one line and
|
|
49
|
+
the text truncates. One action at most.
|
|
51
50
|
|
|
52
51
|
<Canvas of={BannerStories.WithIconAndAction} />
|
|
53
52
|
|
|
@@ -57,7 +56,7 @@ import { Banner } from '@estiva-app/ui'
|
|
|
57
56
|
</Banner>
|
|
58
57
|
```
|
|
59
58
|
|
|
60
|
-
- `onDismiss` adds an ✕ at the right-hand end
|
|
59
|
+
- `onDismiss` adds an ✕ at the right-hand end. Use it where
|
|
61
60
|
the reader decides the notice is done with, rather than the app. The row
|
|
62
61
|
is 40px rather than 36px with it, because the button is taller than the
|
|
63
62
|
line of text; without it, nothing about the strip changes.
|
package/src/Breadcrumb.mdx
CHANGED
|
@@ -56,6 +56,8 @@ import { Breadcrumb } from '@estiva-app/ui'
|
|
|
56
56
|
`min-w-0` container so it can truncate at all.
|
|
57
57
|
- The separators are `aria-hidden` — the trail reads as its places.
|
|
58
58
|
|
|
59
|
+
- 6px between each place and its separator.
|
|
60
|
+
|
|
59
61
|
## What it owns
|
|
60
62
|
|
|
61
63
|
It does these by itself. Write none of them around it.
|
package/src/Button.mdx
CHANGED
|
@@ -6,7 +6,7 @@ import * as ButtonStories from './Button.stories'
|
|
|
6
6
|
# Button
|
|
7
7
|
|
|
8
8
|
The push-button for an action: a verb, clicked once — "Create", "Save",
|
|
9
|
-
"Delete
|
|
9
|
+
"Delete". Four variants on one geometry: 32px default / 24px small,
|
|
10
10
|
6px radius, medium weight.
|
|
11
11
|
|
|
12
12
|
<Canvas of={ButtonStories.AllVariants} />
|
|
@@ -67,7 +67,7 @@ import { IconPlus } from '@tabler/icons-react'
|
|
|
67
67
|
has the same look.
|
|
68
68
|
- **Placed from outside, never restyled.** `className` takes space, size,
|
|
69
69
|
flex and grid, and position; a colour, a text size, a border, a corner or a
|
|
70
|
-
shadow is refused by the lint (`estiva/no-restyled-part
|
|
70
|
+
shadow is refused by the lint (`estiva/no-restyled-part`).
|
|
71
71
|
|
|
72
72
|
## What it owns
|
|
73
73
|
|
|
@@ -78,6 +78,7 @@ It does these by itself. Write none of them around it.
|
|
|
78
78
|
| Says what it is to assistive technology: a button | `role="button"` | ✓ |
|
|
79
79
|
| Is reachable with Tab, also when it is disabled with a reason | a `tabIndex` | ✓ |
|
|
80
80
|
| Presses on Enter and Space, and does nothing while disabled | a key handler | |
|
|
81
|
+
| Keeps its 32px (24px small) in a scrolling column | `shrink-0` | |
|
|
81
82
|
|
|
82
83
|
## Keys
|
|
83
84
|
|
package/src/Button.tsx
CHANGED
|
@@ -86,8 +86,8 @@ export function Button({
|
|
|
86
86
|
// this package's own story frames, each of which asks for the top.
|
|
87
87
|
// A control does not get to decide where its caller puts it.
|
|
88
88
|
'inline-flex items-center justify-center gap-1 rounded-md transition-colors font-sans font-medium',
|
|
89
|
-
size === 'default' && 'h-8 text-btn-default',
|
|
90
|
-
size === 'small' && 'h-6 text-btn-small',
|
|
89
|
+
size === 'default' && 'h-8 min-h-8 text-btn-default',
|
|
90
|
+
size === 'small' && 'h-6 min-h-6 text-btn-small',
|
|
91
91
|
// Extra right padding beside a leading icon, for optical balance.
|
|
92
92
|
size === 'default' && (hasLeadingIcon ? 'pl-2 pr-3' : 'px-2'),
|
|
93
93
|
size === 'small' && (hasLeadingIcon ? 'pl-1.5 pr-2' : 'px-1.5'),
|
package/src/Card.mdx
CHANGED
|
@@ -12,6 +12,8 @@ hairline. What goes inside, and the space around it, is yours.
|
|
|
12
12
|
|
|
13
13
|
## When
|
|
14
14
|
|
|
15
|
+
- **It draws no padding and no layout**: both are yours.
|
|
16
|
+
|
|
15
17
|
A box is a card when it stands for **one thing you could name**, and there are
|
|
16
18
|
many of it or you treat the whole box as that thing — open it, point at it,
|
|
17
19
|
move it.
|
|
@@ -73,9 +75,11 @@ import { Card } from '@estiva-app/ui'
|
|
|
73
75
|
picture that runs edge to edge. Without it their square corners show.
|
|
74
76
|
- **Placed from outside, never restyled.** `className` takes space, size,
|
|
75
77
|
flex and grid, and position; a colour, a text size, a border, a corner or a
|
|
76
|
-
shadow is refused by the lint (`estiva/no-restyled-part
|
|
78
|
+
shadow is refused by the lint (`estiva/no-restyled-part`). What is inside the card is yours: its padding and its words go on
|
|
77
79
|
your own element inside.
|
|
78
80
|
|
|
81
|
+
- **Planned:** the inside layout of a linked object's card, in migration stage 7.
|
|
82
|
+
|
|
79
83
|
## What it owns
|
|
80
84
|
|
|
81
85
|
It does these by itself. Write none of them around it.
|
package/src/Checkbox.mdx
CHANGED
|
@@ -88,6 +88,7 @@ It does these by itself. Write none of them around it.
|
|
|
88
88
|
| Says what it is to assistive technology: a checkbox, ticked or not | `role="checkbox"` or `aria-checked` | ✓ |
|
|
89
89
|
| Toggles on Space and on a click; Enter is left to the form | a click or key handler | |
|
|
90
90
|
| Is one Tab stop, in a `row` too | a `tabIndex` on the row | |
|
|
91
|
+
| Keeps a `row`'s 40px in a scrolling column | `shrink-0` | |
|
|
91
92
|
|
|
92
93
|
## Keys
|
|
93
94
|
|
package/src/ChipInput.mdx
CHANGED
|
@@ -66,7 +66,7 @@ import { ChipInput } from '@estiva-app/ui'
|
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
- **`InputChip` on its own** names its ✕ `Remove <label>`. Where the ✕ does
|
|
69
|
-
something else, say what with `removeLabel` (a
|
|
69
|
+
something else, say what with `removeLabel` (a chip for a group you belong to: "Leave Design").
|
|
70
70
|
- To cap a long label, give the chip a `max-w-*` and `truncate`: the label is
|
|
71
71
|
cut and the ✕ keeps its size. `truncate` is off unless asked, because
|
|
72
72
|
cutting clips a letter's soft edge by up to 4px at 1x even when the label
|
|
@@ -14,11 +14,11 @@ toggle, and the rows under it, sliding shut and open.
|
|
|
14
14
|
## When
|
|
15
15
|
|
|
16
16
|
- A group of rows in a sidebar or a panel that the reader may want out of
|
|
17
|
-
the way —
|
|
17
|
+
the way — starred items, recent places.
|
|
18
18
|
- `defaultOpen={false}` for a group that starts folded.
|
|
19
19
|
- `storageKey` when the section should stay how the reader left it across
|
|
20
20
|
reloads: this browser remembers, under that key. The app prefixes the
|
|
21
|
-
key (`
|
|
21
|
+
key (`app.sidebar.starred`).
|
|
22
22
|
- `open` + `onOpenChange` when the app owns the state.
|
|
23
23
|
- `actions` for add, sort, filter beside the title — SectionHeader's,
|
|
24
24
|
revealed on hover or focus.
|
|
@@ -41,7 +41,7 @@ composition lives:
|
|
|
41
41
|
```tsx
|
|
42
42
|
import { CollapsibleSection, NavItem } from '@estiva-app/ui'
|
|
43
43
|
|
|
44
|
-
<CollapsibleSection title="
|
|
44
|
+
<CollapsibleSection title="Starred" storageKey="app.sidebar.starred" className="mt-2 shrink-0" contentClassName="gap-px">
|
|
45
45
|
<NavItem href="/p/1" label="Item one" />
|
|
46
46
|
<NavItem href="/p/2" label="Item two" />
|
|
47
47
|
</CollapsibleSection>
|
package/src/ConfirmDialog.mdx
CHANGED
|
@@ -45,7 +45,7 @@ import { ConfirmDialog } from '@estiva-app/ui'
|
|
|
45
45
|
dialog open — your surface has said why; a resolved `void`/`true`
|
|
46
46
|
closes it.
|
|
47
47
|
- **Escape cancels; a press on the backdrop does not.** This is Base UI's
|
|
48
|
-
`AlertDialog`, through the shell's `alert
|
|
48
|
+
`AlertDialog`, through the shell's `alert`: a
|
|
49
49
|
destructive question is answered rather than clicked away. The backdrop
|
|
50
50
|
used to cancel, and that is the one behaviour stage 3 deliberately changed.
|
|
51
51
|
- It announces itself as an alert (`role="alertdialog"`), so a screen reader
|
|
@@ -71,7 +71,7 @@ Different from `DialogShell`'s in one row, and it is the row that matters.
|
|
|
71
71
|
| Input | What happens |
|
|
72
72
|
|---|---|
|
|
73
73
|
| Escape | cancels |
|
|
74
|
-
| backdrop click | **nothing** — the question has to be answered
|
|
74
|
+
| backdrop click | **nothing** — the question has to be answered |
|
|
75
75
|
| close button | cancels |
|
|
76
76
|
| Tab / Shift+Tab | cycles the controls inside the card, and cannot leave it |
|
|
77
77
|
| on close | focus returns to whatever opened it |
|
package/src/ContainerHeader.mdx
CHANGED
|
@@ -7,8 +7,7 @@ import * as ContainerHeaderStories from './ContainerHeader.stories'
|
|
|
7
7
|
|
|
8
8
|
The bar across the top of a column — a list, a thread, a side panel: its
|
|
9
9
|
title, and the buttons that act on the whole column at the right edge. 48px,
|
|
10
|
-
with a hairline under it.
|
|
11
|
-
looks (Katerina, 2026-09-18).
|
|
10
|
+
with a hairline under it.
|
|
12
11
|
|
|
13
12
|
<Canvas of={ContainerHeaderStories.WithActions} />
|
|
14
13
|
|
|
@@ -26,6 +25,11 @@ looks (Katerina, 2026-09-18).
|
|
|
26
25
|
- The top of the whole app → **TopBar**.
|
|
27
26
|
- A dialog's title → **DialogShell** draws its own.
|
|
28
27
|
|
|
28
|
+
- A row you draw yourself at the top of a column — your own padding and a line
|
|
29
|
+
under it → this part. Its numbers are every column's.
|
|
30
|
+
- A whole list column, header and scrolling list → **ListColumn**, which draws
|
|
31
|
+
this bar at its top.
|
|
32
|
+
|
|
29
33
|
## How
|
|
30
34
|
|
|
31
35
|
```tsx
|
|
@@ -53,7 +57,12 @@ import { ContainerHeader, IconButton } from '@estiva-app/ui'
|
|
|
53
57
|
|
|
54
58
|
## What it owns
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
It does these by itself. Write none of them around it.
|
|
61
|
+
|
|
62
|
+
| It does | So you never write | Refused when written by hand |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| Draws the column's bar: 48px tall, the title 20px in at 14px medium, the actions 16px from the right, a hairline under it | a row of your own with padding and a border | |
|
|
65
|
+
| Keeps its 48px in a scrolling column | `shrink-0` | |
|
|
57
66
|
|
|
58
67
|
## Props
|
|
59
68
|
|
package/src/DialogShell.mdx
CHANGED
|
@@ -71,7 +71,7 @@ import { DialogShell, Button } from '@estiva-app/ui'
|
|
|
71
71
|
worth setting where typing is the point — the trap decides where focus
|
|
72
72
|
*cannot* go, not where it starts.
|
|
73
73
|
- `alert` for a question that has to be answered: a press on the backdrop
|
|
74
|
-
stops closing it
|
|
74
|
+
stops closing it. Escape and the ✕ still do.
|
|
75
75
|
`ConfirmDialog` sets it; a form or a roster should not.
|
|
76
76
|
|
|
77
77
|
## What it owns
|
package/src/Divider.mdx
CHANGED
|
@@ -49,6 +49,8 @@ import { Divider } from '@estiva-app/ui'
|
|
|
49
49
|
- **Inside a `Menu` or a `Popover` it has no inset**: the hairline runs the
|
|
50
50
|
width of the rows. The panel tells it so; nothing is passed in.
|
|
51
51
|
|
|
52
|
+
- The line is 1px; the room around it is yours (`my-1` in a Menu).
|
|
53
|
+
|
|
52
54
|
## What it owns
|
|
53
55
|
|
|
54
56
|
It does these by itself. Write none of them around it.
|
|
@@ -56,6 +58,7 @@ It does these by itself. Write none of them around it.
|
|
|
56
58
|
| It does | So you never write | Refused when written by hand |
|
|
57
59
|
|---|---|---|
|
|
58
60
|
| Says what it is to assistive technology: a separator | `role="separator"` | ✓ |
|
|
61
|
+
| Keeps its 1px line in a scrolling column | `shrink-0` | |
|
|
59
62
|
|
|
60
63
|
## Props
|
|
61
64
|
|
package/src/EditableText.mdx
CHANGED
|
@@ -13,6 +13,9 @@ edit is never silently lost.
|
|
|
13
13
|
|
|
14
14
|
## When
|
|
15
15
|
|
|
16
|
+
- **It has no size of its own.** Give it the size of the text it stands in for —
|
|
17
|
+
a pane title's `text-body-2-strong` — on your element around it; the text and
|
|
18
|
+
the field both take it.
|
|
16
19
|
- A title or description edited where it is shown — no dialog, no separate
|
|
17
20
|
form.
|
|
18
21
|
- A reader who cannot write gets `readOnly` — the value without the edit
|
|
@@ -55,7 +58,9 @@ import { EditableText } from '@estiva-app/ui'
|
|
|
55
58
|
- **Its text size is where it sits**, like `Person`'s: put `text-h2` on your own
|
|
56
59
|
element around it, and the text and the field both take it (a field inherits
|
|
57
60
|
the page's font). `className` places it — `-mx-2` to line its text up with
|
|
58
|
-
a header's inset — and a size passed there is refused by the lint
|
|
61
|
+
a header's inset — and a size passed there is refused by the lint.
|
|
62
|
+
|
|
63
|
+
- **Planned:** rich text edited where it is shown (UIG-30).
|
|
59
64
|
|
|
60
65
|
## What it owns
|
|
61
66
|
|
package/src/EmptyState.mdx
CHANGED
|
@@ -57,14 +57,14 @@ import { EmptyState } from '@estiva-app/ui'
|
|
|
57
57
|
|
|
58
58
|
**When there is one thing to do about it, give it as `action`.** It is drawn
|
|
59
59
|
16px under the line as the package's Button, outlined — the same look wherever
|
|
60
|
-
it appears
|
|
60
|
+
it appears. Never put a Button beside an EmptyState in a
|
|
61
61
|
box of your own: the box places it, and no two boxes place it alike. A `page`
|
|
62
62
|
only; a section's line takes no action.
|
|
63
63
|
|
|
64
64
|
<Canvas of={EmptyStateStories.WithAction} />
|
|
65
65
|
|
|
66
66
|
```tsx
|
|
67
|
-
<EmptyState icon={<IconMessagePlus size={16} stroke={1.5} />} message="
|
|
67
|
+
<EmptyState icon={<IconMessagePlus size={16} stroke={1.5} />} message="Nothing here yet." action={{ label: 'Create', onClick: create }} />
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
**Put a section's empty state inside the box its rows live in, and give it no
|
package/src/Field.mdx
CHANGED
|
@@ -43,14 +43,13 @@ import { Field, TextInput } from '@estiva-app/ui'
|
|
|
43
43
|
reversed at stage 3: the Field used to win and override it.
|
|
44
44
|
- `helper` is the line under the control — the format, or what happens if
|
|
45
45
|
it is left empty. `error` **replaces** it and marks the control invalid,
|
|
46
|
-
so you no longer pass `aria-invalid` yourself. Both are announced;
|
|
47
|
-
|
|
48
|
-
those was.
|
|
46
|
+
so you no longer pass `aria-invalid` yourself. Both are announced; a line
|
|
47
|
+
written by hand beside the control is not.
|
|
49
48
|
- With neither, the control stays a direct child of the field, so nothing
|
|
50
49
|
that predates the two props moved a pixel.
|
|
51
50
|
- **`required` marks the control as well as the label.** The asterisk is a
|
|
52
51
|
picture of the requirement; `aria-required` on the control is the word for
|
|
53
|
-
it
|
|
52
|
+
it. Base UI's
|
|
54
53
|
`Field` has no `required` of its own, so the Field puts it there. A control
|
|
55
54
|
that says so already is left alone, and a `children` of several elements
|
|
56
55
|
keeps the asterisk and owes its own.
|
package/src/Form.mdx
CHANGED
package/src/IconButton.mdx
CHANGED
|
@@ -48,7 +48,7 @@ import { IconPencil } from '@tabler/icons-react'
|
|
|
48
48
|
- `glow` lights it in Signal — the send arrow while there is something to send.
|
|
49
49
|
- **Placed from outside, never restyled.** `className` takes space, size,
|
|
50
50
|
flex and grid, and position; a colour, a text size, a border, a corner or a
|
|
51
|
-
shadow is refused by the lint (`estiva/no-restyled-part
|
|
51
|
+
shadow is refused by the lint (`estiva/no-restyled-part`).
|
|
52
52
|
- `type` defaults to `"button"`; it is a native `<button>` on Base UI's
|
|
53
53
|
Button, and every native prop passes through.
|
|
54
54
|
- `disabledReason="Read only"` disables it, keeps it reachable by Tab,
|
package/src/IdentityMenu.mdx
CHANGED
|
@@ -74,7 +74,7 @@ It does these by itself. Write none of them around it.
|
|
|
74
74
|
## Keys
|
|
75
75
|
|
|
76
76
|
Since stage 4 the panel is a Base UI `Menu`, and **the arrow keys walk the
|
|
77
|
-
actions only
|
|
77
|
+
actions only**: they step over the identity block, the
|
|
78
78
|
workspace line and the notes, because those are not things you can do.
|
|
79
79
|
|
|
80
80
|
| Input | What happens |
|
package/src/InlineChip.mdx
CHANGED
|
@@ -51,6 +51,9 @@ import { InlineChip } from '@estiva-app/ui'
|
|
|
51
51
|
- A rich-text editor that renders chips from strings cannot use a component.
|
|
52
52
|
`inlineChipClassName(tone)` gives it the same classes.
|
|
53
53
|
|
|
54
|
+
- `INLINE_CHIP_CLASSES` and `INLINE_CHIP_TONE_CLASSES` give the same look where
|
|
55
|
+
only classes can go — inside an editor.
|
|
56
|
+
|
|
54
57
|
## What it owns
|
|
55
58
|
|
|
56
59
|
It does these by itself. Write none of them around it.
|
package/src/Kbd.mdx
CHANGED
|
@@ -41,8 +41,8 @@ Geist Mono owns `←` and `→` but draws them 10px wide, so a chip holding one
|
|
|
41
41
|
comes out wider than the chips beside it. Measure before adding a symbol to
|
|
42
42
|
this list; the font's coverage is not uniform.
|
|
43
43
|
|
|
44
|
-
And a trigger is the character you would actually type.
|
|
45
|
-
|
|
44
|
+
And a trigger is the character you would actually type. A shortcut that fires
|
|
45
|
+
on `->` shows `->` — there is no `→` key to press.
|
|
46
46
|
|
|
47
47
|
## When not
|
|
48
48
|
|
package/src/Link.mdx
CHANGED
|
@@ -12,6 +12,8 @@ the app decides how it navigates.
|
|
|
12
12
|
|
|
13
13
|
## When
|
|
14
14
|
|
|
15
|
+
- **It takes the size and colour of the text around it**: put it inside that
|
|
16
|
+
text.
|
|
15
17
|
- **`text`** — a link written inside text: the info colour, always
|
|
16
18
|
underlined, dimming on hover.
|
|
17
19
|
- **`quiet`** — a title or a time that is also a link. It takes the colour and
|
|
@@ -59,7 +61,7 @@ import { Link } from '@estiva-app/ui'
|
|
|
59
61
|
Its size and colour still come from the text around it.
|
|
60
62
|
- `quiet`, `underlined` and `plain` take their size and colour from where they
|
|
61
63
|
sit, so put those on your own element around the link, not on the link —
|
|
62
|
-
the lint refuses a look passed in `className
|
|
64
|
+
the lint refuses a look passed in `className`.
|
|
63
65
|
|
|
64
66
|
## What it owns
|
|
65
67
|
|