react-material-expressive 1.0.0

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/LICENSE +21 -0
  3. package/README.md +286 -0
  4. package/dist/index.cjs +7014 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.d.cts +2068 -0
  7. package/dist/index.d.ts +2068 -0
  8. package/dist/index.js +6941 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/styles.css +2 -0
  11. package/dist/theme.css +187 -0
  12. package/docs/components/Amount.md +48 -0
  13. package/docs/components/Avatar.md +69 -0
  14. package/docs/components/AvatarStack.md +50 -0
  15. package/docs/components/Badge.md +50 -0
  16. package/docs/components/Blob.md +44 -0
  17. package/docs/components/Button.md +79 -0
  18. package/docs/components/ButtonGroup.md +46 -0
  19. package/docs/components/ButtonGroupConnected.md +62 -0
  20. package/docs/components/Card.md +52 -0
  21. package/docs/components/Checkbox.md +45 -0
  22. package/docs/components/Chips.md +77 -0
  23. package/docs/components/DatePicker.md +112 -0
  24. package/docs/components/Dialog.md +83 -0
  25. package/docs/components/Divider.md +48 -0
  26. package/docs/components/Dropdown.md +79 -0
  27. package/docs/components/FAB.md +63 -0
  28. package/docs/components/FABMenu.md +76 -0
  29. package/docs/components/Gallery.md +35 -0
  30. package/docs/components/Icon.md +36 -0
  31. package/docs/components/IconButton.md +69 -0
  32. package/docs/components/Img.md +52 -0
  33. package/docs/components/Layers.md +43 -0
  34. package/docs/components/Link.md +43 -0
  35. package/docs/components/List.md +87 -0
  36. package/docs/components/Loading.md +67 -0
  37. package/docs/components/LoadingIndicator.md +64 -0
  38. package/docs/components/MaterialSymbol.md +48 -0
  39. package/docs/components/MediaFrame.md +46 -0
  40. package/docs/components/Menu.md +149 -0
  41. package/docs/components/NavigationBar.md +78 -0
  42. package/docs/components/NavigationRail.md +105 -0
  43. package/docs/components/OverflowMenu.md +65 -0
  44. package/docs/components/Perspective.md +45 -0
  45. package/docs/components/Progress.md +83 -0
  46. package/docs/components/Radio.md +39 -0
  47. package/docs/components/Search.md +100 -0
  48. package/docs/components/Select.md +76 -0
  49. package/docs/components/Sheets.md +62 -0
  50. package/docs/components/Slider.md +89 -0
  51. package/docs/components/Snackbar.md +73 -0
  52. package/docs/components/SplitButton.md +75 -0
  53. package/docs/components/Stories.md +71 -0
  54. package/docs/components/Switch.md +40 -0
  55. package/docs/components/Table.md +67 -0
  56. package/docs/components/Tabs.md +67 -0
  57. package/docs/components/TextElement.md +37 -0
  58. package/docs/components/TextField.md +70 -0
  59. package/docs/components/TimePicker.md +83 -0
  60. package/docs/components/ToggleTheme.md +71 -0
  61. package/docs/components/Toolbar.md +102 -0
  62. package/docs/components/Tooltip.md +63 -0
  63. package/docs/components/TopAppBar.md +84 -0
  64. package/docs/components/Video.md +35 -0
  65. package/llms.txt +90 -0
  66. package/package.json +101 -0
