@usememos/ui 26.11.2 → 26.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +233 -41
- package/llms.txt +2 -1
- package/package.json +1 -1
package/llms-full.txt
CHANGED
|
@@ -25,7 +25,7 @@ It is not a general-purpose UI library for outside developers. Components are ge
|
|
|
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
27
|
| [Icons](icons.md) | Lucide, sizes, fixed stroke widths and accessibility |
|
|
28
|
-
| [Components](components/README.md) | The
|
|
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 |
|
|
@@ -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 |
|
|
@@ -556,12 +559,12 @@ When Lucide doesn't have an icon, draw it on Lucide's 24px grid with 2px round s
|
|
|
556
559
|
|
|
557
560
|
# Components
|
|
558
561
|
|
|
559
|
-
|
|
562
|
+
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
563
|
|
|
561
564
|
| Group | Components |
|
|
562
565
|
| --- | --- |
|
|
563
566
|
| 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) |
|
|
567
|
+
| 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
568
|
| Navigation | [Tabs](tabs.md), [Collapsible](collapsible.md) |
|
|
566
569
|
| Overlays | [Dialog](dialog.md) (and its sheet form), [Drawer](drawer.md), [Popover](popover.md), [Tooltip](tooltip.md), [Toast](toast.md) |
|
|
567
570
|
| 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 +584,7 @@ Components follow the [API conventions](../building.md#api-conventions): parts w
|
|
|
581
584
|
|
|
582
585
|
## Left out on purpose
|
|
583
586
|
|
|
584
|
-
- **Card.** Content is grouped by spacing, headings and separators. The only boxes are a [List](list.md) of settings or records
|
|
587
|
+
- **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
588
|
- **Slider, Stepper.** No setting in our products needs a continuous range. Use an Input with `type="number"` or a Select.
|
|
586
589
|
- **Chip, SegmentedControl, Accordion.** A Toggle covers filter chips, a single-choice ToggleGroup covers segmented choices, and stacked Collapsibles cover an accordion.
|
|
587
590
|
- **Later, when a screen needs them:** multi-select Combobox, ContextMenu, ScrollArea, Progress, Calendar.
|
|
@@ -610,6 +613,7 @@ The short version. [Using the components](../usage.md) has the full decision gui
|
|
|
610
613
|
| Confirm something happened | Toast |
|
|
611
614
|
| Settings rows, or a short list of records with actions | List |
|
|
612
615
|
| Data with columns | Table |
|
|
616
|
+
| A small form (heading, a few fields, buttons) among Lists on a settings page | Form with `bordered` |
|
|
613
617
|
| A status or tag label | Badge |
|
|
614
618
|
| A clickable tag filter | Toggle |
|
|
615
619
|
| Content still loading | Skeleton |
|
|
@@ -757,7 +761,8 @@ import { Bold, Code, Italic } from "lucide-react";
|
|
|
757
761
|
|
|
758
762
|
```ts
|
|
759
763
|
interface ToggleGroupProps extends BaseToggleGroup.Props {
|
|
760
|
-
size?: Size;
|
|
764
|
+
size?: Size; // passed to every Toggle inside
|
|
765
|
+
fullWidth?: boolean; // fills the container; labeled Toggles share the width equally
|
|
761
766
|
}
|
|
762
767
|
```
|
|
763
768
|
|
|
@@ -770,10 +775,11 @@ Base UI's props pass through: `value`, `defaultValue`, `onValueChange`, `multipl
|
|
|
770
775
|
- Without `multiple`: filters and view switches that don't change the whole page. For sections of a page, use Tabs.
|
|
771
776
|
- 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
777
|
- Arrow keys move focus between items; Space or Enter presses one.
|
|
778
|
+
- 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
779
|
|
|
774
780
|
## Style
|
|
775
781
|
|
|
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.
|
|
782
|
+
- 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
783
|
- `multiple`: items 2px apart.
|
|
778
784
|
- 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
785
|
|
|
@@ -907,6 +913,72 @@ Provide `href` and `children`.
|
|
|
907
913
|
|
|
908
914
|
- `accent` text that underlines on hover: the one place the hue appears at rest. `muted` uses `ink-muted`.
|
|
909
915
|
|
|
916
|
+
<!-- docs/components/form.md -->
|
|
917
|
+
|
|
918
|
+
# Form
|
|
919
|
+
|
|
920
|
+
A `<form>` with a title, its fields and its buttons. `bordered` boxes a small form among the [Lists](list.md) of a settings page.
|
|
921
|
+
|
|
922
|
+
## Example
|
|
923
|
+
|
|
924
|
+
```tsx
|
|
925
|
+
import { Button, Field, Form, Input, Switch } from "@usememos/ui";
|
|
926
|
+
|
|
927
|
+
<Form.Root bordered onSubmit={addRule}>
|
|
928
|
+
<Form.Title>Add a tag rule</Form.Title>
|
|
929
|
+
<Form.Description>Tag new notes that match a word.</Form.Description>
|
|
930
|
+
<Field.Root name="tag">
|
|
931
|
+
<Field.Label>Tag</Field.Label>
|
|
932
|
+
<Input required />
|
|
933
|
+
</Field.Root>
|
|
934
|
+
<Field.Root orientation="horizontal">
|
|
935
|
+
<Field.Label>Apply to existing notes</Field.Label>
|
|
936
|
+
<Switch name="existing" />
|
|
937
|
+
</Field.Root>
|
|
938
|
+
<Form.Footer>
|
|
939
|
+
<Button variant="ghost" onClick={cancel}>Cancel</Button>
|
|
940
|
+
<Button variant="primary" type="submit">Add rule</Button>
|
|
941
|
+
</Form.Footer>
|
|
942
|
+
</Form.Root>
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
## Parts
|
|
946
|
+
|
|
947
|
+
| Part | Base UI part | Renders |
|
|
948
|
+
| --- | --- | --- |
|
|
949
|
+
| `Form.Root` | `Form` | A `<form>`. Takes `bordered`. Named by its Title |
|
|
950
|
+
| `Form.Title` | none (ours) | An `<h2>` in `label` style. Takes `render` for another heading level |
|
|
951
|
+
| `Form.Description` | none (ours) | A `<p>` under the title |
|
|
952
|
+
| `Form.Footer` | none (ours) | A `<div>` that right-aligns the buttons |
|
|
953
|
+
|
|
954
|
+
## Props
|
|
955
|
+
|
|
956
|
+
```ts
|
|
957
|
+
interface Form.RootProps extends BaseForm.Props {
|
|
958
|
+
bordered?: boolean; // a List's edge around the form, and the Footer as a canvas bar
|
|
959
|
+
}
|
|
960
|
+
interface Form.TitleProps extends React.ComponentProps<"h2"> {
|
|
961
|
+
render?: React.ReactElement; // such as <h3 /> under a page section
|
|
962
|
+
}
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
Base UI's props pass through: `onFormSubmit`, `errors`, `validationMode` and `actionsRef`, as do the native `<form>` attributes (`onSubmit`, `action`).
|
|
966
|
+
|
|
967
|
+
## Rules
|
|
968
|
+
|
|
969
|
+
- 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.
|
|
970
|
+
- 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).
|
|
971
|
+
- Don't box single settings: a setting that saves at once is a horizontal [Field](field.md) in a List.
|
|
972
|
+
- Footer: a ghost Cancel, then one `primary` Button with `type="submit"` named with its verb ("Add rule", never "OK" or "Submit").
|
|
973
|
+
- Give each Field a `name`; errors from the server go in `errors`, keyed by name, and appear under their fields.
|
|
974
|
+
- Name it with a Title, or `aria-label` on the root when there is no visible heading.
|
|
975
|
+
|
|
976
|
+
## Style
|
|
977
|
+
|
|
978
|
+
- 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.
|
|
979
|
+
- `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.
|
|
980
|
+
- The form is always md: controls inside it ignore the size of a parent Root.
|
|
981
|
+
|
|
910
982
|
<!-- docs/components/field.md -->
|
|
911
983
|
|
|
912
984
|
# Field and Fieldset
|
|
@@ -983,7 +1055,7 @@ Base UI's props pass through: `name`, `invalid`, `disabled`, `validate`, `valida
|
|
|
983
1055
|
## Style
|
|
984
1056
|
|
|
985
1057
|
- 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.
|
|
1058
|
+
- 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
1059
|
- Description and error: `caption` (13px), below the control. The error hides the description and turns the control's edge `danger`.
|
|
988
1060
|
|
|
989
1061
|
<!-- docs/components/input.md -->
|
|
@@ -1027,7 +1099,7 @@ Wraps Base UI's [Input](https://base-ui.com/react/components/input), so inside a
|
|
|
1027
1099
|
## Style
|
|
1028
1100
|
|
|
1029
1101
|
- 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`.
|
|
1102
|
+
- 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
1103
|
- 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
1104
|
- The side with a prefix or suffix has 2px less padding.
|
|
1033
1105
|
|
|
@@ -1070,6 +1142,7 @@ Base UI has no textarea part, so this renders Base UI's `Field.Control` as a `<t
|
|
|
1070
1142
|
## Style
|
|
1071
1143
|
|
|
1072
1144
|
- Looks like [Input](input.md). Resizes vertically only.
|
|
1145
|
+
- 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
1146
|
|
|
1074
1147
|
<!-- docs/components/select.md -->
|
|
1075
1148
|
|
|
@@ -1139,7 +1212,7 @@ Base UI's props pass through: `items`, `value`, `defaultValue`, `onValueChange`,
|
|
|
1139
1212
|
- Label it with a [Field](field.md), or `aria-label` on the trigger.
|
|
1140
1213
|
- 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
1214
|
- 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`.
|
|
1215
|
+
- `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
1216
|
|
|
1144
1217
|
## Style
|
|
1145
1218
|
|
|
@@ -1552,10 +1625,11 @@ Dialog and AlertDialog have the same parts.
|
|
|
1552
1625
|
interface Dialog.PopupProps extends BaseDialog.Popup.Props {
|
|
1553
1626
|
sheet?: boolean; // below breakpoint-sm, a sheet from the bottom; default true
|
|
1554
1627
|
variant?: "panel" | "bare"; // default "panel"; bare: no padding, gap or X (Dialog only)
|
|
1628
|
+
fullScreen?: boolean; // covers the whole screen with square corners, at every width (Dialog only)
|
|
1555
1629
|
closeLabel?: string; // accessible name of the X; default "Close"
|
|
1556
1630
|
container?: HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null; // portal target
|
|
1557
1631
|
}
|
|
1558
|
-
// AlertDialog.Popup takes sheet and container; it has no X and no
|
|
1632
|
+
// AlertDialog.Popup takes sheet and container; it has no X, no variant and no fullScreen.
|
|
1559
1633
|
```
|
|
1560
1634
|
|
|
1561
1635
|
Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `modal`, `disablePointerDismissal` on the root; `initialFocus`, `finalFocus` on the popup.
|
|
@@ -1568,6 +1642,8 @@ Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `modal`, `d
|
|
|
1568
1642
|
- Escape and a backdrop click close a Dialog. While a form has unsaved input, keep it open from `onOpenChange`.
|
|
1569
1643
|
- Focus moves into the dialog and returns to the trigger on close.
|
|
1570
1644
|
- `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.
|
|
1645
|
+
- Every Dialog has the same backdrop, `overlay`, including a search palette. There is no prop to lighten or remove it.
|
|
1646
|
+
- `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
1647
|
- 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
1648
|
|
|
1573
1649
|
## Style
|
|
@@ -1577,6 +1653,7 @@ Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `modal`, `d
|
|
|
1577
1653
|
- 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
1654
|
- Long content scrolls the viewport around the panel.
|
|
1579
1655
|
- `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`.
|
|
1656
|
+
- `fullScreen`: the viewport's padding goes, and the popup fills it with square corners.
|
|
1580
1657
|
|
|
1581
1658
|
<!-- docs/components/drawer.md -->
|
|
1582
1659
|
|
|
@@ -1618,7 +1695,7 @@ interface Drawer.RootProps extends BaseDrawer.Root.Props {
|
|
|
1618
1695
|
}
|
|
1619
1696
|
interface Drawer.PopupProps extends BaseDrawer.Popup.Props {
|
|
1620
1697
|
variant?: "panel" | "bare"; // default "panel"; bare: no padding, gap or X
|
|
1621
|
-
closeLabel?: string; // accessible name of the X; default "Close"
|
|
1698
|
+
closeLabel?: string; // accessible name of the X, or of bare's screen-reader Close; default "Close"
|
|
1622
1699
|
container?: HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null; // portal target
|
|
1623
1700
|
}
|
|
1624
1701
|
```
|
|
@@ -1629,15 +1706,16 @@ Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `onOpenChan
|
|
|
1629
1706
|
|
|
1630
1707
|
- 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
1708
|
- 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`.
|
|
1709
|
+
- `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
1710
|
- Close it when people pick a destination: wrap each link in `Drawer.Close render={<a … />}`, or close it from the router.
|
|
1634
1711
|
- Escape, a backdrop click and a swipe toward its edge close it. Focus moves in and returns to the trigger.
|
|
1635
1712
|
- The panel is always md, as a Dialog is.
|
|
1636
1713
|
|
|
1637
1714
|
## Style
|
|
1638
1715
|
|
|
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`.
|
|
1716
|
+
- `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
1717
|
- `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`.
|
|
1718
|
+
- `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
1719
|
- The backdrop is `overlay`; it fades as the drawer is swiped away.
|
|
1642
1720
|
- Slides in from its edge over `duration-settle` with `ease-settle` and follows the finger while swiped. Under reduced motion it fades instead.
|
|
1643
1721
|
|
|
@@ -1691,7 +1769,7 @@ Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `modal` on
|
|
|
1691
1769
|
|
|
1692
1770
|
- 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
1771
|
- 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.
|
|
1772
|
+
- `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
1773
|
- 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
1774
|
- It closes on Escape and on a click outside; focus returns to the trigger.
|
|
1697
1775
|
- An app's own floating panel, such as an emoji picker, takes `popoverClasses()`. See [Class functions](../custom-components.md#class-functions).
|
|
@@ -1879,31 +1957,32 @@ import { Hash, Lock } from "lucide-react";
|
|
|
1879
1957
|
<Badge icon={<Hash />}>reading</Badge>
|
|
1880
1958
|
<Badge tone="outline" icon={<Lock />}>Private</Badge>
|
|
1881
1959
|
<Badge tone="success">Synced</Badge>
|
|
1960
|
+
<Badge tone="primary">New</Badge>
|
|
1882
1961
|
```
|
|
1883
1962
|
|
|
1884
1963
|
## Props
|
|
1885
1964
|
|
|
1886
1965
|
```ts
|
|
1887
1966
|
interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
1888
|
-
tone?: Tone | "outline"; // default "neutral"
|
|
1967
|
+
tone?: Tone | "outline" | "primary"; // default "neutral"
|
|
1889
1968
|
icon?: IconElement;
|
|
1890
1969
|
children: React.ReactNode; // one or two words
|
|
1891
1970
|
render?: React.ReactElement;
|
|
1892
1971
|
}
|
|
1893
1972
|
|
|
1894
|
-
function badgeClasses(options?: { tone?: Tone | "outline" }): string;
|
|
1973
|
+
function badgeClasses(options?: { tone?: Tone | "outline" | "primary" }): string;
|
|
1895
1974
|
```
|
|
1896
1975
|
|
|
1897
1976
|
## Rules
|
|
1898
1977
|
|
|
1899
|
-
- Tags: `neutral` with a `Hash` icon. Visibility: `outline` with its icon. Status: `success`, `warning` or `danger`, always with a word, never color alone.
|
|
1978
|
+
- 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
1979
|
- A badge is not a button. A clickable tag filter is a [Toggle](toggle.md).
|
|
1901
1980
|
- 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
1981
|
|
|
1903
1982
|
## Style
|
|
1904
1983
|
|
|
1905
1984
|
- `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.
|
|
1985
|
+
- `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
1986
|
|
|
1908
1987
|
<!-- docs/components/kbd.md -->
|
|
1909
1988
|
|
|
@@ -2100,6 +2179,7 @@ No Base UI part: these are styled native table elements.
|
|
|
2100
2179
|
| `Table.Row` | `<tr>` |
|
|
2101
2180
|
| `Table.Head` | `<th scope="col">`. Takes `num` |
|
|
2102
2181
|
| `Table.Cell` | `<td>`. Takes `num` and `muted` |
|
|
2182
|
+
| `Table.Actions` | A `<div>` that lays two or more row actions side by side in one Cell |
|
|
2103
2183
|
|
|
2104
2184
|
## Props
|
|
2105
2185
|
|
|
@@ -2124,6 +2204,16 @@ Every native attribute passes through.
|
|
|
2124
2204
|
- Use it for data with columns. Lists of content (notes, comments) are [entries](../patterns.md#entry), not rows.
|
|
2125
2205
|
- Set `num` on a numeric column's Head and on its Cells.
|
|
2126
2206
|
- 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`.
|
|
2207
|
+
- 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:
|
|
2208
|
+
|
|
2209
|
+
```tsx
|
|
2210
|
+
<Table.Cell num>
|
|
2211
|
+
<Table.Actions>
|
|
2212
|
+
<Button variant="ghost" size="sm" icon={<Pencil />} aria-label={`Edit ${hook.name}`} />
|
|
2213
|
+
<Button variant="ghost" size="sm" icon={<Trash2 />} aria-label={`Delete ${hook.name}`} />
|
|
2214
|
+
</Table.Actions>
|
|
2215
|
+
</Table.Cell>
|
|
2216
|
+
```
|
|
2127
2217
|
- Name the table with a Caption, or `aria-label` on the root.
|
|
2128
2218
|
- Sort, select and paginate in the app. The table has no data props.
|
|
2129
2219
|
- 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 +2221,9 @@ Every native attribute passes through.
|
|
|
2131
2221
|
## Style
|
|
2132
2222
|
|
|
2133
2223
|
- 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.
|
|
2224
|
+
- `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
2225
|
- 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.
|
|
2226
|
+
- 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
2227
|
- Cells pad `px` at the sides. Caption: `caption` in `ink-muted`, `space-2` above.
|
|
2138
2228
|
|
|
2139
2229
|
<!-- docs/components/list.md -->
|
|
@@ -2173,6 +2263,7 @@ No Base UI part: these are styled native list elements.
|
|
|
2173
2263
|
| --- | --- |
|
|
2174
2264
|
| `List.Root` | A `<ul>` with a `line` edge and rounded corners. Takes `size` |
|
|
2175
2265
|
| `List.Item` | An `<li>`: one row, divided from the next by a `line` |
|
|
2266
|
+
| `List.ItemButton` | A `<button>` that fills its Item, for a row that acts as a whole. Takes `render` for a link |
|
|
2176
2267
|
|
|
2177
2268
|
## Props
|
|
2178
2269
|
|
|
@@ -2190,13 +2281,27 @@ Every native attribute passes through.
|
|
|
2190
2281
|
- 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
2282
|
- Name the list: `aria-labelledby` pointing at the section title above it, or `aria-label`.
|
|
2192
2283
|
- 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
|
-
-
|
|
2284
|
+
- 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.
|
|
2285
|
+
- 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.
|
|
2286
|
+
|
|
2287
|
+
```tsx
|
|
2288
|
+
<List.Root aria-label="Spaces">
|
|
2289
|
+
{spaces.map((space) => (
|
|
2290
|
+
<List.Item key={space.id}>
|
|
2291
|
+
<List.ItemButton render={<a href={`/s/${space.id}`} />}>
|
|
2292
|
+
<Avatar.Root size="sm">…</Avatar.Root>
|
|
2293
|
+
{space.name}
|
|
2294
|
+
</List.ItemButton>
|
|
2295
|
+
</List.Item>
|
|
2296
|
+
))}
|
|
2297
|
+
</List.Root>
|
|
2298
|
+
```
|
|
2194
2299
|
- An empty list shows the [empty state](../patterns.md#empty-state) in place of the List.
|
|
2195
2300
|
|
|
2196
2301
|
## Style
|
|
2197
2302
|
|
|
2198
2303
|
- `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
|
|
2304
|
+
- 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
2305
|
- Text is `body` in `ink`.
|
|
2201
2306
|
|
|
2202
2307
|
<!-- docs/components/prose.md -->
|
|
@@ -2240,8 +2345,8 @@ Every native attribute passes through.
|
|
|
2240
2345
|
|
|
2241
2346
|
## Style
|
|
2242
2347
|
|
|
2243
|
-
- Text: 16px on a 24px line (`sm`: 14 on 22), weight 400, `ink`. Blocks are `space-3` apart.
|
|
2244
|
-
- Headings: `h1` 20/28px, `
|
|
2348
|
+
- 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.
|
|
2349
|
+
- 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
2350
|
- Links: `accent`, underlined at rest, because color alone does not set them apart from the text.
|
|
2246
2351
|
- 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
2352
|
- Quotes: a 2px `edge` bar, text in `ink-muted`. Rules: a 1px `line` with `space-5` around it.
|
|
@@ -2249,7 +2354,7 @@ Every native attribute passes through.
|
|
|
2249
2354
|
- Images and video: never wider than the column, `radius-lg`.
|
|
2250
2355
|
- Tables follow [Table](table.md): `line` between rows, 13px `ink-muted` heads (weight 500), tabular numbers; they scroll sideways when too wide.
|
|
2251
2356
|
- 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`,
|
|
2357
|
+
- 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
2358
|
|
|
2254
2359
|
<!-- docs/usage.md -->
|
|
2255
2360
|
|
|
@@ -2486,7 +2591,7 @@ const [confirming, setConfirming] = useState(false);
|
|
|
2486
2591
|
</List.Root>
|
|
2487
2592
|
```
|
|
2488
2593
|
|
|
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.
|
|
2594
|
+
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
2595
|
|
|
2491
2596
|
No Save button and no toast: the Switch shows the result. If saving fails, move the Switch back and show a danger Toast.
|
|
2492
2597
|
|
|
@@ -2514,6 +2619,49 @@ One `Tooltip.Provider` around the toolbar (or the app) so moving along it opens
|
|
|
2514
2619
|
- Show one or two frequent actions as xs or sm ghost Buttons beside it, each with a Tooltip.
|
|
2515
2620
|
- On an entry the trigger appears on hover; on touch screens it is always visible (see [Entry](patterns.md#entry)).
|
|
2516
2621
|
|
|
2622
|
+
### A sidebar navigation row
|
|
2623
|
+
|
|
2624
|
+
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:
|
|
2625
|
+
|
|
2626
|
+
```tsx
|
|
2627
|
+
<aside className="app-sidebar mu-compact">
|
|
2628
|
+
<Select.Root value={space} onValueChange={setSpace}>
|
|
2629
|
+
<Select.Trigger variant="ghost">…</Select.Trigger>
|
|
2630
|
+
…
|
|
2631
|
+
</Select.Root>
|
|
2632
|
+
<Button variant="primary" icon={<Plus />}>New note</Button>
|
|
2633
|
+
<nav className="app-nav mu-s-md mu-base" aria-label="Main">
|
|
2634
|
+
<a className="app-nav-row mu-focus" href="/" aria-current={path === "/" ? "page" : undefined}>
|
|
2635
|
+
<House className="mu-icon" aria-hidden />
|
|
2636
|
+
Home
|
|
2637
|
+
</a>
|
|
2638
|
+
…
|
|
2639
|
+
</nav>
|
|
2640
|
+
</aside>
|
|
2641
|
+
```
|
|
2642
|
+
|
|
2643
|
+
```css
|
|
2644
|
+
.app-nav { display: grid; gap: var(--mu-inset); }
|
|
2645
|
+
.app-nav-row {
|
|
2646
|
+
display: flex;
|
|
2647
|
+
align-items: center;
|
|
2648
|
+
gap: var(--mu-row-gap);
|
|
2649
|
+
height: var(--mu-row-height); /* 28px inside mu-compact, 32px without */
|
|
2650
|
+
padding: 0 var(--mu-row-padding-icon);
|
|
2651
|
+
border-radius: var(--mu-row-radius);
|
|
2652
|
+
color: var(--mu-ink-muted);
|
|
2653
|
+
text-decoration: none;
|
|
2654
|
+
}
|
|
2655
|
+
.app-nav-row:hover, .app-nav-row[aria-current="page"] { color: var(--mu-ink); background-color: var(--mu-hover); }
|
|
2656
|
+
```
|
|
2657
|
+
|
|
2658
|
+
- `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.
|
|
2659
|
+
- 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">`.
|
|
2660
|
+
- `mu-s-md` gives the rows 14px text, 16px icons with a 1.5px 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.
|
|
2661
|
+
- Mark the current page with `aria-current="page"`, not with color alone; the fill shows it too.
|
|
2662
|
+
- 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.
|
|
2663
|
+
- A row with a count or a menu at its end adds it after the label with `margin-inline-start: auto`.
|
|
2664
|
+
|
|
2517
2665
|
### Buttons over a photo
|
|
2518
2666
|
|
|
2519
2667
|
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 +2678,37 @@ Controls over an image or a video sit in a dark-theme scope, so they read on any
|
|
|
2530
2678
|
```
|
|
2531
2679
|
|
|
2532
2680
|
- `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.
|
|
2681
|
+
- When the scope holds only Buttons, leave its own background transparent: the keys carry their own fill and shadow.
|
|
2682
|
+
- A full-screen viewer is a Dialog with `variant="bare" fullScreen`; put the scope inside its Popup.
|
|
2683
|
+
|
|
2684
|
+
### Text over a photo
|
|
2685
|
+
|
|
2686
|
+
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:
|
|
2687
|
+
|
|
2688
|
+
```tsx
|
|
2689
|
+
<div className="app-viewer-controls" data-theme="dark">
|
|
2690
|
+
<span className="app-viewer-label mu-label-sm" aria-live="polite">{index + 1} / {count}</span>
|
|
2691
|
+
<Button icon={<ZoomIn />} aria-label="Zoom in" onClick={zoomIn} />
|
|
2692
|
+
</div>
|
|
2693
|
+
```
|
|
2694
|
+
|
|
2695
|
+
```css
|
|
2696
|
+
.app-viewer-label {
|
|
2697
|
+
display: inline-flex;
|
|
2698
|
+
align-items: center;
|
|
2699
|
+
height: var(--mu-control-md);
|
|
2700
|
+
padding: 0 var(--mu-space-3);
|
|
2701
|
+
border-radius: var(--mu-radius-md);
|
|
2702
|
+
color: var(--mu-ink);
|
|
2703
|
+
background-color: var(--mu-surface);
|
|
2704
|
+
box-shadow: var(--mu-shadow-pop);
|
|
2705
|
+
font-variant-numeric: tabular-nums;
|
|
2706
|
+
}
|
|
2707
|
+
```
|
|
2708
|
+
|
|
2709
|
+
- 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.
|
|
2710
|
+
- 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.
|
|
2711
|
+
- 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
2712
|
|
|
2536
2713
|
### Inside a shadow root or a host page
|
|
2537
2714
|
|
|
@@ -2545,7 +2722,7 @@ In an extension or an embed, pass the same `container` to every `Popup` and to `
|
|
|
2545
2722
|
## Size
|
|
2546
2723
|
|
|
2547
2724
|
- **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
|
|
2725
|
+
- **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
2726
|
- **Never make a control bigger to make it stronger.** Emphasis is the `primary` variant.
|
|
2550
2727
|
- **Don't mix sizes in one row.** A Button beside an Input has the Input's size.
|
|
2551
2728
|
- **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 +2734,7 @@ In an extension or an embed, pass the same `container` to every `Popup` and to `
|
|
|
2557
2734
|
- **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
2735
|
- **`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
2736
|
- **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.
|
|
2737
|
+
- **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
2738
|
- **Links are the only blue.** Don't color text, icons or backgrounds with `accent`.
|
|
2562
2739
|
|
|
2563
2740
|
## Accessibility checklist
|
|
@@ -2599,10 +2776,20 @@ Run through it for every screen.
|
|
|
2599
2776
|
| A Checkbox that saves at once | A Switch |
|
|
2600
2777
|
| A Select with 30 options | A Combobox |
|
|
2601
2778
|
| 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) |
|
|
2779
|
+
| 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
2780
|
| An icon after a Button's label inside `children` | `iconEnd` |
|
|
2604
2781
|
| A `<button>` with its own classes as a row in a Popover | `menuClasses().item`, in a Popover with `variant="list"` |
|
|
2605
2782
|
| A Dialog with `className` that removes its padding and X | `variant="bare"` |
|
|
2783
|
+
| A local box (a "settings panel") around a small form on a settings page | `Form.Root bordered` with `Form.Footer` |
|
|
2784
|
+
| A Dialog with `fixed inset-0 rounded-none` | `variant="bare" fullScreen` |
|
|
2785
|
+
| A lighter or clear backdrop for one Dialog, such as a search palette | The standard backdrop: every Dialog dims the page the same way |
|
|
2786
|
+
| A blue or custom-colored New badge | `tone="primary"` |
|
|
2787
|
+
| A wrapper with negative margins around two Buttons in a table cell | `Table.Actions` |
|
|
2788
|
+
| A button with negative margins to make a List row clickable | `List.ItemButton` |
|
|
2789
|
+
| `w-full` on a ToggleGroup and `flex-1` on its Toggles | `fullWidth` |
|
|
2790
|
+
| `h-dvh`, a `border-e` or your own Close on a bare Drawer's content | Nothing: bare does these; give your content `height: 100%` |
|
|
2791
|
+
| A 28px row class, or `--mu-row-height` set by hand | `mu-compact` on the sidebar or popup |
|
|
2792
|
+
| Text over a photo on a translucent, blurred fill | An opaque `surface` pill in a `data-theme="dark"` scope |
|
|
2606
2793
|
| Tailwind `size-4` on a standalone icon | `mu-icon-sm` or `mu-icon-md`, which set the stroke too |
|
|
2607
2794
|
| A Spinner where a list will appear | Skeletons in the shape of the list |
|
|
2608
2795
|
| `size="md"` or `"lg"` on a Button to emphasize it | `variant="primary"` at the row's size |
|
|
@@ -2634,7 +2821,8 @@ Names and parts from other libraries and where they go.
|
|
|
2634
2821
|
| Tooltip with a `title` or `content` prop | [Tooltip](components/tooltip.md) parts: Root, Trigger, Popup |
|
|
2635
2822
|
| Snackbar, Toaster, sonner's `toast()` | [Toast](components/toast.md) and `Toast.useToastManager().add()` |
|
|
2636
2823
|
| 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) |
|
|
2824
|
+
| 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) |
|
|
2825
|
+
| Form (Base UI, React Hook Form wrappers) | [Form](components/form.md), with [Field](components/field.md)s inside |
|
|
2638
2826
|
| Divider | [Separator](components/separator.md) |
|
|
2639
2827
|
| Chip, Tag | [Badge](components/badge.md) to show, [Toggle](components/toggle.md) to filter |
|
|
2640
2828
|
| SegmentedControl, RadioButtons in a row | [ToggleGroup](components/toggle-group.md) without `multiple` |
|
|
@@ -2664,10 +2852,10 @@ How the components go together. Each product builds its screens from these patte
|
|
|
2664
2852
|
## Page
|
|
2665
2853
|
|
|
2666
2854
|
- 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
|
-
-
|
|
2855
|
+
- 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
2856
|
- Content sits in a column of at most `content-width` (720px), padded `space-4` on phones and `space-6` on desktop.
|
|
2669
2857
|
- 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
|
|
2858
|
+
- 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
2859
|
|
|
2672
2860
|
## Form
|
|
2673
2861
|
|
|
@@ -2677,6 +2865,7 @@ How the components go together. Each product builds its screens from these patte
|
|
|
2677
2865
|
- A form that saves at once (settings) uses switches and has no Save button. A form with a Save button uses checkboxes.
|
|
2678
2866
|
- 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
2867
|
- Errors appear under the field, in words that say how to fix it.
|
|
2868
|
+
- 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
2869
|
|
|
2681
2870
|
## Entry
|
|
2682
2871
|
|
|
@@ -2933,6 +3122,7 @@ Apply these classes to an app's own elements. Don't override their rules: the pr
|
|
|
2933
3122
|
| `mu-s-xs` | The xs size row: 20px | Secondary actions beside 13px text |
|
|
2934
3123
|
| `mu-s-sm` | The sm size row: 24px | Dense toolbars, table rows, filters |
|
|
2935
3124
|
| `mu-s-md` | The md size row: 32px | Everything else |
|
|
3125
|
+
| `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
3126
|
| `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
3127
|
| `mu-recessed` | `well`, a 1px `edge` and `shadow-track` | A part that holds a value |
|
|
2938
3128
|
| `mu-key` | A `key` with the sheen and `shadow-key` | A key inside a well |
|
|
@@ -2967,13 +3157,15 @@ A size row class sets these variables on its element, and children inherit them.
|
|
|
2967
3157
|
| `--mu-row-padding-icon` | 4px | 6px | 10px |
|
|
2968
3158
|
| `--mu-row-font-size` | 12px | 13px | 14px |
|
|
2969
3159
|
| `--mu-row-line-height` | 16px | 18px | 20px |
|
|
3160
|
+
| `--mu-row-field-font-size` | 12px | 13px | 14px |
|
|
3161
|
+
| `--mu-row-field-line-height` | 16px | 18px | 20px |
|
|
2970
3162
|
| `--mu-row-radius` | 4px | 4px | 6px |
|
|
2971
3163
|
| `--mu-row-gap` | 4px | 4px | 6px |
|
|
2972
3164
|
| `--mu-row-icon` | 12px | 14px | 16px |
|
|
2973
3165
|
| `--mu-row-icon-stroke` | 1.25px | 1.25px | 1.5px |
|
|
2974
3166
|
| `--mu-row-check-box` | 14px | 16px | 18px |
|
|
2975
3167
|
|
|
2976
|
-
`--mu-row-padding-icon` is the padding on the side of a control that starts or ends with an icon.
|
|
3168
|
+
`--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
3169
|
|
|
2978
3170
|
```css
|
|
2979
3171
|
.app-tag-input-chip {
|