@usememos/ui 26.11.2 → 26.11.4
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/README.md +1 -1
- package/dist/components/badge/index.js.map +1 -1
- package/dist/components/dialog/parts.js +1 -1
- package/dist/components/dialog/parts.js.map +3 -3
- package/dist/components/drawer/index.js +1 -1
- package/dist/components/drawer/index.js.map +2 -2
- package/dist/components/form/index.js +3 -0
- package/dist/components/form/index.js.map +7 -0
- package/dist/components/list/index.js +1 -1
- package/dist/components/list/index.js.map +3 -3
- package/dist/components/table/index.js +1 -1
- package/dist/components/table/index.js.map +3 -3
- package/dist/components/toggle-group/index.js +1 -1
- package/dist/components/toggle-group/index.js.map +3 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +3 -3
- package/dist/styles.css +1 -1
- package/dist/tokens.css +1 -1
- package/dist/types/components/badge/index.d.ts +2 -2
- package/dist/types/components/dialog/parts.d.ts +5 -0
- package/dist/types/components/drawer/index.d.ts +4 -2
- package/dist/types/components/form/index.d.ts +28 -0
- package/dist/types/components/list/index.d.ts +11 -1
- package/dist/types/components/table/index.d.ts +8 -0
- package/dist/types/components/toggle-group/index.d.ts +2 -0
- package/dist/types/index.d.ts +1 -0
- package/llms-full.txt +240 -47
- package/llms.txt +3 -2
- package/package.json +1 -1
package/llms-full.txt
CHANGED
|
@@ -24,8 +24,8 @@ It is not a general-purpose UI library for outside developers. Components are ge
|
|
|
24
24
|
| [Size, spacing and layout](layout.md) | Control sizes, the 4px scale, radius, layout widths, breakpoints and layers |
|
|
25
25
|
| [Motion](motion.md) | Press, settle and fade timing, and reduced motion |
|
|
26
26
|
| [States](states.md) | Hover, pressed, loading, disabled, invalid and focus |
|
|
27
|
-
| [Icons](icons.md) | Lucide, sizes, fixed stroke
|
|
28
|
-
| [Components](components/README.md) | The
|
|
27
|
+
| [Icons](icons.md) | Lucide, sizes, the fixed stroke and accessibility |
|
|
28
|
+
| [Components](components/README.md) | The 31 components, what was left out, and one page per component |
|
|
29
29
|
| [Using the components](usage.md) | Which component to pick (overlays, controls, feedback), recipes for combining them, size, emphasis, an accessibility checklist, common mistakes and a map from other UI kits |
|
|
30
30
|
| [Patterns](patterns.md) | Page, form, entry, confirmation, loading, empty state and mobile |
|
|
31
31
|
| [Building on it](building.md) | Base UI, loading the CSS, scoping inside a host page, theme selection and API conventions |
|
|
@@ -39,7 +39,7 @@ It is not a general-purpose UI library for outside developers. Components are ge
|
|
|
39
39
|
- **Ink is the accent.** The primary action and every on state use `primary`: near-black in Light, near-white in Dark.
|
|
40
40
|
- **One quiet hue.** `accent` is a quiet blue used only for links and text selection, never as a fill. Focus is ink.
|
|
41
41
|
- **Status colors only for status.** Green means success, amber means warning, red means danger or error. None of them is used for emphasis.
|
|
42
|
-
- **Lucide icons** at
|
|
42
|
+
- **Lucide icons** at one fixed screen-pixel stroke, 1.25px, at every size.
|
|
43
43
|
- **The system UI font** for all text. The package ships no font files.
|
|
44
44
|
- **Few sizes, two weights.** App UI uses 13, 14, 16 and 20px text at weights 400 and 500. 20px is for the page title only.
|
|
45
45
|
- **Dense, on a 4px grid.** Controls are 32px tall by default.
|
|
@@ -267,7 +267,7 @@ Each depth class applies a whole role in one class. Use them for new parts befor
|
|
|
267
267
|
|
|
268
268
|
- Use few sizes and few weights. App text uses four sizes (13, 14, 16 and 20px) and two weights (400 and 500); 12px is only for the labels of 20px parts (see `label-xs`). Hierarchy comes from text color, spacing and grouping first, then from weight, and from size last.
|
|
269
269
|
- Weight 400 is for running text and values people typed or chose. Weight 500 is for labels, controls, titles and headings. The UI uses no 600 or 700. The one exception is [Prose](components/prose.md): bold text and headings that people wrote are 600, because 500 does not read as bold in system fonts.
|
|
270
|
-
- 20px (`heading`) is for the page title only, once per page. 32px (`display`) is for the hero headline on a website only, once per page. Nothing else is larger than 16px, and 16px is only for `title` and Prose text.
|
|
270
|
+
- 20px (`heading`) is for the page title only, once per page. 32px (`display`) is for the hero headline on a website only, once per page. Nothing else in the UI is larger than 16px, and 16px is only for `title` and Prose text. Headings that people write inside Prose have their own sizes (see [Prose](#prose)).
|
|
271
271
|
- A section title inside a page or panel is `label` (14px, 500), set apart by space above it, not by a larger size. See [Page](patterns.md#page).
|
|
272
272
|
- Secondary text (descriptions, helper text, metadata) is `caption` (13px) in `ink-muted`. No text is smaller than 13px, except `label-xs` inside 20px parts.
|
|
273
273
|
- Control text scales with the control size: 12px at xs, 13px at sm, 14px at md.
|
|
@@ -277,16 +277,18 @@ Each depth class applies a whole role in one class. Use them for new parts befor
|
|
|
277
277
|
- Selected and unselected states never change weight, so labels don't shift.
|
|
278
278
|
- Adding a size, a weight or a style needs a written reason in this page and the maintainer's sign-off.
|
|
279
279
|
- The platform's UI font for everything (San Francisco on Apple, Segoe UI on Windows, Roboto on Android), smoothed with antialiasing. The package ships no font files.
|
|
280
|
+
- Titles and headings (`title`, `heading`, `display`, and headings in Prose) use `text-wrap: balance`, so a heading that wraps does not leave one word on its last line. Prose paragraphs and list items use `text-wrap: pretty`. Browsers without these wrap as usual.
|
|
281
|
+
- Typed text in fields is 16px on touch screens (`pointer: coarse`), whatever the field's size: below 16px, iOS Safari zooms the page in when a field takes focus. The field keeps its height; its label and placeholder layout do not change.
|
|
280
282
|
|
|
281
283
|
## Fonts
|
|
282
284
|
|
|
283
285
|
| Family | Stack |
|
|
284
286
|
| --- | --- |
|
|
285
|
-
| `--mu-font-sans` |
|
|
286
|
-
| `--mu-font-display` |
|
|
287
|
+
| `--mu-font-sans` | `-apple-system, BlinkMacSystemFont, "Segoe UI (mu)", system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif` |
|
|
288
|
+
| `--mu-font-display` | `-apple-system, BlinkMacSystemFont, "Segoe UI (mu)", system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif` |
|
|
287
289
|
| `--mu-font-mono` | `ui-monospace, "SF Mono", Menlo, Consolas, monospace` |
|
|
288
290
|
|
|
289
|
-
The package ships no font files and loads no remote font: text uses the system UI font, so it looks native on each platform and costs no download. An app that wants another font sets `--mu-font-sans`, `--mu-font-display` or `--mu-font-mono` after loading `tokens.css`, and loads the font itself. These are the only type tokens an app may set: sizes, line heights, weights and letter spacing are fixed (see [What an app can change](color.md#what-an-app-can-change)). Code in [Prose](components/prose.md) uses `font-mono`, the platform's monospace font by default.
|
|
291
|
+
The package ships no font files and loads no remote font: text uses the system UI font, so it looks native on each platform and costs no download. Do not import or define another font family. The one exception is `"Segoe UI (mu)"`, an alias of the installed Windows UI font that the stylesheet defines with `local()` sources only, so it loads no file. Segoe UI has Regular, Semibold and Bold faces but no Medium, so with a plain stack the browser draws weight 500 at 400 and every label on Windows loses its weight. The alias draws 500 and 600 with the installed Semibold face. It comes after `-apple-system` and `BlinkMacSystemFont`, so Apple platforms still get San Francisco, and before `system-ui`, which is plain Segoe UI on Windows. An app that wants another font sets `--mu-font-sans`, `--mu-font-display` or `--mu-font-mono` after loading `tokens.css`, and loads the font itself. These are the only type tokens an app may set: sizes, line heights, weights and letter spacing are fixed (see [What an app can change](color.md#what-an-app-can-change)). Code in [Prose](components/prose.md) uses `font-mono`, the platform's monospace font by default.
|
|
290
292
|
|
|
291
293
|
## Scale
|
|
292
294
|
|
|
@@ -327,7 +329,7 @@ The scale used to have 12 sizes and 3 weights. These styles were removed; use th
|
|
|
327
329
|
|
|
328
330
|
## Prose
|
|
329
331
|
|
|
330
|
-
Rendered content (notes, comments, changelogs, website pages) uses the `Prose` styles, not the scale above. It uses
|
|
332
|
+
Rendered content (notes, comments, changelogs, website pages) uses the `Prose` styles, not the scale above. It uses 16px text (14px at `sm`) and 13px table heads, and has three heading sizes: 24px `h1`, 20px `h2`, and 16px `h3`–`h6`. 24px appears nowhere else. The reason: notes have sections and subsections, and when `h2` and `h3` were both the size of the text they read as one level, and as bold sentences. At `sm`, `h1` is 16px and `h2`–`h6` are 14px. See [Prose](components/prose.md).
|
|
331
333
|
|
|
332
334
|
<!-- docs/layout.md -->
|
|
333
335
|
|
|
@@ -343,7 +345,7 @@ Every control takes one `size` prop: `xs`, `sm` or `md` (the default). There is
|
|
|
343
345
|
| sm | `control-sm` 24px | 8px | 14px | 4px | 13 / 18px | `radius-sm` 4px | 16px |
|
|
344
346
|
| md (default) | `control-md` 32px | 12px | 16px | 6px | 14 / 20px | `radius-md` 6px | 18px |
|
|
345
347
|
|
|
346
|
-
- Use `sm` for dense toolbars, table rows and filters inside panels, and `md` everywhere else, including marketing pages, onboarding
|
|
348
|
+
- Use `sm` for dense toolbars, table rows and filters inside panels, and `md` everywhere else, including marketing pages, onboarding and sheets. A sidebar with 28px rows puts the `mu-compact` class on itself: md controls and rows inside are `control-compact` (28px) tall, with md text, icons, padding and radius (see [A sidebar navigation row](usage.md#a-sidebar-navigation-row)). It is the only height between sizes, and it is not a `size` value. Emphasis comes from the `primary` variant, not from a bigger control. On touch screens the hit area, not the visual size, grows.
|
|
347
349
|
- Use `xs` for secondary actions beside 13px text: actions on a row or an entry, inline formatting, filters in a tight header, checkboxes in a dense list. It is the height of a Badge or a Kbd, so they line up. Don't use `xs` for forms, for navigation, or for the main action of a screen.
|
|
348
350
|
- Only Button, Toggle, ToggleGroup, Checkbox, RadioGroup and Radio, Switch, Avatar, Spinner and Skeleton take `xs`. Text entry, menus, tabs and tables are too cramped at 20px, so their `size` is `sm | md`, and an `xs` inherited from a parent renders them at `sm`.
|
|
349
351
|
- The icon side of a control has 2px less padding, so text and icon look centered.
|
|
@@ -396,6 +398,7 @@ Buttons, inputs and badges are never pills.
|
|
|
396
398
|
| `control-xs` | 20px | xs control height; Badge and Kbd |
|
|
397
399
|
| `control-sm` | 24px | sm control height |
|
|
398
400
|
| `control-md` | 32px | Default control height |
|
|
401
|
+
| `control-compact` | 28px | md height inside `mu-compact`, for an app's sidebar |
|
|
399
402
|
| `inset` | 2px | Well-to-key inset at every size |
|
|
400
403
|
| `hit-area` | 44px | Minimum touch target on `pointer: coarse`, added invisibly around a control |
|
|
401
404
|
| `focus-width` | 2px | Width of the keyboard focus ring |
|
|
@@ -404,6 +407,7 @@ Buttons, inputs and badges are never pills.
|
|
|
404
407
|
| `icon-xs` | 12px | Icons in xs controls and badges |
|
|
405
408
|
| `icon-sm` | 14px | Icons in sm controls |
|
|
406
409
|
| `icon-md` | 16px | Icons in md controls and nav rows |
|
|
410
|
+
| `icon-stroke` | 1.25px | Stroke of every icon, in screen pixels, at every size. Only the checkbox check differs (2px) |
|
|
407
411
|
|
|
408
412
|
## Layout
|
|
409
413
|
|
|
@@ -522,11 +526,11 @@ import { Pin, Trash2 } from "lucide-react";
|
|
|
522
526
|
|
|
523
527
|
- **Icons are elements, not names.** Components take `icon={<Pin />}`, never `icon="pin"`.
|
|
524
528
|
- **Size comes from the control.** An icon inside a component is 12px at xs, 14px at sm and 16px at md (`icon-xs`, `icon-sm`, `icon-md`). Don't pass `size`.
|
|
525
|
-
- **
|
|
529
|
+
- **One stroke, fixed in screen pixels.** Every icon, at every size and in badges, draws the `icon-stroke` token: 1.25px. The check in a checkbox is the one exception, at 2px. Apps don't change the token. At 1.25px a 16px outline glyph (calendar, paperclip, search) reads no heavier than the 14px weight-400 text beside it: an icon puts more ink in its box than a letter does, so matching the letters' stem width would read heavier. CSS applies `vector-effect: non-scaling-stroke`, which matches Lucide's `absoluteStrokeWidth`, so no props are needed. Don't pass `strokeWidth`.
|
|
526
530
|
- **Color is `currentColor`.** Icons follow the text color: `ink` in controls, `ink-muted` in menu rows, the tone color in toasts.
|
|
527
531
|
- **Icons sit in a fixed square.** In a menu row or a control, the icon square keeps labels aligned whether or not a row has an icon.
|
|
528
532
|
- **Accessibility.** Icons are `aria-hidden`. Every icon-only Button or Toggle has an `aria-label`, and a Tooltip with the same text.
|
|
529
|
-
- **Standalone icons.** Outside a component, give the icon a size class: `mu-icon-xs` (12px
|
|
533
|
+
- **Standalone icons.** Outside a component, give the icon a size class: `mu-icon-xs` (12px), `mu-icon-sm` (14px) or `mu-icon-md` (16px), all with the 1.25px stroke. Each sets the box and the stroke together, so a heading's icon or an empty state's icon matches the icons in controls. `mu-icon` alone takes the size of the nearest size row (`mu-s-*`), or 16px outside one. Don't size an icon with a utility such as `size-4`: it changes the box but not the stroke.
|
|
530
534
|
|
|
531
535
|
```tsx
|
|
532
536
|
<h2 className="mu-title"><Inbox className="mu-icon-sm" aria-hidden /> Inbox</h2>
|
|
@@ -556,12 +560,12 @@ When Lucide doesn't have an icon, draw it on Lucide's 24px grid with 2px round s
|
|
|
556
560
|
|
|
557
561
|
# Components
|
|
558
562
|
|
|
559
|
-
|
|
563
|
+
31 components in five groups, chosen from what Base UI and other popular libraries provide and what our products need today. Every component is generic: it works the same in memos, the website, the web clipper and any future product.
|
|
560
564
|
|
|
561
565
|
| Group | Components |
|
|
562
566
|
| --- | --- |
|
|
563
567
|
| Actions | [Button](button.md), [Toggle](toggle.md), [ToggleGroup](toggle-group.md), [Menu](menu.md), [Link](link.md) |
|
|
564
|
-
| Forms | [Field and Fieldset](field.md), [Input](input.md), [Textarea](textarea.md), [Select](select.md), [Combobox](combobox.md), [Checkbox](checkbox.md), [Switch](switch.md), [RadioGroup](radio-group.md) |
|
|
568
|
+
| Forms | [Form](form.md), [Field and Fieldset](field.md), [Input](input.md), [Textarea](textarea.md), [Select](select.md), [Combobox](combobox.md), [Checkbox](checkbox.md), [Switch](switch.md), [RadioGroup](radio-group.md) |
|
|
565
569
|
| Navigation | [Tabs](tabs.md), [Collapsible](collapsible.md) |
|
|
566
570
|
| Overlays | [Dialog](dialog.md) (and its sheet form), [Drawer](drawer.md), [Popover](popover.md), [Tooltip](tooltip.md), [Toast](toast.md) |
|
|
567
571
|
| Display | [Avatar](avatar.md), [Badge](badge.md), [Kbd](kbd.md), [Separator](separator.md), [Spinner](spinner.md), [Skeleton](skeleton.md), [Table](table.md), [List](list.md), [Prose](prose.md), plus [Lucide icons](../icons.md) |
|
|
@@ -581,7 +585,7 @@ Components follow the [API conventions](../building.md#api-conventions): parts w
|
|
|
581
585
|
|
|
582
586
|
## Left out on purpose
|
|
583
587
|
|
|
584
|
-
- **Card.** Content is grouped by spacing, headings and separators. The only boxes are a [List](list.md) of settings or records
|
|
588
|
+
- **Card.** Content is grouped by spacing, headings and separators. The only boxes are a [List](list.md) of settings or records, a `bordered` [Table](table.md) and a `bordered` [Form](form.md) for a small form among them.
|
|
585
589
|
- **Slider, Stepper.** No setting in our products needs a continuous range. Use an Input with `type="number"` or a Select.
|
|
586
590
|
- **Chip, SegmentedControl, Accordion.** A Toggle covers filter chips, a single-choice ToggleGroup covers segmented choices, and stacked Collapsibles cover an accordion.
|
|
587
591
|
- **Later, when a screen needs them:** multi-select Combobox, ContextMenu, ScrollArea, Progress, Calendar.
|
|
@@ -610,6 +614,7 @@ The short version. [Using the components](../usage.md) has the full decision gui
|
|
|
610
614
|
| Confirm something happened | Toast |
|
|
611
615
|
| Settings rows, or a short list of records with actions | List |
|
|
612
616
|
| Data with columns | Table |
|
|
617
|
+
| A small form (heading, a few fields, buttons) among Lists on a settings page | Form with `bordered` |
|
|
613
618
|
| A status or tag label | Badge |
|
|
614
619
|
| A clickable tag filter | Toggle |
|
|
615
620
|
| Content still loading | Skeleton |
|
|
@@ -757,7 +762,8 @@ import { Bold, Code, Italic } from "lucide-react";
|
|
|
757
762
|
|
|
758
763
|
```ts
|
|
759
764
|
interface ToggleGroupProps extends BaseToggleGroup.Props {
|
|
760
|
-
size?: Size;
|
|
765
|
+
size?: Size; // passed to every Toggle inside
|
|
766
|
+
fullWidth?: boolean; // fills the container; labeled Toggles share the width equally
|
|
761
767
|
}
|
|
762
768
|
```
|
|
763
769
|
|
|
@@ -770,10 +776,11 @@ Base UI's props pass through: `value`, `defaultValue`, `onValueChange`, `multipl
|
|
|
770
776
|
- Without `multiple`: filters and view switches that don't change the whole page. For sections of a page, use Tabs.
|
|
771
777
|
- Base UI lets people press the chosen item to clear it. When one item must stay chosen, ignore an empty value, as in the example.
|
|
772
778
|
- Arrow keys move focus between items; Space or Enter presses one.
|
|
779
|
+
- Set `fullWidth` when the group is the only control on its row and should line up with the edges, such as a view switch across a popover. Don't set `className="w-full"` and `flex-1` on the Toggles yourself.
|
|
773
780
|
|
|
774
781
|
## Style
|
|
775
782
|
|
|
776
|
-
- At most as wide as its container. When the labels don't fit, they shrink with an ellipsis; icon-only items keep their size.
|
|
783
|
+
- At most as wide as its container. With `fullWidth`, exactly as wide, and labeled Toggles take equal shares (icon-only ones keep their size). When the labels don't fit, they shrink with an ellipsis; icon-only items keep their size.
|
|
777
784
|
- `multiple`: items 2px apart.
|
|
778
785
|
- Without `multiple`: a segmented well; the key slides to the chosen item over `duration-settle` with `ease-settle` and re-measures when an item changes width. Under reduced motion it jumps.
|
|
779
786
|
|
|
@@ -907,6 +914,72 @@ Provide `href` and `children`.
|
|
|
907
914
|
|
|
908
915
|
- `accent` text that underlines on hover: the one place the hue appears at rest. `muted` uses `ink-muted`.
|
|
909
916
|
|
|
917
|
+
<!-- docs/components/form.md -->
|
|
918
|
+
|
|
919
|
+
# Form
|
|
920
|
+
|
|
921
|
+
A `<form>` with a title, its fields and its buttons. `bordered` boxes a small form among the [Lists](list.md) of a settings page.
|
|
922
|
+
|
|
923
|
+
## Example
|
|
924
|
+
|
|
925
|
+
```tsx
|
|
926
|
+
import { Button, Field, Form, Input, Switch } from "@usememos/ui";
|
|
927
|
+
|
|
928
|
+
<Form.Root bordered onSubmit={addRule}>
|
|
929
|
+
<Form.Title>Add a tag rule</Form.Title>
|
|
930
|
+
<Form.Description>Tag new notes that match a word.</Form.Description>
|
|
931
|
+
<Field.Root name="tag">
|
|
932
|
+
<Field.Label>Tag</Field.Label>
|
|
933
|
+
<Input required />
|
|
934
|
+
</Field.Root>
|
|
935
|
+
<Field.Root orientation="horizontal">
|
|
936
|
+
<Field.Label>Apply to existing notes</Field.Label>
|
|
937
|
+
<Switch name="existing" />
|
|
938
|
+
</Field.Root>
|
|
939
|
+
<Form.Footer>
|
|
940
|
+
<Button variant="ghost" onClick={cancel}>Cancel</Button>
|
|
941
|
+
<Button variant="primary" type="submit">Add rule</Button>
|
|
942
|
+
</Form.Footer>
|
|
943
|
+
</Form.Root>
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
## Parts
|
|
947
|
+
|
|
948
|
+
| Part | Base UI part | Renders |
|
|
949
|
+
| --- | --- | --- |
|
|
950
|
+
| `Form.Root` | `Form` | A `<form>`. Takes `bordered`. Named by its Title |
|
|
951
|
+
| `Form.Title` | none (ours) | An `<h2>` in `label` style. Takes `render` for another heading level |
|
|
952
|
+
| `Form.Description` | none (ours) | A `<p>` under the title |
|
|
953
|
+
| `Form.Footer` | none (ours) | A `<div>` that right-aligns the buttons |
|
|
954
|
+
|
|
955
|
+
## Props
|
|
956
|
+
|
|
957
|
+
```ts
|
|
958
|
+
interface Form.RootProps extends BaseForm.Props {
|
|
959
|
+
bordered?: boolean; // a List's edge around the form, and the Footer as a canvas bar
|
|
960
|
+
}
|
|
961
|
+
interface Form.TitleProps extends React.ComponentProps<"h2"> {
|
|
962
|
+
render?: React.ReactElement; // such as <h3 /> under a page section
|
|
963
|
+
}
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
Base UI's props pass through: `onFormSubmit`, `errors`, `validationMode` and `actionsRef`, as do the native `<form>` attributes (`onSubmit`, `action`).
|
|
967
|
+
|
|
968
|
+
## Rules
|
|
969
|
+
|
|
970
|
+
- Set `bordered` for a small form (a heading, two to four fields and its buttons) on a settings page, next to [Lists](list.md) or a `bordered` [Table](table.md), such as "Add a tag rule" under the rules it adds to. Leave it off for a form that is the page's main content.
|
|
971
|
+
- Never put a bordered Form inside a Dialog, Popover or Drawer: those are boxes already. A form in a Dialog uses the Dialog's own Title and Footer; see [Form in a dialog](../usage.md#form-in-a-dialog).
|
|
972
|
+
- Don't box single settings: a setting that saves at once is a horizontal [Field](field.md) in a List.
|
|
973
|
+
- Footer: a ghost Cancel, then one `primary` Button with `type="submit"` named with its verb ("Add rule", never "OK" or "Submit").
|
|
974
|
+
- Give each Field a `name`; errors from the server go in `errors`, keyed by name, and appear under their fields.
|
|
975
|
+
- Name it with a Title, or `aria-label` on the root when there is no visible heading.
|
|
976
|
+
|
|
977
|
+
## Style
|
|
978
|
+
|
|
979
|
+
- Children stack `space-4` apart. Title: `label` (14px, 500); Description: `caption` in `ink-muted`, `space-1` under the title. Footer: buttons `space-2` apart at the end.
|
|
980
|
+
- `bordered`: `surface` fill, a 1px `line` edge, `radius-lg`, `space-4` padding, no shadow, as a List. The Footer runs to the edges as a `canvas` bar under a `line`, padded `space-3` × `space-4`, rounded with the box at the bottom.
|
|
981
|
+
- The form is always md: controls inside it ignore the size of a parent Root.
|
|
982
|
+
|
|
910
983
|
<!-- docs/components/field.md -->
|
|
911
984
|
|
|
912
985
|
# Field and Fieldset
|
|
@@ -983,7 +1056,7 @@ Base UI's props pass through: `name`, `invalid`, `disabled`, `validate`, `valida
|
|
|
983
1056
|
## Style
|
|
984
1057
|
|
|
985
1058
|
- Label: `label` style (14px, 500), `space-1` above the control; weight 400 when it wraps a Checkbox, Switch or Radio.
|
|
986
|
-
- Horizontal: a two-column grid. The label (weight 400) and description fill the first column; the control sits at the end, centered on them, `space-4` from the text. The error spans the row under them.
|
|
1059
|
+
- Horizontal: a two-column grid. The label (weight 400, as every settings row name is; see [Page](../patterns.md#page)) and description fill the first column; the control sits at the end, centered on them, `space-4` from the text. The error spans the row under them.
|
|
987
1060
|
- Description and error: `caption` (13px), below the control. The error hides the description and turns the control's edge `danger`.
|
|
988
1061
|
|
|
989
1062
|
<!-- docs/components/input.md -->
|
|
@@ -1027,7 +1100,7 @@ Wraps Base UI's [Input](https://base-ui.com/react/components/input), so inside a
|
|
|
1027
1100
|
## Style
|
|
1028
1101
|
|
|
1029
1102
|
- Flat `surface` with a 1px `edge` and a faint inner shadow from the top. Never a gray well, so it never reads as disabled.
|
|
1030
|
-
- Typed text weight 400; placeholder `ink-muted`.
|
|
1103
|
+
- Typed text weight 400; placeholder `ink-muted`. On touch screens typed text is 16px at every size, so iOS Safari does not zoom in on focus; the field keeps its height.
|
|
1031
1104
|
- Hover darkens the edge to `ink-muted`; focus turns it into a 1.5px `focus-edge` (ink), on click and keyboard alike. There is no ring around the field. The background never changes.
|
|
1032
1105
|
- The side with a prefix or suffix has 2px less padding.
|
|
1033
1106
|
|
|
@@ -1070,6 +1143,7 @@ Base UI has no textarea part, so this renders Base UI's `Field.Control` as a `<t
|
|
|
1070
1143
|
## Style
|
|
1071
1144
|
|
|
1072
1145
|
- Looks like [Input](input.md). Resizes vertically only.
|
|
1146
|
+
- On touch screens typed text is 16px on a 20px line at every size, like Input, so iOS Safari does not zoom in on focus.
|
|
1073
1147
|
|
|
1074
1148
|
<!-- docs/components/select.md -->
|
|
1075
1149
|
|
|
@@ -1139,7 +1213,7 @@ Base UI's props pass through: `items`, `value`, `defaultValue`, `onValueChange`,
|
|
|
1139
1213
|
- Label it with a [Field](field.md), or `aria-label` on the trigger.
|
|
1140
1214
|
- Use [RadioGroup](radio-group.md) for two or three options with room to show them, and [Combobox](combobox.md) for more than about 10 or when people type to find.
|
|
1141
1215
|
- For actions, use [Menu](menu.md).
|
|
1142
|
-
- `variant="ghost"` draws the trigger as a row instead of a key, for a value people switch in place, such as the current space in a sidebar. Put an icon or Avatar before `Select.Value`. In a form, keep `neutral`.
|
|
1216
|
+
- `variant="ghost"` draws the trigger as a row instead of a key, for a value people switch in place, such as the current space in a sidebar. In a sidebar with `mu-compact` it is 28px tall, like the rows around it. Put an icon or Avatar before `Select.Value`. In a form, keep `neutral`.
|
|
1143
1217
|
|
|
1144
1218
|
## Style
|
|
1145
1219
|
|
|
@@ -1552,10 +1626,11 @@ Dialog and AlertDialog have the same parts.
|
|
|
1552
1626
|
interface Dialog.PopupProps extends BaseDialog.Popup.Props {
|
|
1553
1627
|
sheet?: boolean; // below breakpoint-sm, a sheet from the bottom; default true
|
|
1554
1628
|
variant?: "panel" | "bare"; // default "panel"; bare: no padding, gap or X (Dialog only)
|
|
1629
|
+
fullScreen?: boolean; // covers the whole screen with square corners, at every width (Dialog only)
|
|
1555
1630
|
closeLabel?: string; // accessible name of the X; default "Close"
|
|
1556
1631
|
container?: HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null; // portal target
|
|
1557
1632
|
}
|
|
1558
|
-
// AlertDialog.Popup takes sheet and container; it has no X and no
|
|
1633
|
+
// AlertDialog.Popup takes sheet and container; it has no X, no variant and no fullScreen.
|
|
1559
1634
|
```
|
|
1560
1635
|
|
|
1561
1636
|
Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `modal`, `disablePointerDismissal` on the root; `initialFocus`, `finalFocus` on the popup.
|
|
@@ -1568,6 +1643,8 @@ Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `modal`, `d
|
|
|
1568
1643
|
- Escape and a backdrop click close a Dialog. While a form has unsaved input, keep it open from `onOpenChange`.
|
|
1569
1644
|
- Focus moves into the dialog and returns to the trigger on close.
|
|
1570
1645
|
- `variant="bare"` is for content that runs edge to edge: a search palette, a picker with its own header, an image. The content sets its own padding. Name the dialog with a `Dialog.Title` (hidden with `mu-visually-hidden` if needed) or `aria-label` on the popup; Escape and a backdrop click still close it.
|
|
1646
|
+
- Every Dialog has the same backdrop, `overlay`, including a search palette. There is no prop to lighten or remove it.
|
|
1647
|
+
- `fullScreen` is for content that needs the whole screen, such as an image viewer (`variant="bare" fullScreen`). It replaces the sheet on phones. The content handles the safe-area insets. Don't add `fixed inset-0` or `rounded-none` yourself.
|
|
1571
1648
|
- The panel is always md: controls inside it ignore the size of a Root the Dialog is declared in (a sm Table). Pass `size` to a control inside only when it should differ.
|
|
1572
1649
|
|
|
1573
1650
|
## Style
|
|
@@ -1577,6 +1654,7 @@ Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `modal`, `d
|
|
|
1577
1654
|
- Fades in and rises `space-1` over `duration-settle`. A sheet slides up `space-6`, with square bottom corners and full-width buttons stacked with the action on top. No movement under reduced motion.
|
|
1578
1655
|
- Long content scrolls the viewport around the panel.
|
|
1579
1656
|
- `bare`: no padding, no gap and no X; the content is clipped to the radius. Set the width with `className` when it differs from `dialog-width`.
|
|
1657
|
+
- `fullScreen`: the viewport's padding goes, and the popup fills it with square corners.
|
|
1580
1658
|
|
|
1581
1659
|
<!-- docs/components/drawer.md -->
|
|
1582
1660
|
|
|
@@ -1618,7 +1696,7 @@ interface Drawer.RootProps extends BaseDrawer.Root.Props {
|
|
|
1618
1696
|
}
|
|
1619
1697
|
interface Drawer.PopupProps extends BaseDrawer.Popup.Props {
|
|
1620
1698
|
variant?: "panel" | "bare"; // default "panel"; bare: no padding, gap or X
|
|
1621
|
-
closeLabel?: string; // accessible name of the X; default "Close"
|
|
1699
|
+
closeLabel?: string; // accessible name of the X, or of bare's screen-reader Close; default "Close"
|
|
1622
1700
|
container?: HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null; // portal target
|
|
1623
1701
|
}
|
|
1624
1702
|
```
|
|
@@ -1629,15 +1707,16 @@ Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `onOpenChan
|
|
|
1629
1707
|
|
|
1630
1708
|
- Use it for content that belongs beside the page rather than on top of it: the navigation sidebar below `breakpoint-md`, or the details of one item. For a decision or a short form, use [Dialog](dialog.md), which already becomes a sheet from the bottom on phones.
|
|
1631
1709
|
- Name it: a `Drawer.Title`, hidden with `mu-visually-hidden` when the content names itself, or `aria-label` on the popup.
|
|
1632
|
-
- `variant="bare"` is for content that lays itself out, such as an app's sidebar. Set its width with `className` when the content has its own, such as `sidebar-width`.
|
|
1710
|
+
- `variant="bare"` is for content that lays itself out, such as an app's sidebar. Set its width with `className` when the content has its own, such as `sidebar-width`. From the start or end, its content is exactly the drawer's height: give the app's sidebar `height: 100%` and let a region inside it scroll (`flex: 1; min-height: 0; overflow-y: auto`). Don't set `100dvh` or add a Close or an edge yourself: bare has a Close for screen readers (out of the tab order; keyboards use Escape) and the drawer draws its own edge.
|
|
1633
1711
|
- Close it when people pick a destination: wrap each link in `Drawer.Close render={<a … />}`, or close it from the router.
|
|
1634
1712
|
- Escape, a backdrop click and a swipe toward its edge close it. Focus moves in and returns to the trigger.
|
|
1635
1713
|
- The panel is always md, as a Dialog is.
|
|
1636
1714
|
|
|
1637
1715
|
## Style
|
|
1638
1716
|
|
|
1639
|
-
- `surface` with `shadow-pop`, full height and `drawer-width` (320px) wide from the start or end edge, at most the screen width minus `space-12`. Square corners; from the bottom it is full width with `radius-2xl` top corners and at most the screen height minus `space-6`.
|
|
1717
|
+
- `surface` with `shadow-pop`, full height and `drawer-width` (320px) wide from the start or end edge, with a 1px `line` edge on its inner side (the side facing the page), as a fixed sidebar has, at most the screen width minus `space-12`. Square corners; from the bottom it is full width with `radius-2xl` top corners and at most the screen height minus `space-6`.
|
|
1640
1718
|
- `panel`: content `space-5` in, stacked `space-4` apart; the X is a small ghost Button at `space-4` from the top end corner. Title: `title`. Description: `body` in `ink-muted`.
|
|
1719
|
+
- `bare`: no padding, gap or X. A visually hidden `Drawer.Close` named `closeLabel` follows the content, so screen readers on touch screens can close it.
|
|
1641
1720
|
- The backdrop is `overlay`; it fades as the drawer is swiped away.
|
|
1642
1721
|
- Slides in from its edge over `duration-settle` with `ease-settle` and follows the finger while swiped. Under reduced motion it fades instead.
|
|
1643
1722
|
|
|
@@ -1691,7 +1770,7 @@ Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `modal` on
|
|
|
1691
1770
|
|
|
1692
1771
|
- Use it for a few controls that belong to one thing. For a list of actions, use [Menu](menu.md). For a decision that blocks the page, use [Dialog](dialog.md).
|
|
1693
1772
|
- Give it a `Popover.Title`, or `aria-label` on the popup.
|
|
1694
|
-
- `variant="list"` is for a panel made of rows, such as an account panel (a name, then links) or a scope picker with a header. Draw the rows with `menuClasses().item` and the lines between them with `menuClasses().separator`. When the rows are only actions, use a [Menu](menu.md) instead.
|
|
1773
|
+
- `variant="list"` is for a panel made of rows, such as an account panel (a name, then links) or a scope picker with a header. Draw the rows with `menuClasses().item` and the lines between them with `menuClasses().separator`. When the rows are only actions, use a [Menu](menu.md) instead. For 28px rows beside a sidebar with `mu-compact`, put `mu-compact` on the popup too: `className="mu-compact"`.
|
|
1695
1774
|
- The panel is always md: controls inside it ignore the size of the Root around the Popover (a sm toolbar). Pass `size` to a control inside only when it should differ.
|
|
1696
1775
|
- It closes on Escape and on a click outside; focus returns to the trigger.
|
|
1697
1776
|
- An app's own floating panel, such as an emoji picker, takes `popoverClasses()`. See [Class functions](../custom-components.md#class-functions).
|
|
@@ -1879,31 +1958,32 @@ import { Hash, Lock } from "lucide-react";
|
|
|
1879
1958
|
<Badge icon={<Hash />}>reading</Badge>
|
|
1880
1959
|
<Badge tone="outline" icon={<Lock />}>Private</Badge>
|
|
1881
1960
|
<Badge tone="success">Synced</Badge>
|
|
1961
|
+
<Badge tone="primary">New</Badge>
|
|
1882
1962
|
```
|
|
1883
1963
|
|
|
1884
1964
|
## Props
|
|
1885
1965
|
|
|
1886
1966
|
```ts
|
|
1887
1967
|
interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
1888
|
-
tone?: Tone | "outline"; // default "neutral"
|
|
1968
|
+
tone?: Tone | "outline" | "primary"; // default "neutral"
|
|
1889
1969
|
icon?: IconElement;
|
|
1890
1970
|
children: React.ReactNode; // one or two words
|
|
1891
1971
|
render?: React.ReactElement;
|
|
1892
1972
|
}
|
|
1893
1973
|
|
|
1894
|
-
function badgeClasses(options?: { tone?: Tone | "outline" }): string;
|
|
1974
|
+
function badgeClasses(options?: { tone?: Tone | "outline" | "primary" }): string;
|
|
1895
1975
|
```
|
|
1896
1976
|
|
|
1897
1977
|
## Rules
|
|
1898
1978
|
|
|
1899
|
-
- Tags: `neutral` with a `Hash` icon. Visibility: `outline` with its icon. Status: `success`, `warning` or `danger`, always with a word, never color alone.
|
|
1979
|
+
- Tags: `neutral` with a `Hash` icon. Visibility: `outline` with its icon. Status: `success`, `warning` or `danger`, always with a word, never color alone. A label that should stand out, such as New on a feature: `primary`, in ink, the system's one emphasis. Use it for one badge in a view at most, and never for a status.
|
|
1900
1980
|
- A badge is not a button. A clickable tag filter is a [Toggle](toggle.md).
|
|
1901
1981
|
- An app's own element that should look like a badge, such as a tag that is also a link, takes `badgeClasses()`. See [Class functions](../custom-components.md#class-functions).
|
|
1902
1982
|
|
|
1903
1983
|
## Style
|
|
1904
1984
|
|
|
1905
1985
|
- `control-xs` (20px) tall, `radius-sm` (never a pill), `label-xs` text (12px, 500), optional `icon-xs` (12px) icon at a 1.25px stroke. It lines up with xs controls.
|
|
1906
|
-
- `neutral`: `ink` on `well`. Status tones: their `-soft` background with full-strength text.
|
|
1986
|
+
- `neutral`: `ink` on `well`. `primary`: `on-primary` on `primary`, the colors of a primary Button. Status tones: their `-soft` background with full-strength text.
|
|
1907
1987
|
|
|
1908
1988
|
<!-- docs/components/kbd.md -->
|
|
1909
1989
|
|
|
@@ -2100,6 +2180,7 @@ No Base UI part: these are styled native table elements.
|
|
|
2100
2180
|
| `Table.Row` | `<tr>` |
|
|
2101
2181
|
| `Table.Head` | `<th scope="col">`. Takes `num` |
|
|
2102
2182
|
| `Table.Cell` | `<td>`. Takes `num` and `muted` |
|
|
2183
|
+
| `Table.Actions` | A `<div>` that lays two or more row actions side by side in one Cell |
|
|
2103
2184
|
|
|
2104
2185
|
## Props
|
|
2105
2186
|
|
|
@@ -2124,6 +2205,16 @@ Every native attribute passes through.
|
|
|
2124
2205
|
- Use it for data with columns. Lists of content (notes, comments) are [entries](../patterns.md#entry), not rows.
|
|
2125
2206
|
- Set `num` on a numeric column's Head and on its Cells.
|
|
2126
2207
|
- Put row actions in the last column as a ghost [Menu](menu.md) trigger, named for its row. Give that column's Head text in `mu-visually-hidden`.
|
|
2208
|
+
- Two or three frequent actions, such as Edit and Delete, go in a `Table.Actions` inside that Cell, as ghost Buttons. Put a single Button straight in the Cell:
|
|
2209
|
+
|
|
2210
|
+
```tsx
|
|
2211
|
+
<Table.Cell num>
|
|
2212
|
+
<Table.Actions>
|
|
2213
|
+
<Button variant="ghost" size="sm" icon={<Pencil />} aria-label={`Edit ${hook.name}`} />
|
|
2214
|
+
<Button variant="ghost" size="sm" icon={<Trash2 />} aria-label={`Delete ${hook.name}`} />
|
|
2215
|
+
</Table.Actions>
|
|
2216
|
+
</Table.Cell>
|
|
2217
|
+
```
|
|
2127
2218
|
- Name the table with a Caption, or `aria-label` on the root.
|
|
2128
2219
|
- Sort, select and paginate in the app. The table has no data props.
|
|
2129
2220
|
- Set `bordered` when the table is one section of a settings or admin page, next to other sections or a [List](list.md), so it reads as one block. Leave it off when the table is the page's main content. Never put a bordered table inside a Dialog or Popover.
|
|
@@ -2131,9 +2222,9 @@ Every native attribute passes through.
|
|
|
2131
2222
|
## Style
|
|
2132
2223
|
|
|
2133
2224
|
- No stripes: a `line` under the head row and between body rows. Body rows highlight with `hover`.
|
|
2134
|
-
- `bordered`: `surface` fill, a 1px `line` edge and `radius-lg` on the scrolling wrapper; the Caption sits inside the edge, under a `line`. The outer columns pad `space-1` more, so the text lines up with a [List](list.md) row's.
|
|
2225
|
+
- `bordered`: `surface` fill, a 1px `line` edge and `radius-lg` on the scrolling wrapper; the head row has a `canvas` fill, so it reads as the head inside the box; the Caption sits inside the edge, under a `line`. The outer columns pad `space-1` more, so the text lines up with a [List](list.md) row's.
|
|
2135
2226
|
- Heads: `label-sm` (13px, 500) in `ink-muted`, a control height tall.
|
|
2136
|
-
- Cells: `caption` at sm and `body` at md, with tabular numbers. Rows are 34 or 40px; a Button in a cell overlaps the padding, so rows keep that height.
|
|
2227
|
+
- Cells: `caption` at sm and `body` at md, with tabular numbers. Rows are 34 or 40px; a Button in a cell, or a `Table.Actions` group of them (`space-1` apart), overlaps the padding, so rows keep that height.
|
|
2137
2228
|
- Cells pad `px` at the sides. Caption: `caption` in `ink-muted`, `space-2` above.
|
|
2138
2229
|
|
|
2139
2230
|
<!-- docs/components/list.md -->
|
|
@@ -2173,6 +2264,7 @@ No Base UI part: these are styled native list elements.
|
|
|
2173
2264
|
| --- | --- |
|
|
2174
2265
|
| `List.Root` | A `<ul>` with a `line` edge and rounded corners. Takes `size` |
|
|
2175
2266
|
| `List.Item` | An `<li>`: one row, divided from the next by a `line` |
|
|
2267
|
+
| `List.ItemButton` | A `<button>` that fills its Item, for a row that acts as a whole. Takes `render` for a link |
|
|
2176
2268
|
|
|
2177
2269
|
## Props
|
|
2178
2270
|
|
|
@@ -2190,13 +2282,27 @@ Every native attribute passes through.
|
|
|
2190
2282
|
- A settings row is a horizontal [Field](field.md) inside the Item. A record row lays out its own content: the record's text first, its actions at the end (a ghost [Menu](menu.md) trigger named for the row, or one or two Buttons).
|
|
2191
2283
|
- Name the list: `aria-labelledby` pointing at the section title above it, or `aria-label`.
|
|
2192
2284
|
- Don't nest Lists, and don't put a List inside a Dialog or Popover: those are boxes already. Use horizontal Fields stacked `space-4` apart there.
|
|
2193
|
-
-
|
|
2285
|
+
- When the whole row opens something (a space, a record's page), put one `List.ItemButton` in the Item and the row's content inside it. For a link, `render={<a href="…" />}` or the router's link. Don't give a button negative margins to fill the row yourself.
|
|
2286
|
+
- A row with an ItemButton has no other controls: buttons don't nest. A row with its own actions (a Menu trigger, a Switch) keeps them in the Item and opens the record from a Link in its text.
|
|
2287
|
+
|
|
2288
|
+
```tsx
|
|
2289
|
+
<List.Root aria-label="Spaces">
|
|
2290
|
+
{spaces.map((space) => (
|
|
2291
|
+
<List.Item key={space.id}>
|
|
2292
|
+
<List.ItemButton render={<a href={`/s/${space.id}`} />}>
|
|
2293
|
+
<Avatar.Root size="sm">…</Avatar.Root>
|
|
2294
|
+
{space.name}
|
|
2295
|
+
</List.ItemButton>
|
|
2296
|
+
</List.Item>
|
|
2297
|
+
))}
|
|
2298
|
+
</List.Root>
|
|
2299
|
+
```
|
|
2194
2300
|
- An empty list shows the [empty state](../patterns.md#empty-state) in place of the List.
|
|
2195
2301
|
|
|
2196
2302
|
## Style
|
|
2197
2303
|
|
|
2198
2304
|
- `surface` fill, a 1px `line` edge, `radius-lg`. No shadow: it is flat, like a field.
|
|
2199
|
-
- Rows pad `space-3` × `space-4` at md and `space-2` × `space-3` at sm, divided by a 1px `line`. No hover
|
|
2305
|
+
- Rows pad `space-3` × `space-4` at md and `space-2` × `space-3` at sm, divided by a 1px `line`. No hover on a plain row. An ItemButton fills its row, padded as the row is, lays out its children in a row `space-3` apart, highlights with `hover`, and rounds with the box at the first and last row; keyboard focus shows the ink ring.
|
|
2200
2306
|
- Text is `body` in `ink`.
|
|
2201
2307
|
|
|
2202
2308
|
<!-- docs/components/prose.md -->
|
|
@@ -2240,8 +2346,8 @@ Every native attribute passes through.
|
|
|
2240
2346
|
|
|
2241
2347
|
## Style
|
|
2242
2348
|
|
|
2243
|
-
- Text: 16px on a 24px line (`sm`: 14 on 22), weight 400, `ink`. Blocks are `space-3` apart.
|
|
2244
|
-
- Headings: `h1` 20/28px, `
|
|
2349
|
+
- Text: 16px on a 24px line (`sm`: 14 on 22), weight 400, `ink`. Paragraphs and list items use `text-wrap: pretty`, so a last line is rarely one word. Blocks are `space-3` apart.
|
|
2350
|
+
- Headings: `h1` 24/32px, `h2` 20/28px, `h3`–`h6` 16/24px (`sm`: `h1` 16/24px, `h2`–`h6` 14/22px); all weight 600, balanced over their lines (`text-wrap: balance`), with `space-5` above and `space-2` below. Bold (`strong`, `b`) is 600 too. These are the only 600s in the system: people wrote them as bold, and 500 does not read as bold in system fonts.
|
|
2245
2351
|
- Links: `accent`, underlined at rest, because color alone does not set them apart from the text.
|
|
2246
2352
|
- Code: `--mu-font-mono` at 0.875em on `well`. Inline code has `radius-xs`; blocks have `radius-md`, `space-3` by `space-4` padding, and scroll sideways.
|
|
2247
2353
|
- Quotes: a 2px `edge` bar, text in `ink-muted`. Rules: a 1px `line` with `space-5` around it.
|
|
@@ -2249,7 +2355,7 @@ Every native attribute passes through.
|
|
|
2249
2355
|
- Images and video: never wider than the column, `radius-lg`.
|
|
2250
2356
|
- Tables follow [Table](table.md): `line` between rows, 13px `ink-muted` heads (weight 500), tabular numbers; they scroll sideways when too wide.
|
|
2251
2357
|
- Tags: `ink-muted` on `well`, `radius-xs`. They read as words, not badges.
|
|
2252
|
-
- Sizes are custom properties set on `.mu-prose` in the foundation (`--prose-fs`, `--prose-lh`,
|
|
2358
|
+
- Sizes are custom properties set on `.mu-prose` in the foundation (`--prose-fs`, `--prose-lh`, `--prose-h1`, `--prose-h2`, `--prose-h3` and their line heights).
|
|
2253
2359
|
|
|
2254
2360
|
<!-- docs/usage.md -->
|
|
2255
2361
|
|
|
@@ -2486,7 +2592,7 @@ const [confirming, setConfirming] = useState(false);
|
|
|
2486
2592
|
</List.Root>
|
|
2487
2593
|
```
|
|
2488
2594
|
|
|
2489
|
-
A horizontal Field puts the setting's name first and the Switch or Select at the end of the row. A [List](components/list.md) boxes the rows of one section and divides them with lines. Inside a Dialog, leave out the List and stack the Fields. For a short list of options under one heading (notification types), a Switch or Checkbox inside its `Field.Label` also works.
|
|
2595
|
+
A horizontal Field puts the setting's name first, at weight 400, and the Switch or Select at the end of the row. The section title above it is the `label` (500). A [List](components/list.md) boxes the rows of one section and divides them with lines. Inside a Dialog, leave out the List and stack the Fields. For a short list of options under one heading (notification types), a Switch or Checkbox inside its `Field.Label` also works.
|
|
2490
2596
|
|
|
2491
2597
|
No Save button and no toast: the Switch shows the result. If saving fails, move the Switch back and show a danger Toast.
|
|
2492
2598
|
|
|
@@ -2514,6 +2620,49 @@ One `Tooltip.Provider` around the toolbar (or the app) so moving along it opens
|
|
|
2514
2620
|
- Show one or two frequent actions as xs or sm ghost Buttons beside it, each with a Tooltip.
|
|
2515
2621
|
- On an entry the trigger appears on hover; on touch screens it is always visible (see [Entry](patterns.md#entry)).
|
|
2516
2622
|
|
|
2623
|
+
### A sidebar navigation row
|
|
2624
|
+
|
|
2625
|
+
The package ships no navigation component: the sidebar's rows are the app's links. For 28px rows, put `mu-compact` on the sidebar: every md control and row inside it is `control-compact` (28px) tall instead of 32px, with md text, icons, padding and radius. The app's own rows read the row variables, so they match the package's controls beside them:
|
|
2626
|
+
|
|
2627
|
+
```tsx
|
|
2628
|
+
<aside className="app-sidebar mu-compact">
|
|
2629
|
+
<Select.Root value={space} onValueChange={setSpace}>
|
|
2630
|
+
<Select.Trigger variant="ghost">…</Select.Trigger>
|
|
2631
|
+
…
|
|
2632
|
+
</Select.Root>
|
|
2633
|
+
<Button variant="primary" icon={<Plus />}>New note</Button>
|
|
2634
|
+
<nav className="app-nav mu-s-md mu-base" aria-label="Main">
|
|
2635
|
+
<a className="app-nav-row mu-focus" href="/" aria-current={path === "/" ? "page" : undefined}>
|
|
2636
|
+
<House className="mu-icon" aria-hidden />
|
|
2637
|
+
Home
|
|
2638
|
+
</a>
|
|
2639
|
+
…
|
|
2640
|
+
</nav>
|
|
2641
|
+
</aside>
|
|
2642
|
+
```
|
|
2643
|
+
|
|
2644
|
+
```css
|
|
2645
|
+
.app-nav { display: grid; gap: var(--mu-inset); }
|
|
2646
|
+
.app-nav-row {
|
|
2647
|
+
display: flex;
|
|
2648
|
+
align-items: center;
|
|
2649
|
+
gap: var(--mu-row-gap);
|
|
2650
|
+
height: var(--mu-row-height); /* 28px inside mu-compact, 32px without */
|
|
2651
|
+
padding: 0 var(--mu-row-padding-icon);
|
|
2652
|
+
border-radius: var(--mu-row-radius);
|
|
2653
|
+
color: var(--mu-ink-muted);
|
|
2654
|
+
text-decoration: none;
|
|
2655
|
+
}
|
|
2656
|
+
.app-nav-row:hover, .app-nav-row[aria-current="page"] { color: var(--mu-ink); background-color: var(--mu-hover); }
|
|
2657
|
+
```
|
|
2658
|
+
|
|
2659
|
+
- `mu-compact` changes only md. sm (24px) and xs (20px) controls inside keep their heights, so an xs ghost Button for a row action still fits.
|
|
2660
|
+
- Popups opened from the sidebar (the Select's list, a Menu) mount outside it, at the normal 32px. To give a panel of rows the same 28px, put `mu-compact` on its popup: `<Popover.Popup variant="list" className="mu-compact">`.
|
|
2661
|
+
- `mu-s-md` gives the rows 14px text, 16px icons with a 1.25px stroke, and md padding and radius; `mu-base` sets the font and weight 500. Never set the `--mu-row-*` variables or the size tokens yourself; `mu-compact` is the only way to change a row's height.
|
|
2662
|
+
- Mark the current page with `aria-current="page"`, not with color alone; the fill shows it too.
|
|
2663
|
+
- Rows stack `inset` (2px) apart. Don't add `mu-hit` to stacked rows: their 44px touch areas would overlap. 28px is above the 24px minimum target size.
|
|
2664
|
+
- A row with a count or a menu at its end adds it after the label with `margin-inline-start: auto`.
|
|
2665
|
+
|
|
2517
2666
|
### Buttons over a photo
|
|
2518
2667
|
|
|
2519
2668
|
Controls over an image or a video sit in a dark-theme scope, so they read on any picture in both app themes:
|
|
@@ -2530,8 +2679,37 @@ Controls over an image or a video sit in a dark-theme scope, so they read on any
|
|
|
2530
2679
|
```
|
|
2531
2680
|
|
|
2532
2681
|
- `data-theme="dark"` on any element sets the Dark tokens for everything inside it, so the Buttons are dark `neutral` keys with light icons. Don't use `ghost` there: it has no fill and disappears on a busy picture.
|
|
2533
|
-
-
|
|
2534
|
-
- A full-screen viewer is a Dialog with `variant="bare"`; put the scope inside its Popup.
|
|
2682
|
+
- When the scope holds only Buttons, leave its own background transparent: the keys carry their own fill and shadow.
|
|
2683
|
+
- A full-screen viewer is a Dialog with `variant="bare" fullScreen`; put the scope inside its Popup.
|
|
2684
|
+
|
|
2685
|
+
### Text over a photo
|
|
2686
|
+
|
|
2687
|
+
Plain text over a picture, such as a viewer's "3 / 12" counter or its zoom level, has no fill of its own, so it needs one. Put it in the same dark-theme scope and give the text a `surface` pill:
|
|
2688
|
+
|
|
2689
|
+
```tsx
|
|
2690
|
+
<div className="app-viewer-controls" data-theme="dark">
|
|
2691
|
+
<span className="app-viewer-label mu-label-sm" aria-live="polite">{index + 1} / {count}</span>
|
|
2692
|
+
<Button icon={<ZoomIn />} aria-label="Zoom in" onClick={zoomIn} />
|
|
2693
|
+
</div>
|
|
2694
|
+
```
|
|
2695
|
+
|
|
2696
|
+
```css
|
|
2697
|
+
.app-viewer-label {
|
|
2698
|
+
display: inline-flex;
|
|
2699
|
+
align-items: center;
|
|
2700
|
+
height: var(--mu-control-md);
|
|
2701
|
+
padding: 0 var(--mu-space-3);
|
|
2702
|
+
border-radius: var(--mu-radius-md);
|
|
2703
|
+
color: var(--mu-ink);
|
|
2704
|
+
background-color: var(--mu-surface);
|
|
2705
|
+
box-shadow: var(--mu-shadow-pop);
|
|
2706
|
+
font-variant-numeric: tabular-nums;
|
|
2707
|
+
}
|
|
2708
|
+
```
|
|
2709
|
+
|
|
2710
|
+
- The pill is opaque, as tall as the Buttons beside it, with their radius. Dark `surface` with `ink` text keeps 4.5:1 on any picture.
|
|
2711
|
+
- No blur and no translucent fill: glass behind text is not in the system (see [Depth](depth.md)), and it fails contrast on bright pictures.
|
|
2712
|
+
- Several labels and Buttons in one row can share one pill instead: put the `surface`, radius, padding and shadow on the scope, and the Buttons become `ghost` inside it, since the pill is their fill.
|
|
2535
2713
|
|
|
2536
2714
|
### Inside a shadow root or a host page
|
|
2537
2715
|
|
|
@@ -2545,7 +2723,7 @@ In an extension or an embed, pass the same `container` to every `Popup` and to `
|
|
|
2545
2723
|
## Size
|
|
2546
2724
|
|
|
2547
2725
|
- **Set `size` once, on the outermost part that takes it.** `Menu.Root`, `Select.Root`, `Combobox.Root`, `Tabs.Root`, `Collapsible.Root`, `Table.Root`, `Field.Root`, `Fieldset.Root`, `RadioGroup` and `ToggleGroup` pass their size to every part and every Button, Toggle, Checkbox or Input inside them. Don't repeat it on the parts.
|
|
2548
|
-
- **Pick by place, not by importance.** `md` everywhere by default, including dialogs, forms, onboarding, marketing pages and
|
|
2726
|
+
- **Pick by place, not by importance.** `md` everywhere by default, including dialogs, forms, onboarding, marketing pages and an app's sidebar. A sidebar with 28px rows puts `mu-compact` on itself, which makes md 28px inside it; see [A sidebar navigation row](#a-sidebar-navigation-row). `sm` for dense toolbars, table rows and filters inside panels. `xs` for secondary actions beside 13px text (entry footers, row actions). See [Control sizes](layout.md#control-sizes).
|
|
2549
2727
|
- **Never make a control bigger to make it stronger.** Emphasis is the `primary` variant.
|
|
2550
2728
|
- **Don't mix sizes in one row.** A Button beside an Input has the Input's size.
|
|
2551
2729
|
- **Dialogs and Popovers start at md.** Their panels are md wherever they are declared, so a Dialog inside a `Table.Root size="sm"` still has md Buttons and fields. Set `size` inside the panel only for a part that should differ.
|
|
@@ -2557,7 +2735,7 @@ In an extension or an embed, pass the same `container` to every `Popup` and to `
|
|
|
2557
2735
|
- **One `primary` Button per view.** A view is a page, a dialog or a popover. Everything else is `neutral`, or `ghost` in toolbars and rows.
|
|
2558
2736
|
- **`danger` confirms; it never starts.** The first click on "Delete" is a Menu item with `danger` or a neutral Button; the `danger` Button lives in the AlertDialog that follows.
|
|
2559
2737
|
- **Dialog footers** are a ghost Cancel, then the action, named with its verb ("Delete note", "Create"), never "OK" or "Yes".
|
|
2560
|
-
- **Tone is status only.** Badge and Toast tones say success, warning or danger. Don't use them to highlight, decorate or color-code categories.
|
|
2738
|
+
- **Tone is status only.** Badge and Toast tones say success, warning or danger. Don't use them to highlight, decorate or color-code categories. The one exception is Badge `tone="primary"`: ink, for the single label in a view that should stand out, such as New.
|
|
2561
2739
|
- **Links are the only blue.** Don't color text, icons or backgrounds with `accent`.
|
|
2562
2740
|
|
|
2563
2741
|
## Accessibility checklist
|
|
@@ -2599,10 +2777,20 @@ Run through it for every screen.
|
|
|
2599
2777
|
| A Checkbox that saves at once | A Switch |
|
|
2600
2778
|
| A Select with 30 options | A Combobox |
|
|
2601
2779
|
| A Select with two options and room to spare | A RadioGroup |
|
|
2602
|
-
| A boxed card around each item | Whitespace, a section title, or a Separator. Rows of settings or records go in one [List](components/list.md) |
|
|
2780
|
+
| A boxed card around each item | Whitespace, a section title, or a Separator. Rows of settings or records go in one [List](components/list.md); a small form among them in a `bordered` [Form](components/form.md) |
|
|
2603
2781
|
| An icon after a Button's label inside `children` | `iconEnd` |
|
|
2604
2782
|
| A `<button>` with its own classes as a row in a Popover | `menuClasses().item`, in a Popover with `variant="list"` |
|
|
2605
2783
|
| A Dialog with `className` that removes its padding and X | `variant="bare"` |
|
|
2784
|
+
| A local box (a "settings panel") around a small form on a settings page | `Form.Root bordered` with `Form.Footer` |
|
|
2785
|
+
| A Dialog with `fixed inset-0 rounded-none` | `variant="bare" fullScreen` |
|
|
2786
|
+
| A lighter or clear backdrop for one Dialog, such as a search palette | The standard backdrop: every Dialog dims the page the same way |
|
|
2787
|
+
| A blue or custom-colored New badge | `tone="primary"` |
|
|
2788
|
+
| A wrapper with negative margins around two Buttons in a table cell | `Table.Actions` |
|
|
2789
|
+
| A button with negative margins to make a List row clickable | `List.ItemButton` |
|
|
2790
|
+
| `w-full` on a ToggleGroup and `flex-1` on its Toggles | `fullWidth` |
|
|
2791
|
+
| `h-dvh`, a `border-e` or your own Close on a bare Drawer's content | Nothing: bare does these; give your content `height: 100%` |
|
|
2792
|
+
| A 28px row class, or `--mu-row-height` set by hand | `mu-compact` on the sidebar or popup |
|
|
2793
|
+
| Text over a photo on a translucent, blurred fill | An opaque `surface` pill in a `data-theme="dark"` scope |
|
|
2606
2794
|
| Tailwind `size-4` on a standalone icon | `mu-icon-sm` or `mu-icon-md`, which set the stroke too |
|
|
2607
2795
|
| A Spinner where a list will appear | Skeletons in the shape of the list |
|
|
2608
2796
|
| `size="md"` or `"lg"` on a Button to emphasize it | `variant="primary"` at the row's size |
|
|
@@ -2634,7 +2822,8 @@ Names and parts from other libraries and where they go.
|
|
|
2634
2822
|
| Tooltip with a `title` or `content` prop | [Tooltip](components/tooltip.md) parts: Root, Trigger, Popup |
|
|
2635
2823
|
| Snackbar, Toaster, sonner's `toast()` | [Toast](components/toast.md) and `Toast.useToastManager().add()` |
|
|
2636
2824
|
| Toast `variant="destructive"` | `type: "danger"` |
|
|
2637
|
-
| Card, Paper, Panel | No component: spacing, a section title and [Separator](components/separator.md). For a boxed group of rows, [List](components/list.md) |
|
|
2825
|
+
| Card, Paper, Panel | No component: spacing, a section title and [Separator](components/separator.md). For a boxed group of rows, [List](components/list.md); for a boxed small form, a `bordered` [Form](components/form.md) |
|
|
2826
|
+
| Form (Base UI, React Hook Form wrappers) | [Form](components/form.md), with [Field](components/field.md)s inside |
|
|
2638
2827
|
| Divider | [Separator](components/separator.md) |
|
|
2639
2828
|
| Chip, Tag | [Badge](components/badge.md) to show, [Toggle](components/toggle.md) to filter |
|
|
2640
2829
|
| SegmentedControl, RadioButtons in a row | [ToggleGroup](components/toggle-group.md) without `multiple` |
|
|
@@ -2664,10 +2853,10 @@ How the components go together. Each product builds its screens from these patte
|
|
|
2664
2853
|
## Page
|
|
2665
2854
|
|
|
2666
2855
|
- From `breakpoint-md` (1024px) up: a fixed sidebar of `sidebar-width` (240px). Below it, the sidebar is a [Drawer](components/drawer.md) from the start edge (`side="start"`, `variant="bare"`) opened from a ghost button.
|
|
2667
|
-
-
|
|
2856
|
+
- A sidebar may use 28px rows: put `mu-compact` on it and every md control and row inside is `control-compact` (28px) tall, the package's (a ghost Select switcher, the "New" Button) and the app's own navigation rows alike. See [A sidebar navigation row](usage.md#a-sidebar-navigation-row). Without it, sidebar rows are md (32px).
|
|
2668
2857
|
- Content sits in a column of at most `content-width` (720px), padded `space-4` on phones and `space-6` on desktop.
|
|
2669
2858
|
- A page header is a `heading` with an optional `body` line under it, and the page's one primary action at the right. `heading` appears once per page.
|
|
2670
|
-
- Sections inside a page have a `label` title (14px, 500) with `space-6` above it and `space-2` below. Section titles are never larger than the text around them; space and grouping set them apart. A section's rows
|
|
2859
|
+
- Sections inside a page have a `label` title (14px, 500) with `space-6` above it and `space-2` below. Section titles are never larger than the text around them; space and grouping set them apart. A section's rows are horizontal Fields: the name at weight 400 (`body`) and the description in `caption`, `ink-muted`. The section title above is the only `label` (500); the row names stay 400 so the title stands out from them.
|
|
2671
2860
|
|
|
2672
2861
|
## Form
|
|
2673
2862
|
|
|
@@ -2677,6 +2866,7 @@ How the components go together. Each product builds its screens from these patte
|
|
|
2677
2866
|
- A form that saves at once (settings) uses switches and has no Save button. A form with a Save button uses checkboxes.
|
|
2678
2867
|
- On a settings page, each section is a title and a [List](components/list.md) of horizontal Fields: the setting's name at the start, its Switch or Select at the end. A section of records (tokens, webhooks) is a List, or a `bordered` [Table](components/table.md) when the records have columns.
|
|
2679
2868
|
- Errors appear under the field, in words that say how to fix it.
|
|
2869
|
+
- A small form on a settings page, among Lists ("Add a tag rule", a reactions picker), is a `bordered` [Form](components/form.md): a title, its fields, and the buttons in a footer bar.
|
|
2680
2870
|
|
|
2681
2871
|
## Entry
|
|
2682
2872
|
|
|
@@ -2933,6 +3123,7 @@ Apply these classes to an app's own elements. Don't override their rules: the pr
|
|
|
2933
3123
|
| `mu-s-xs` | The xs size row: 20px | Secondary actions beside 13px text |
|
|
2934
3124
|
| `mu-s-sm` | The sm size row: 24px | Dense toolbars, table rows, filters |
|
|
2935
3125
|
| `mu-s-md` | The md size row: 32px | Everything else |
|
|
3126
|
+
| `mu-compact` | md controls and rows inside are `control-compact` (28px) tall; text, icons, padding and radius stay md | An app's sidebar with 28px rows, or a list Popover beside it |
|
|
2936
3127
|
| `mu-base` | The system font, the row's text size and weight 500, `ink` text, and icon sizing for Lucide icons inside | The root of a control |
|
|
2937
3128
|
| `mu-recessed` | `well`, a 1px `edge` and `shadow-track` | A part that holds a value |
|
|
2938
3129
|
| `mu-key` | A `key` with the sheen and `shadow-key` | A key inside a well |
|
|
@@ -2945,7 +3136,7 @@ Apply these classes to an app's own elements. Don't override their rules: the pr
|
|
|
2945
3136
|
| `mu-icon` | Icon size and stroke from the nearest size row, 16px outside one | A Lucide icon inside an app's own row |
|
|
2946
3137
|
| `mu-icon-xs` | A 12px icon with a 1.25px stroke | A Lucide icon beside 12px or 13px text, outside a control |
|
|
2947
3138
|
| `mu-icon-sm` | A 14px icon with a 1.25px stroke | A Lucide icon beside 13px or 14px text, outside a control |
|
|
2948
|
-
| `mu-icon-md` | A 16px icon with a 1.
|
|
3139
|
+
| `mu-icon-md` | A 16px icon with a 1.25px stroke | A Lucide icon beside 14px or 16px text, outside a control |
|
|
2949
3140
|
| `mu-visually-hidden` | Hidden on screen, read by screen readers | Text for screen readers only |
|
|
2950
3141
|
| `mu-label-xs` | 12 / 16px, 500 | Labels of 20px parts |
|
|
2951
3142
|
| `mu-caption` | 13 / 18px, 400 | Secondary text |
|
|
@@ -2967,13 +3158,15 @@ A size row class sets these variables on its element, and children inherit them.
|
|
|
2967
3158
|
| `--mu-row-padding-icon` | 4px | 6px | 10px |
|
|
2968
3159
|
| `--mu-row-font-size` | 12px | 13px | 14px |
|
|
2969
3160
|
| `--mu-row-line-height` | 16px | 18px | 20px |
|
|
3161
|
+
| `--mu-row-field-font-size` | 12px | 13px | 14px |
|
|
3162
|
+
| `--mu-row-field-line-height` | 16px | 18px | 20px |
|
|
2970
3163
|
| `--mu-row-radius` | 4px | 4px | 6px |
|
|
2971
3164
|
| `--mu-row-gap` | 4px | 4px | 6px |
|
|
2972
3165
|
| `--mu-row-icon` | 12px | 14px | 16px |
|
|
2973
|
-
| `--mu-row-icon-stroke` | 1.25px | 1.25px | 1.
|
|
3166
|
+
| `--mu-row-icon-stroke` | 1.25px | 1.25px | 1.25px |
|
|
2974
3167
|
| `--mu-row-check-box` | 14px | 16px | 18px |
|
|
2975
3168
|
|
|
2976
|
-
`--mu-row-padding-icon` is the padding on the side of a control that starts or ends with an icon.
|
|
3169
|
+
`--mu-row-padding-icon` is the padding on the side of a control that starts or ends with an icon. Inside `mu-compact`, the md `--mu-row-height` is 28px. `--mu-row-field-font-size` and `--mu-row-field-line-height` are for text people type; on touch screens they are 16px and 20px at every size, so iOS Safari does not zoom in when a field takes focus.
|
|
2977
3170
|
|
|
2978
3171
|
```css
|
|
2979
3172
|
.app-tag-input-chip {
|