@@ -0,0 +1,48 @@
1
+ # Divider
2
+
3
+ M3 divider: a 1px rule in `outline-variant` (decorative — per M3 never use
4
+ `outline` for dividers).
5
+
6
+ ## Import
7
+
8
+ ```tsx
9
+ import {Divider} from "react-material-expressive";
10
+ ```
11
+
12
+ ## API
13
+
14
+ ```ts
15
+ interface DividerProps extends ComponentProps<"hr"> {
16
+ // M3 inset dividers (16dp). Omit for a full-width rule.
17
+ inset?: "start" | "middle" | "end";
18
+ }
19
+ ```
20
+
21
+ `inset` mirrors the M3 spec's named insets:
22
+
23
+ | Value | M3 name | Leading / trailing |
24
+ | ---------- | ------------ | --------------------------- |
25
+ | (omitted) | full-width | 0 / 0 (100%) |
26
+ | `"start"` | inset | 16dp / 0 (the list divider) |
27
+ | `"middle"` | middle-inset | 16dp / 16dp |
28
+ | `"end"` | — | 0 / 16dp (RTL-symmetric) |
29
+
30
+ Margins are logical (`ms`/`me`), so the inset follows the writing
31
+ direction.
32
+
33
+ ## Examples
34
+
35
+ ```tsx
36
+ <Divider />
37
+ <Divider inset="start" /> // list divider (leading 16dp)
38
+ <Divider inset="middle" /> // middle-inset (16dp both)
39
+ <Divider className="my-2" /> // tighter spacing
40
+ ```
41
+
42
+ ## Gotchas
43
+
44
+ - Renders an `<hr>` (1px `outline-variant`); default vertical margin is
45
+ 16dp (`my-4`) — override with `className` for context-specific spacing.
46
+ - Horizontal only. M3's web spec and @material/web don't define a vertical
47
+ divider (it exists only in Compose as `VerticalDivider`); for a vertical
48
+ rule use a `1px`-wide `bg-outline-variant` element directly.
@@ -0,0 +1,79 @@
1
+ # Dropdown
2
+
3
+ Trigger + anchor for the shared M3E vertical [Menu](Menu.md): it positions
4
+ the menu below the trigger and owns the open state (toggles on trigger
5
+ click, closes on outside click, item activation or Escape, and returns
6
+ focus to the trigger). All the menu visuals and keyboard come from `Menu`,
7
+ so `Dropdown.Item` is an alias of `Menu.Item` and you can also use
8
+ `Menu.Label` / `Menu.Divider` and the `selected` / `badge` item props.
9
+ Standard (surface-based) by default or `vibrant` (tertiary-based).
10
+
11
+ ## Import
12
+
13
+ ```tsx
14
+ import {Dropdown} from "react-material-expressive";
15
+ ```
16
+
17
+ ## API
18
+
19
+ ```ts
20
+ interface DropdownProps {
21
+ apart?: boolean; // detach the menu with a 4dp gap
22
+ children: ReactNode; // trigger
23
+ className?: string;
24
+ menu?: ReactNode; // Dropdown.Item list
25
+ menuClassName?: string;
26
+ vibrant?: boolean; // tertiary-based scheme instead of standard
27
+ }
28
+
29
+ interface DropdownItemProps {
30
+ children?;
31
+ className?;
32
+ disabled?: boolean;
33
+ label?: ReactNode;
34
+ leftElement?: ReactNode; // 20px box (use icon size 20)
35
+ onClick?: MouseEventHandler<HTMLButtonElement>;
36
+ rightElement?: ReactNode; // trailing meta (body-small)
37
+ }
38
+ ```
39
+
40
+ ## Example
41
+
42
+ ```tsx
43
+ <Dropdown
44
+ menu={
45
+ <>
46
+ <Dropdown.Item
47
+ label="Profile"
48
+ leftElement={<MaterialSymbol name="person" size={20} />}
49
+ />
50
+ <Dropdown.Item
51
+ label="Settings"
52
+ rightElement="⌘S"
53
+ onClick={openSettings}
54
+ />
55
+ </>
56
+ }>
57
+ <Button iconRight={<MaterialSymbol name="expand_more" size={18} />}>
58
+ Open
59
+ </Button>
60
+ </Dropdown>
61
+ ```
62
+
63
+ ## Gotchas
64
+
65
+ - The whole wrapper toggles on click, so clicking an item also closes the
66
+ menu (select-like behavior).
67
+ - The menu is content-width, clamped to the M3 spec's 112–280dp range
68
+ (`w-max min-w-[112px] max-w-[280px]`); use `apart` for a detached look.
69
+ - Icons are 20dp in the vertical menu — pass `size={20}` to `MaterialSymbol`
70
+ so the glyph fills its box (the default is 24).
71
+ - The first and last items round their outer corners (top / bottom) to
72
+ corner-medium so they sit concentric with the menu's corner-large and
73
+ don't read as a square cut against the rounded container; the inner
74
+ corners stay extra-small.
75
+ - The expressive shape-morph (corners growing on the active state) only
76
+ applies to submenu parents; this flat menu keeps the extra-small corner
77
+ and shows hover/focus/pressed through the state layer.
78
+ - For corner positioning use `OverflowMenu` instead (still the baseline
79
+ square menu — not yet migrated to the vertical menu).
@@ -0,0 +1,63 @@
1
+ # FAB / ExtendedFAB
2
+
3
+ M3 floating action buttons. FAB sizes 56/80/96 with shapes
4
+ large/large-increased/extra-large and icons 24/28/36 (80 is the default
5
+ M3 Expressive medium FAB); Extended FAB comes in the M3 Expressive sizes
6
+ small 56 (large shape, title-medium, icon 24, padding 16, gap 8), medium
7
+ 80 (large-increased, title-large, icon 28, padding 26, gap 12) and large
8
+ 96 (extra-large, headline-small, icon 36, padding 28, gap 16). Both rest
9
+ at elevation level 3 and raise to 4 on hover.
10
+
11
+ ## Import
12
+
13
+ ```tsx
14
+ import {ExtendedFAB, FAB} from "react-material-expressive";
15
+ ```
16
+
17
+ ## API
18
+
19
+ ```ts
20
+ interface FABProps extends ComponentProps<"button"> {
21
+ icon?: ReactNode;
22
+ size?: "fab" | "fabMedium" | "fabLarge"; // 56 / 80 / 96, default "fabMedium"
23
+ variant?: "primary" | "secondary" | "tertiary"; // default "primary"
24
+ }
25
+
26
+ interface ExtendedFABProps extends ComponentProps<"button"> {
27
+ icon?: ReactNode; // optional leading icon (24/28/36px box per size)
28
+ size?: "small" | "medium" | "large"; // 56 / 80 / 96, default "small"
29
+ text?: string; // label fallback when no children
30
+ variant?: "primary" | "secondary" | "tertiary"; // default "primary"
31
+ }
32
+ ```
33
+
34
+ ## Variants
35
+
36
+ - `primary` — primary-container (the M3 Expressive default color style).
37
+ - `secondary` — secondary-container.
38
+ - `tertiary` — tertiary-container.
39
+
40
+ The small FAB (40) and the `surface` color style were removed — both are no
41
+ longer recommended in M3 Expressive.
42
+
43
+ ## Examples
44
+
45
+ ```tsx
46
+ <FAB aria-label="Create" icon={<MaterialSymbol name="add" />} />
47
+ <FAB aria-label="Edit" size="fabLarge" variant="tertiary"
48
+ icon={<MaterialSymbol name="edit" size={36} />} />
49
+ <ExtendedFAB icon={<MaterialSymbol name="edit" />}>Compose</ExtendedFAB>
50
+ <ExtendedFAB size="medium" variant="secondary"
51
+ icon={<MaterialSymbol name="edit" size={28} />}>Compose</ExtendedFAB>
52
+ ```
53
+
54
+ ## Gotchas
55
+
56
+ - FAB is icon-only: pass `aria-label`.
57
+ - Use one FAB per screen (M3); prefer `secondary`/`tertiary` containers on
58
+ busy surfaces.
59
+ - Inside a DockedToolbar the FAB is flattened
60
+ automatically — no extra class needed.
61
+ - The spec's solid color styles (primary & on-primary, etc.) aren't
62
+ built-in variants; recolor via `className` (e.g.
63
+ `"bg-primary text-on-primary"`).
@@ -0,0 +1,76 @@
1
+ # FABMenu
2
+
3
+ M3 Expressive FAB menu: a 56dp FAB that morphs into a fully-round close
4
+ button — radius, container color (container → solid set color) and the
5
+ icon crossfade ride one fast-spatial spring, like Compose's
6
+ `ToggleFloatingActionButton` — revealing up to six menu-item pills above
7
+ it, right-aligned and staggered bottom-up (8dp gap to the FAB, 4dp
8
+ between items). Each pill reveals by width from its trailing edge
9
+ (fast-spatial spring) while fading in on a quicker track, and closing
10
+ runs the same springs in reverse, top-down — the Compose
11
+ `FloatingActionButtonMenu` choreography. Items use the medium button
12
+ metrics (height 56, `title-medium`, icon 24, paddings 24, level 3
13
+ shadow) on the set's container color (`md.comp.fab-menu` tokens).
14
+
15
+ ## Import
16
+
17
+ ```tsx
18
+ import {FABMenu} from "react-material-expressive";
19
+ ```
20
+
21
+ ## API
22
+
23
+ ```ts
24
+ interface FABMenuProps {
25
+ children?: ReactNode; // FABMenu.Item list (2–6 items per spec)
26
+ className?: string;
27
+ icon?: ReactNode; // FAB icon while closed (24px box)
28
+ labels?: FABMenuLabels; // trigger aria-labels
29
+ variant?: "primary" | "secondary" | "tertiary"; // color set
30
+ }
31
+
32
+ interface FABMenuLabels {
33
+ open?: string; // trigger aria-label while closed (default "Open menu")
34
+ close?: string; // trigger aria-label while open (default "Close menu")
35
+ }
36
+
37
+ interface FABMenuItemProps {
38
+ className?: string;
39
+ icon?: ReactNode; // 24px box
40
+ label?: ReactNode;
41
+ onClick?: MouseEventHandler;
42
+ }
43
+ ```
44
+
45
+ ## Example
46
+
47
+ ```tsx
48
+ <FABMenu
49
+ icon={<MaterialSymbol name="add" size={24} />}
50
+ labels={{open: "Create"}}>
51
+ <FABMenu.Item
52
+ icon={<MaterialSymbol name="edit" size={24} />}
53
+ label="Compose"
54
+ onClick={compose}
55
+ />
56
+ <FABMenu.Item
57
+ icon={<MaterialSymbol name="group" size={24} />}
58
+ label="New group"
59
+ onClick={newGroup}
60
+ />
61
+ </FABMenu>
62
+ ```
63
+
64
+ ## Gotchas
65
+
66
+ - Position the wrapper yourself — per spec the FAB sits with 16dp margins
67
+ (e.g. `className="fixed right-4 bottom-4"`); the menu opens upward from
68
+ the FAB's top trailing edge.
69
+ - Item clicks close the menu (so do Escape and outside clicks); run your
70
+ action in the item `onClick`.
71
+ - The spec recommends 2–6 items.
72
+ - Colors resolve through the `--md-sys-color-*` tokens of the chosen set,
73
+ so themes apply automatically.
74
+ - **BREAKING:** the `label` prop was replaced by `labels={{open, close}}`
75
+ (the open-state aria-label, previously hardcoded "Close menu", is now
76
+ `labels.close`).
@@ -0,0 +1,35 @@
1
+ # Gallery
2
+
3
+ Stacked media gallery with 8dp gaps; compose rows with `Gallery.Row` and
4
+ size each media by width fractions.
5
+
6
+ ## Import
7
+
8
+ ```tsx
9
+ import {Gallery} from "react-material-expressive";
10
+ ```
11
+
12
+ ## API
13
+
14
+ ```ts
15
+ type GalleryProps = ComponentProps<"div">;
16
+ type ImageRowProps = ComponentProps<"div">; // Gallery.Row
17
+ ```
18
+
19
+ ## Example
20
+
21
+ ```tsx
22
+ <Gallery>
23
+ <Gallery.Row>
24
+ <Img alt="" aspect={21 / 9} className="w-full" radius={16} src={hero} />
25
+ </Gallery.Row>
26
+ <Gallery.Row>
27
+ <Img alt="" aspect={1} className="w-1/2" radius={16} src={a} />
28
+ <Img alt="" aspect={1} className="w-1/2" radius={16} src={b} />
29
+ </Gallery.Row>
30
+ </Gallery>
31
+ ```
32
+
33
+ ## Gotchas
34
+
35
+ - Layout-only: pair with `Img`/`Video` or any media node.
@@ -0,0 +1,36 @@
1
+ # Icon
2
+
3
+ Icon slot helper: wraps each provided node in a fixed square box (default
4
+ 24px, `shrink-0`, centered, `leading-none`) so arbitrary icons align with
5
+ adjacent text. Used internally by buttons/menus; useful for custom rows.
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import {Icon} from "react-material-expressive";
11
+ ```
12
+
13
+ ## API
14
+
15
+ ```ts
16
+ interface IconProps {
17
+ className?: string;
18
+ icon?: ReactNode; // standalone slot
19
+ iconLeft?: ReactNode; // leading slot
20
+ iconRight?: ReactNode; // trailing slot
21
+ size?: number; // box px, default 24
22
+ }
23
+ ```
24
+
25
+ ## Example
26
+
27
+ ```tsx
28
+ <span className="flex items-center gap-2 text-body-large">
29
+ <Icon iconLeft={<MaterialSymbol name="event" />} />
30
+ Aligned with text
31
+ </span>
32
+ ```
33
+
34
+ ## Gotchas
35
+
36
+ - The box fixes ALIGNMENT; size the glyph itself via the icon you pass.
@@ -0,0 +1,69 @@
1
+ # IconButton
2
+
3
+ M3 icon button: 40×40 visual container with a 48×48 touch target, 24px
4
+ icon, full shape. M3 Expressive sizes, widths, shapes and the toggle
5
+ (`selected`) variant are available.
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import {IconButton} from "react-material-expressive";
11
+ ```
12
+
13
+ ## API
14
+
15
+ ```ts
16
+ interface IconButtonProps extends ComponentProps<"button"> {
17
+ icon?: ReactNode; // 24px box; children work as a fallback slot
18
+ selected?: boolean; // M3 Expressive toggle (adds aria-pressed)
19
+ shape?: "round" | "square"; // M3 Expressive; default "round"
20
+ size?: "xs" | "s" | "m" | "l" | "xl"; // M3 Expressive: 32/40/56/96/136, default "s"
21
+ variant?: "filled" | "tonal" | "outlined" | "standard"; // default "filled"
22
+ width?: "narrow" | "default" | "wide"; // M3 Expressive
23
+ }
24
+ ```
25
+
26
+ ## Variants
27
+
28
+ - `filled` — primary container.
29
+ - `tonal` — secondary-container.
30
+ - `outlined` — outline border, on-surface-variant icon.
31
+ - `standard` — no container, on-surface-variant icon.
32
+
33
+ ## Expressive sizes, widths and toggle
34
+
35
+ Sizes use icons 20/24/24/32/40; `width` narrows or widens the container
36
+ per size. Pressing morphs both shapes to the per-size pressed radius
37
+ (8/8/12/16/16, the DSDB PressedContainerShape tokens). With `selected` the button becomes a toggle: colors follow the
38
+ M3 Expressive toggle tokens per variant (filled: surface-container ↔
39
+ primary; tonal: secondary-container ↔ secondary; outlined selected:
40
+ inverse-surface; standard: on-surface-variant ↔ primary) and the shape
41
+ flips with the selection (unselected round, selected square) unless
42
+ `shape` is set explicitly.
43
+
44
+ ## States
45
+
46
+ State layers on hover/focus/pressed (the press ripple grows from the
47
+ pointer); `disabled` = container `on-surface/12` + icon `on-surface/38`
48
+ (standard/outlined keep no fill).
49
+
50
+ ## Examples
51
+
52
+ ```tsx
53
+ <IconButton
54
+ aria-label="Settings"
55
+ icon={<MaterialSymbol name="settings" />}
56
+ variant="standard"
57
+ />
58
+ <IconButton
59
+ aria-label="Favorite"
60
+ icon={<MaterialSymbol name="favorite" />}
61
+ selected={isFavorite}
62
+ onClick={() => setIsFavorite((value) => !value)}
63
+ />
64
+ ```
65
+
66
+ ## Gotchas
67
+
68
+ - Icon-only: always pass `aria-label`.
69
+ - The 48×48 touch target is a child span on the sub-48 sizes (`xs`/`s`).
@@ -0,0 +1,52 @@
1
+ # Img
2
+
3
+ MediaFrame + image: renders a native `<img>` (object-fit, hides on error
4
+ so the placeholder shows) or your injected media. Accepts
5
+ `string | StaticImageData` sources.
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import {Img} from "react-material-expressive";
11
+ ```
12
+
13
+ ## API
14
+
15
+ ```ts
16
+ interface ImgProps extends MediaInjectionProps {
17
+ // children | image | render
18
+ alt?: string;
19
+ aspect?: number | string; // e.g. 16/9
20
+ className?: string;
21
+ height?: number | string;
22
+ width?: number | string;
23
+ objectFit?: "fill" | "contain" | "cover" | "none" | "scale-down"; // default cover
24
+ onClick?: MouseEventHandler<HTMLDivElement>;
25
+ placeholder?: ReactNode; // shown with no media or on error
26
+ radius?: number | string; // px or CSS value
27
+ size?: number | string; // width = height shorthand
28
+ src?: string | StaticImageData;
29
+ style?: CSSProperties;
30
+ }
31
+ ```
32
+
33
+ Injection priority: `render({src, alt, className, style})` > `image` >
34
+ `children` > native `<img>`.
35
+
36
+ ## Examples
37
+
38
+ ```tsx
39
+ <Img alt="Cover" aspect={16 / 9} radius={16} src="/cover.jpg" width={320} />
40
+ <Img alt="Broken" placeholder={<MaterialSymbol name="broken_image" />} size={140} src={maybeUrl} />
41
+
42
+ // Next.js injection
43
+ <Img aspect={1} size={140} src={photo} render={({src, alt, className, style}) => (
44
+ <Image alt={alt} className={className} fill src={src!} style={style} />
45
+ )} />
46
+ ```
47
+
48
+ ## Gotchas
49
+
50
+ - The frame paints `surface-container-highest` behind the media — visible
51
+ while loading and on error.
52
+ - `radius` accepts CSS strings (e.g. `"var(--md-sys-shape-corner-large)"`).
@@ -0,0 +1,43 @@
1
+ # Layers — Container / Section
2
+
3
+ Layout helpers. `Container` is a dotted showcase panel (token-based dot
4
+ grid, large shape) for presenting content over a neutral, theme-aware
5
+ background. `Section` is a responsive content band: column on small
6
+ screens, row on `lg+`.
7
+
8
+ ## Import
9
+
10
+ ```tsx
11
+ import {Container, Section} from "react-material-expressive";
12
+ ```
13
+
14
+ ## API
15
+
16
+ ```ts
17
+ interface ContainerProps {
18
+ children?: ReactNode;
19
+ className?: string;
20
+ padding?: string; // padding class override (default p-6)
21
+ }
22
+
23
+ interface SectionProps {
24
+ children: ReactNode;
25
+ className?: string;
26
+ }
27
+ ```
28
+
29
+ ## Example
30
+
31
+ ```tsx
32
+ <Section>
33
+ <Card>…</Card>
34
+ <Container className="mx-0">
35
+ <Button>Showcased</Button>
36
+ </Container>
37
+ </Section>
38
+ ```
39
+
40
+ ## Gotchas
41
+
42
+ - The dot grid uses `--md-sys-color-outline-variant`, so it adapts to the
43
+ active theme.
@@ -0,0 +1,43 @@
1
+ # LinkBox / LinkContainer
2
+
3
+ Native `<a>` links — no router coupling. `LinkBox` is a **text link** that
4
+ reads as a link: primary color, **underlined at rest** (the underline is not
5
+ color-only on purpose — an inline link must be distinguishable from body text
6
+ without relying on color, WCAG 1.4.1, since `primary` vs text is only
7
+ 2.64:1 light / 1.31:1 dark). The current page (`active`) gets a heavier
8
+ underline (no color change) + `aria-current="page"`. `LinkContainer` is a
9
+ block-level link that wraps arbitrary content (cards, media) without adding
10
+ text styling.
11
+
12
+ ## Import
13
+
14
+ ```tsx
15
+ import {LinkBox, LinkContainer} from "react-material-expressive";
16
+ ```
17
+
18
+ ## API
19
+
20
+ ```ts
21
+ interface LinkBoxProps extends ComponentProps<"a"> {
22
+ active?: boolean; // explicit current-page (heavier underline); else derived
23
+ currentPath?: string; // pathname from YOUR router
24
+ }
25
+ type LinkContainerProps = ComponentProps<"a">;
26
+ ```
27
+
28
+ ## Examples
29
+
30
+ ```tsx
31
+ <p>Read the <LinkBox href="/guidelines">guidelines</LinkBox>.</p>
32
+ <LinkBox currentPath={pathname} href="/docs">Docs</LinkBox>
33
+
34
+ <LinkContainer aria-label="Open article" href="/post/42">
35
+ <Card variant="outlined">…</Card>
36
+ </LinkContainer>
37
+ ```
38
+
39
+ ## Gotchas
40
+
41
+ - Client-side routing: intercept `onClick` (preventDefault + router.push)
42
+ or wrap with your framework's Link passing these as children styles.
43
+ - `LinkBox` inherits font size; set type scale via `className`.
@@ -0,0 +1,87 @@
1
+ # List
2
+
3
+ M3 list: transparent container (inherits the surface it sits on) with
4
+ 8dp vertical padding; item height follows the text-row count — **56dp**
5
+ one-line, **72dp** two-line, **88dp** three-line (overline + headline +
6
+ supporting; three-line items top-align their leading + text, the trailing
7
+ element stays centered). 16dp leading/trailing space, 12dp between elements.
8
+ Compose with `List.Item`. `variant` defaults to `"expressive"` (filled
9
+ `surface-container` tiles separated by a 2dp gap, with a container shape that
10
+ morphs on interaction (4 → 12 → 16dp) and 20px icons — recommended for new
11
+ designs); pass `variant="plain"` for the transparent, rectangular list with
12
+ 24px icons, which M3E keeps available.
13
+
14
+ ## Import
15
+
16
+ ```tsx
17
+ import {List} from "react-material-expressive";
18
+ ```
19
+
20
+ ## API
21
+
22
+ ```ts
23
+ interface ListProps extends ComponentProps<"ul"> {
24
+ variant?: "expressive" | "plain"; // default "expressive"
25
+ }
26
+
27
+ interface ListItemProps {
28
+ body?: ReactNode; // supporting text → 72dp item
29
+ children?: ReactNode; // custom content replacing the text block
30
+ className?: string;
31
+ disabled?: boolean;
32
+ headline?: ReactNode; // body-large
33
+ href?: string; // renders a native <a>
34
+ label?: ReactNode; // overline (label-small)
35
+ leftElement?: ReactNode; // 24px min box, on-surface-variant
36
+ onClick?: MouseEventHandler<HTMLElement>;
37
+ rightElement?: ReactNode;
38
+ selected?: boolean; // secondary-container fill + on-secondary-container (visual)
39
+ target?: string;
40
+ }
41
+ ```
42
+
43
+ Interactive items (`href`/`onClick`) render `<a>`/`<button>` with an M3
44
+ state layer; static items render a plain row. `selected` is visual only
45
+ (secondary-container) — the consumer owns selection semantics, as with
46
+ `Menu.Item`.
47
+
48
+ ## Example
49
+
50
+ ```tsx
51
+ <List>
52
+ <List.Item
53
+ headline="Inbox"
54
+ leftElement={<MaterialSymbol name="inbox" />}
55
+ onClick={openInbox}
56
+ rightElement={<span>24</span>}
57
+ />
58
+ <List.Item
59
+ headline="Profile"
60
+ body="Account settings"
61
+ href="/profile"
62
+ leftElement={<Avatar name="A" size={40} />}
63
+ />
64
+ </List>
65
+
66
+ // Plain (transparent, rectangular) variant.
67
+ <List variant="plain">
68
+ <List.Item
69
+ headline="Inbox"
70
+ leftElement={<MaterialSymbol name="inbox" />}
71
+ onClick={openInbox}
72
+ />
73
+ </List>
74
+ ```
75
+
76
+ ## Gotchas
77
+
78
+ - The container is transparent on purpose (M3) — it picks up the sheet,
79
+ card or surface behind it.
80
+ - For dividers between items use `<Divider className="my-0" inset />`.
81
+ - The default is the **Expressive** variant (`variant="expressive"`): filled
82
+ `surface-container` tiles with a shape that morphs on interaction
83
+ (extra-small → medium → large) and 20px icons. The filled tiles read against
84
+ a plain `surface` background; inside a `surface-container` set a different
85
+ container via `className`. Pass `variant="plain"` for the M3 **plain** list
86
+ (transparent, rectangular items, 24px icons), which M3E keeps fully
87
+ supported.