@bug-on/md3-react 0.1.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 (96) hide show
  1. package/README.md +215 -0
  2. package/dist/assets/fonts/GoogleSansFlex-VariableFont.woff2 +0 -0
  3. package/dist/assets/fonts/MaterialSymbolsOutlined-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
  4. package/dist/assets/fonts/MaterialSymbolsRounded-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
  5. package/dist/assets/fonts/MaterialSymbolsSharp-VariableFont_FILL,GRAD,opsz,wght.ttf +0 -0
  6. package/dist/assets/loading-indicator.svg +19 -0
  7. package/dist/assets/material-symbols-cdn.css +65 -0
  8. package/dist/assets/material-symbols-self-hosted.css +109 -0
  9. package/dist/hooks/index.d.ts +3 -0
  10. package/dist/hooks/useMediaQuery.d.ts +11 -0
  11. package/dist/hooks/useRipple.d.ts +26 -0
  12. package/dist/index.d.ts +65 -0
  13. package/dist/index.js +9059 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/index.mjs +8929 -0
  16. package/dist/index.mjs.map +1 -0
  17. package/dist/lib/material-symbols-preconnect.d.ts +42 -0
  18. package/dist/lib/theme-utils.d.ts +63 -0
  19. package/dist/lib/utils.d.ts +2 -0
  20. package/dist/material-symbols-cdn.css +65 -0
  21. package/dist/material-symbols-self-hosted.css +109 -0
  22. package/dist/types/index.d.ts +1 -0
  23. package/dist/types/md3.d.ts +14 -0
  24. package/dist/typography.css +22 -0
  25. package/dist/ui/badge.d.ts +125 -0
  26. package/dist/ui/button-group.d.ts +59 -0
  27. package/dist/ui/button.d.ts +148 -0
  28. package/dist/ui/card.d.ts +62 -0
  29. package/dist/ui/checkbox.d.ts +82 -0
  30. package/dist/ui/chip.d.ts +110 -0
  31. package/dist/ui/code-block.d.ts +14 -0
  32. package/dist/ui/dialog.d.ts +111 -0
  33. package/dist/ui/divider.d.ts +164 -0
  34. package/dist/ui/drawer.d.ts +39 -0
  35. package/dist/ui/dropdown.d.ts +29 -0
  36. package/dist/ui/fab-menu.d.ts +204 -0
  37. package/dist/ui/fab.d.ts +162 -0
  38. package/dist/ui/icon-button.d.ts +131 -0
  39. package/dist/ui/icon.d.ts +88 -0
  40. package/dist/ui/loading-indicator.d.ts +42 -0
  41. package/dist/ui/navigation-rail.d.ts +29 -0
  42. package/dist/ui/progress-indicator/circular.d.ts +3 -0
  43. package/dist/ui/progress-indicator/hooks.d.ts +3 -0
  44. package/dist/ui/progress-indicator/index.d.ts +21 -0
  45. package/dist/ui/progress-indicator/linear-flat.d.ts +10 -0
  46. package/dist/ui/progress-indicator/linear-wavy.d.ts +18 -0
  47. package/dist/ui/progress-indicator/linear.d.ts +3 -0
  48. package/dist/ui/progress-indicator/types.d.ts +151 -0
  49. package/dist/ui/progress-indicator/utils.d.ts +3 -0
  50. package/dist/ui/radio-button.d.ts +106 -0
  51. package/dist/ui/ripple.d.ts +126 -0
  52. package/dist/ui/scroll-area.d.ts +27 -0
  53. package/dist/ui/shared/constants.d.ts +86 -0
  54. package/dist/ui/shared/touch-target.d.ts +38 -0
  55. package/dist/ui/snackbar/index.d.ts +6 -0
  56. package/dist/ui/snackbar/snackbar.d.ts +196 -0
  57. package/dist/ui/switch/index.d.ts +7 -0
  58. package/dist/ui/switch/switch.d.ts +30 -0
  59. package/dist/ui/switch/switch.stories.d.ts +48 -0
  60. package/dist/ui/switch/switch.tokens.d.ts +67 -0
  61. package/dist/ui/switch/switch.types.d.ts +59 -0
  62. package/dist/ui/tabs/index.d.ts +10 -0
  63. package/dist/ui/tabs/tab.d.ts +43 -0
  64. package/dist/ui/tabs/tabs-content.d.ts +36 -0
  65. package/dist/ui/tabs/tabs-list.d.ts +40 -0
  66. package/dist/ui/tabs/tabs.d.ts +60 -0
  67. package/dist/ui/tabs/tabs.tokens.d.ts +94 -0
  68. package/dist/ui/tabs/tabs.types.d.ts +172 -0
  69. package/dist/ui/text-field/index.d.ts +11 -0
  70. package/dist/ui/text-field/subcomponents/active-indicator.d.ts +24 -0
  71. package/dist/ui/text-field/subcomponents/floating-label.d.ts +43 -0
  72. package/dist/ui/text-field/subcomponents/leading-icon.d.ts +23 -0
  73. package/dist/ui/text-field/subcomponents/outline-container.d.ts +42 -0
  74. package/dist/ui/text-field/subcomponents/prefix-suffix.d.ts +24 -0
  75. package/dist/ui/text-field/subcomponents/supporting-text.d.ts +37 -0
  76. package/dist/ui/text-field/subcomponents/trailing-icon.d.ts +41 -0
  77. package/dist/ui/text-field/text-field.d.ts +49 -0
  78. package/dist/ui/text-field/text-field.tokens.d.ts +76 -0
  79. package/dist/ui/text-field/text-field.types.d.ts +126 -0
  80. package/dist/ui/theme-provider/index.d.ts +18 -0
  81. package/dist/ui/toc.d.ts +74 -0
  82. package/dist/ui/tooltip/index.d.ts +8 -0
  83. package/dist/ui/tooltip/plain-tooltip.d.ts +2 -0
  84. package/dist/ui/tooltip/rich-tooltip.d.ts +2 -0
  85. package/dist/ui/tooltip/tooltip-box.d.ts +2 -0
  86. package/dist/ui/tooltip/tooltip-caret-shape.d.ts +9 -0
  87. package/dist/ui/tooltip/tooltip.tokens.d.ts +26 -0
  88. package/dist/ui/tooltip/tooltip.types.d.ts +56 -0
  89. package/dist/ui/tooltip/use-tooltip-position.d.ts +8 -0
  90. package/dist/ui/tooltip/use-tooltip-state.d.ts +2 -0
  91. package/dist/ui/typography/index.d.ts +16 -0
  92. package/dist/ui/typography/type-scale-tokens.d.ts +162 -0
  93. package/dist/ui/typography/typography-key-tokens.d.ts +40 -0
  94. package/dist/ui/typography/typography-tokens.d.ts +220 -0
  95. package/dist/ui/typography/typography.d.ts +265 -0
  96. package/package.json +80 -0
@@ -0,0 +1,196 @@
1
+ /**
2
+ * @file snackbar.tsx
3
+ *
4
+ * MD3 Expressive Snackbar component.
5
+ *
6
+ * Architecture:
7
+ * - `Snackbar` → Pure display component (motion.div, role="status", aria-live="polite")
8
+ * - `SnackbarHost` → AnimatePresence container + queue flush (place once in layout)
9
+ * - `SnackbarProvider` → Context provider that wires SnackbarHost + exposes `useSnackbar`
10
+ * - `useSnackbarState` → Low-level ref-based queue hook (mutex pattern)
11
+ * - `useSnackbar` → Consumer hook for imperative `showSnackbar(visuals)` calls
12
+ *
13
+ * Queue strategy:
14
+ * - One snackbar visible at a time (MD3 spec).
15
+ * - Subsequent `showSnackbar()` calls are enqueued, shown as soon as current one dismisses.
16
+ * - Cleanup: on unmount, all pending promises resolve as 'dismissed'.
17
+ *
18
+ * @see https://m3.material.io/components/snackbar/overview
19
+ */
20
+ import * as React from "react";
21
+ /**
22
+ * Duration preset for the snackbar auto-dismiss timer.
23
+ * - `'short'` → 4 000 ms (default, MD3 spec)
24
+ * - `'long'` → 7 000 ms
25
+ * - `number` → custom milliseconds
26
+ */
27
+ export type SnackbarDuration = "short" | "long" | number;
28
+ /**
29
+ * Resolution value returned by the `showSnackbar()` promise.
30
+ * - `'action-performed'` → user clicked the action button
31
+ * - `'dismissed'` → auto-dismissed or close button clicked
32
+ */
33
+ export type SnackbarResult = "action-performed" | "dismissed";
34
+ /**
35
+ * Visual configuration for a single snackbar instance.
36
+ */
37
+ export interface SnackbarVisuals {
38
+ /** Main message text. */
39
+ message: string;
40
+ /** Label for the optional action button. */
41
+ actionLabel?: string;
42
+ /** When `true`, renders a close (X) icon button. @default false */
43
+ withDismissAction?: boolean;
44
+ /**
45
+ * When `true`, renders the action button below the message (Column layout).
46
+ * Use when both message and actionLabel are long.
47
+ * @default false
48
+ */
49
+ actionOnNewLine?: boolean;
50
+ /**
51
+ * Auto-dismiss duration.
52
+ * @default 'short' (4 000 ms)
53
+ */
54
+ duration?: SnackbarDuration;
55
+ /** Additional className applied to the snackbar container. */
56
+ className?: string;
57
+ }
58
+ /**
59
+ * Internal runtime data for a currently-displayed snackbar.
60
+ * Includes the resolve callback to settle the caller's promise.
61
+ */
62
+ export interface SnackbarData {
63
+ /** Unique key for AnimatePresence element diffing. */
64
+ id: string;
65
+ /** Visual configuration. */
66
+ visuals: SnackbarVisuals;
67
+ /** Settles the promise returned by `showSnackbar()`. */
68
+ resolve: (result: SnackbarResult) => void;
69
+ }
70
+ /** Props for the pure `Snackbar` display component. */
71
+ export interface SnackbarProps {
72
+ /** Runtime data including message, actions, and resolve callback. */
73
+ data: SnackbarData;
74
+ /** Additional className merged onto the snackbar container. */
75
+ className?: string;
76
+ }
77
+ /** Props for the `SnackbarHost` component. */
78
+ export interface SnackbarHostProps {
79
+ /** State returned by `useSnackbarState()`. */
80
+ state: UseSnackbarStateReturn;
81
+ /** Additional className applied to the fixed host wrapper. */
82
+ className?: string;
83
+ }
84
+ /** Return type of `useSnackbarState`. */
85
+ export interface UseSnackbarStateReturn {
86
+ /** Currently visible snackbar data, or `null` when idle. */
87
+ current: SnackbarData | null;
88
+ /**
89
+ * Show a snackbar with the given visuals.
90
+ * Returns a promise that resolves when the snackbar is dismissed or the action is triggered.
91
+ */
92
+ showSnackbar: (visuals: SnackbarVisuals) => Promise<SnackbarResult>;
93
+ /** Internal dismiss handler — called by `SnackbarHost`. */
94
+ _dismiss: (result: SnackbarResult) => void;
95
+ }
96
+ /**
97
+ * Low-level hook that manages the snackbar queue and current state.
98
+ *
99
+ * Uses a `ref`-based queue (mutex pattern) so that enqueueing never
100
+ * triggers a re-render storm — only the state transition does.
101
+ *
102
+ * @example
103
+ * ```tsx
104
+ * // Used internally by SnackbarProvider
105
+ * const state = useSnackbarState();
106
+ * return <SnackbarHost state={state} />;
107
+ * ```
108
+ */
109
+ export declare function useSnackbarState(): UseSnackbarStateReturn;
110
+ /**
111
+ * MD3 Expressive Snackbar — pure display component.
112
+ *
113
+ * Renders a single snackbar with message, optional action button, and
114
+ * optional dismiss icon button. Handles its own auto-dismiss timer.
115
+ *
116
+ * @remarks
117
+ * - Uses `role="status"` + `aria-live="polite"` for screen reader announcements.
118
+ * - All entrance/exit animation is handled by the parent `SnackbarHost` via
119
+ * `AnimatePresence` + `SNACKBAR_ANIM`.
120
+ * - Do NOT render this component directly — use `SnackbarHost`.
121
+ *
122
+ * @example
123
+ * ```tsx
124
+ * // Internal usage inside SnackbarHost — not for direct use
125
+ * <Snackbar data={currentSnackbarData} />
126
+ * ```
127
+ */
128
+ export declare const Snackbar: React.NamedExoticComponent<SnackbarProps>;
129
+ /**
130
+ * MD3 SnackbarHost — renders the AnimatePresence container for snackbar queue.
131
+ *
132
+ * Place this once in your app layout. It will show snackbars one at a time,
133
+ * dequeuing the next one as each dismisses.
134
+ *
135
+ * @example
136
+ * ```tsx
137
+ * // Typically used inside SnackbarProvider — not directly
138
+ * const state = useSnackbarState();
139
+ * <SnackbarHost state={state} />
140
+ * ```
141
+ */
142
+ export declare function SnackbarHost({ state, className }: SnackbarHostProps): import("react/jsx-runtime").JSX.Element;
143
+ export declare namespace SnackbarHost {
144
+ var displayName: string;
145
+ }
146
+ interface SnackbarContextValue {
147
+ showSnackbar: (visuals: SnackbarVisuals) => Promise<SnackbarResult>;
148
+ }
149
+ /**
150
+ * MD3 SnackbarProvider — context provider for imperative snackbar API.
151
+ *
152
+ * Wrap your application (or a section of it) with this provider.
153
+ * Then use `useSnackbar()` in any descendant to show snackbars.
154
+ *
155
+ * @example
156
+ * ```tsx
157
+ * // In your root layout:
158
+ * <SnackbarProvider>
159
+ * <App />
160
+ * </SnackbarProvider>
161
+ *
162
+ * // In any component:
163
+ * const { showSnackbar } = useSnackbar();
164
+ * await showSnackbar({ message: 'Saved!', actionLabel: 'Undo' });
165
+ * ```
166
+ */
167
+ export declare function SnackbarProvider({ children }: {
168
+ children: React.ReactNode;
169
+ }): import("react/jsx-runtime").JSX.Element;
170
+ export declare namespace SnackbarProvider {
171
+ var displayName: string;
172
+ }
173
+ /**
174
+ * Hook that returns the `showSnackbar` function from the nearest `SnackbarProvider`.
175
+ *
176
+ * @throws {Error} if used outside of a `SnackbarProvider`.
177
+ *
178
+ * @example
179
+ * ```tsx
180
+ * function SaveButton() {
181
+ * const { showSnackbar } = useSnackbar();
182
+ *
183
+ * const handleSave = async () => {
184
+ * const result = await showSnackbar({
185
+ * message: 'Changes saved',
186
+ * actionLabel: 'Undo',
187
+ * });
188
+ * if (result === 'action-performed') undoSave();
189
+ * };
190
+ *
191
+ * return <button onClick={handleSave}>Save</button>;
192
+ * }
193
+ * ```
194
+ */
195
+ export declare function useSnackbar(): SnackbarContextValue;
196
+ export {};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @file switch/index.ts
3
+ * Public exports for the MD3 Expressive Switch component.
4
+ */
5
+ export { Switch } from "./switch";
6
+ export { SwitchColors, SwitchTokens } from "./switch.tokens";
7
+ export type { SwitchProps } from "./switch.types";
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @file switch.tsx
3
+ * MD3 Expressive Switch — ARIA switch pattern with Framer Motion animations.
4
+ * Spec: https://m3.material.io/components/switch/overview
5
+ *
6
+ * Key decisions:
7
+ * - Uses `<button role="switch">` (no <input>) per MD3 accessibility spec
8
+ * - Framer Motion for ALL animations (thumb x, size morph, state layer, icons)
9
+ * - Hover state via useState (required for Framer Motion color animate)
10
+ * - Disabled colors via rgba() literals (color-mix() not animatable by FM)
11
+ */
12
+ import * as React from "react";
13
+ import type { SwitchProps } from "./switch.types";
14
+ /**
15
+ * MD3 Expressive Switch component.
16
+ *
17
+ * Toggles a single item on or off. Implements the ARIA switch pattern
18
+ * (`role="switch"`) without `<input>`. Fully animated per MD3 spec:
19
+ * thumb translation, size morph (16→24→28px), state layer, and icon cross-fade.
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * <Switch checked={isOn} onCheckedChange={setIsOn} label="Wi-Fi" />
24
+ * <Switch checked={isOn} onCheckedChange={setIsOn} icons thumbContent={<CheckIcon />} />
25
+ * <Switch checked={isOn} onCheckedChange={setIsOn} disabled />
26
+ * ```
27
+ *
28
+ * @see https://m3.material.io/components/switch/overview
29
+ */
30
+ export declare const Switch: React.NamedExoticComponent<SwitchProps & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @file switch.stories.tsx
3
+ * MD3 Expressive Switch — all usage patterns and states.
4
+ *
5
+ * Can be used as:
6
+ * 1. Storybook stories (if Storybook is configured)
7
+ * 2. Standalone demo component for the docs app
8
+ *
9
+ * Covers all 7 patterns from the MD3 Switch specification:
10
+ * 1. Basic toggle
11
+ * 2. With label
12
+ * 3. With icons (both states)
13
+ * 4. With only selected icon
14
+ * 5. Disabled (checked)
15
+ * 6. Disabled (unchecked)
16
+ * 7. All states grid
17
+ */
18
+ /** Basic stateful switch — no label, no icons. */
19
+ export declare function Basic(): import("react/jsx-runtime").JSX.Element;
20
+ /** Switch with a visible text label using htmlFor linkage. */
21
+ export declare function WithLabel(): import("react/jsx-runtime").JSX.Element;
22
+ /**
23
+ * Switch showing icons in both checked and unchecked states.
24
+ * Uses check icon when on, close icon when off.
25
+ */
26
+ export declare function WithIcons(): import("react/jsx-runtime").JSX.Element;
27
+ /**
28
+ * Switch showing the icon only in the checked (selected) state.
29
+ * Unselected state has no icon.
30
+ */
31
+ export declare function WithOnlySelectedIcon(): import("react/jsx-runtime").JSX.Element;
32
+ /** Disabled switch in the checked (on) state. */
33
+ export declare function DisabledChecked(): import("react/jsx-runtime").JSX.Element;
34
+ /** Disabled switch in the unchecked (off) state. */
35
+ export declare function DisabledUnchecked(): import("react/jsx-runtime").JSX.Element;
36
+ /**
37
+ * Grid displaying all Switch state combinations:
38
+ * - Enabled: checked / unchecked
39
+ * - Disabled: checked / unchecked
40
+ * - With icons: checked / unchecked
41
+ * - With only selected icon: checked / unchecked
42
+ */
43
+ export declare function AllStatesGrid(): import("react/jsx-runtime").JSX.Element;
44
+ /**
45
+ * All switch stories in one scrollable demo page.
46
+ * Used by the docs app to showcase all variants.
47
+ */
48
+ export declare function SwitchDemo(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @file switch.tokens.ts
3
+ * MD3 Expressive Switch — Design tokens ported from SwitchTokens.kt.
4
+ * All dimensional values are in px (dp equivalent for web).
5
+ * @see docs/m3/switch/SwitchTokens.kt
6
+ */
7
+ /**
8
+ * Design tokens for the MD3 Expressive Switch component.
9
+ *
10
+ * Maps directly from `SwitchTokens.kt` (v0_210) to CSS/JS values.
11
+ * Use these as the single source of truth for sizing and opacity.
12
+ *
13
+ * Color tokens are NOT included here — they reference CSS custom properties
14
+ * from the project's MD3 theme system (`--md-sys-color-*`).
15
+ */
16
+ export declare const SwitchTokens: {
17
+ /** SwitchTokens.TrackWidth = 52dp */
18
+ readonly trackWidth: 52;
19
+ /** SwitchTokens.TrackHeight = 32dp */
20
+ readonly trackHeight: 32;
21
+ /** SwitchTokens.TrackOutlineWidth = 2dp */
22
+ readonly trackOutlineWidth: 2;
23
+ /** SwitchTokens.SelectedHandleWidth/Height = 24dp */
24
+ readonly selectedHandleSize: 24;
25
+ /** SwitchTokens.UnselectedHandleWidth/Height = 16dp */
26
+ readonly unselectedHandleSize: 16;
27
+ /** SwitchTokens.IconHandleWidth/Height = 24dp (when thumb has icon content) */
28
+ readonly iconHandleSize: 24;
29
+ /** SwitchTokens.PressedHandleWidth/Height = 28dp */
30
+ readonly pressedHandleSize: 28;
31
+ /** SwitchTokens.StateLayerSize = 40dp */
32
+ readonly stateLayerSize: 40;
33
+ /** SwitchTokens.SelectedIconSize / UnselectedIconSize = 16dp */
34
+ readonly iconSize: 16;
35
+ /** SwitchTokens.DisabledTrackOpacity = 0.12 */
36
+ readonly disabledTrackOpacity: 0.12;
37
+ /** SwitchTokens.DisabledSelectedHandleOpacity = 1.0 */
38
+ readonly disabledSelectedHandleOpacity: 1;
39
+ /** SwitchTokens.DisabledUnselectedHandleOpacity = 0.38 */
40
+ readonly disabledUnselectedHandleOpacity: 0.38;
41
+ /** SwitchTokens.DisabledSelectedIconOpacity = 0.38 */
42
+ readonly disabledSelectedIconOpacity: 0.38;
43
+ /** SwitchTokens.DisabledUnselectedIconOpacity = 0.38 */
44
+ readonly disabledUnselectedIconOpacity: 0.38;
45
+ };
46
+ /**
47
+ * CSS custom property references for Switch colors.
48
+ * These map to the project's `--md-sys-color-*` tokens in `colors.css`.
49
+ *
50
+ * DO NOT hardcode hex values — always use these references so the
51
+ * component automatically adapts to light/dark theme switching.
52
+ */
53
+ export declare const SwitchColors: {
54
+ readonly checkedTrack: "var(--md-sys-color-primary)";
55
+ readonly uncheckedTrack: "var(--md-sys-color-surface-container-highest)";
56
+ readonly uncheckedTrackOutline: "var(--md-sys-color-outline)";
57
+ readonly checkedThumb: "var(--md-sys-color-on-primary)";
58
+ readonly uncheckedThumb: "var(--md-sys-color-outline)";
59
+ readonly hoverCheckedThumb: "var(--md-sys-color-primary-container)";
60
+ readonly hoverUncheckedThumb: "var(--md-sys-color-on-surface-variant)";
61
+ readonly disabledCheckedThumb: "var(--md-sys-color-surface)";
62
+ readonly checkedIcon: "var(--md-sys-color-on-primary-container)";
63
+ readonly uncheckedIcon: "var(--md-sys-color-surface-container-highest)";
64
+ readonly checkedStateLayer: "var(--md-sys-color-primary)";
65
+ readonly uncheckedStateLayer: "var(--md-sys-color-on-surface)";
66
+ readonly focusIndicator: "var(--md-sys-color-secondary)";
67
+ };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @file switch.types.ts
3
+ * MD3 Expressive Switch — TypeScript prop definitions.
4
+ * Spec: https://m3.material.io/components/switch/overview
5
+ */
6
+ import type * as React from "react";
7
+ /**
8
+ * Props for the `Switch` component.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <Switch checked={isOn} onCheckedChange={setIsOn} label="Wi-Fi" />
13
+ * ```
14
+ */
15
+ export interface SwitchProps {
16
+ /** Controlled checked (on) state. */
17
+ checked: boolean;
18
+ /** Called when the switch is toggled. Not called when disabled. */
19
+ onCheckedChange: (checked: boolean) => void;
20
+ /** Disables interaction and applies disabled visual state. @default false */
21
+ disabled?: boolean;
22
+ /**
23
+ * Optional icon content rendered inside the thumb.
24
+ * Expected to measure 16dp (SwitchTokens.iconSize).
25
+ */
26
+ thumbContent?: React.ReactNode;
27
+ /**
28
+ * When true, shows thumb icons in both selected and unselected states.
29
+ * Requires `thumbContent` to be provided.
30
+ * @default false
31
+ */
32
+ icons?: boolean;
33
+ /**
34
+ * When true, shows the icon only in the selected/checked state.
35
+ * Requires `thumbContent` to be provided.
36
+ * @default false
37
+ */
38
+ showOnlySelectedIcon?: boolean;
39
+ /**
40
+ * Visible label text rendered adjacent to the switch.
41
+ * When provided, wraps the switch in a `<label>` for accessibility.
42
+ */
43
+ label?: string;
44
+ /**
45
+ * Overrides the accessible name. Used when no visible `label` is provided.
46
+ * Maps to the `aria-label` attribute.
47
+ */
48
+ ariaLabel?: string;
49
+ /** Additional CSS class names applied to the outermost wrapper. */
50
+ className?: string;
51
+ /** Override track background color when checked. Defaults to MD3 primary. */
52
+ checkedTrackColor?: string;
53
+ /** Override track background color when unchecked. Defaults to MD3 surface-container-highest. */
54
+ uncheckedTrackColor?: string;
55
+ /** Override thumb color when checked. Defaults to MD3 on-primary. */
56
+ checkedThumbColor?: string;
57
+ /** Override thumb color when unchecked. Defaults to MD3 outline. */
58
+ uncheckedThumbColor?: string;
59
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @file tabs/index.ts
3
+ * Public exports for the MD3 Expressive Tabs component system.
4
+ */
5
+ export { Tab } from "./tab";
6
+ export { Tabs } from "./tabs";
7
+ export { TabsColors, TabsTokens } from "./tabs.tokens";
8
+ export type { TabProps, TabsContentProps, TabsListProps, TabsProps, TabsVariant, } from "./tabs.types";
9
+ export { TabsContent } from "./tabs-content";
10
+ export { TabsList } from "./tabs-list";
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @file tab.tsx
3
+ * MD3 Expressive Tab — Individual tab button with Framer Motion indicator.
4
+ *
5
+ * Design decisions:
6
+ * 1. PRIMARY indicator nested inside content wrapper → width = content width (not full button).
7
+ * 2. SECONDARY indicator outside content wrapper → `inset-x-0` = full button width.
8
+ * 3. ROVING TABINDEX (WAI-ARIA): only focused tab has tabIndex=0; ArrowKey moves focus, Enter/Space selects.
9
+ * 4. DISABLED tabs are skipped in ArrowKey navigation.
10
+ * 5. RTL: ArrowLeft/Right directions are swapped when `direction: rtl` is detected.
11
+ * 6. INLINE ICON: icon beside label, height stays 48dp (stacked = 64dp).
12
+ * 7. AUTO-ACTIVATE: when parent `<Tabs autoActivate>`, ArrowKey also selects.
13
+ *
14
+ * @see https://m3.material.io/components/tabs/overview
15
+ * @see https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
16
+ */
17
+ import * as React from "react";
18
+ import type { TabProps } from "./tabs.types";
19
+ /**
20
+ * MD3 Expressive Tab component — individual tab button.
21
+ *
22
+ * Must be a direct child of `<TabsList>`. Implements WAI-ARIA Tabs pattern
23
+ * with roving tabindex keyboard navigation.
24
+ *
25
+ * - **Primary variant**: indicator width = content (text + icon) width.
26
+ * - **Secondary variant**: indicator width = full button hit area.
27
+ * - **Disabled**: Skipped entirely in ArrowKey navigation (cannot be focused).
28
+ * - **inlineIcon**: Icon beside (not above) label; height stays 48dp.
29
+ * - Framer Motion `layoutId` animates indicator with spring physics.
30
+ * - ArrowLeft/Right respect RTL direction automatically.
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * <Tab value="flights" icon={<Icon name="flight" />}>Flights</Tab>
35
+ * <Tab value="trips">Trips</Tab>
36
+ * <Tab value="explore" disabled>Explore</Tab>
37
+ * <Tab value="hotels" icon={<Icon name="hotel" />} inlineIcon>Hotels</Tab>
38
+ * ```
39
+ *
40
+ * @see https://m3.material.io/components/tabs/overview
41
+ * @see https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
42
+ */
43
+ export declare const Tab: React.NamedExoticComponent<TabProps & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @file tabs-content.tsx
3
+ * MD3 Expressive TabsContent — Animated panel component.
4
+ *
5
+ * Implements WAI-ARIA tabpanel role with:
6
+ * - AnimatePresence for fade transition on tab switch
7
+ * - Proper aria-labelledby pointing to the associated <Tab>
8
+ * - tabIndex=0 so keyboard users can Tab from the tablist into the panel
9
+ * - Hidden panels are removed from the DOM (not just visually hidden)
10
+ * to prevent screen readers from reading inactive content
11
+ */
12
+ import * as React from "react";
13
+ import type { TabsContentProps } from "./tabs.types";
14
+ /**
15
+ * MD3 Expressive TabsContent panel component.
16
+ *
17
+ * Each panel corresponds to a `<Tab>` with the same `value`.
18
+ * Only the active panel is rendered in the DOM — inactive panels
19
+ * are fully unmounted (not `display: none`) to prevent screen readers
20
+ * from reading hidden content.
21
+ *
22
+ * Fade animation is applied on both enter and exit via Framer Motion
23
+ * `AnimatePresence`. We use `mode="popLayout"` to prevent height layout shifting
24
+ * during tab transitions. Animation is automatically disabled when the user
25
+ * has enabled `prefers-reduced-motion`.
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * <TabsContent value="flights">
30
+ * <p>Available flights...</p>
31
+ * </TabsContent>
32
+ * ```
33
+ *
34
+ * @see https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
35
+ */
36
+ export declare const TabsContent: React.NamedExoticComponent<TabsContentProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @file tabs-list.tsx
3
+ * MD3 Expressive TabsList — Container component for tab buttons.
4
+ *
5
+ * Responsibilities:
6
+ * - Applies variant (primary/secondary) layout and styling
7
+ * - Manages horizontal scroll for scrollable mode (52px edge padding per MD3)
8
+ * - Renders the bottom divider for secondary variant
9
+ * - Scopes Framer Motion LayoutGroup so indicators animate correctly
10
+ * when multiple <Tabs> instances are on the same page
11
+ * - Restores focus to activeTab when keyboard focus leaves the tablist
12
+ * (matches Google's `focusout` handler on <md-tabs>)
13
+ */
14
+ import * as React from "react";
15
+ import type { TabsListProps } from "./tabs.types";
16
+ /**
17
+ * MD3 Expressive TabsList container component.
18
+ *
19
+ * Renders a horizontal row of `<Tab>` components with MD3-compliant
20
+ * layout (fixed or scrollable) and variant styling (primary or secondary).
21
+ *
22
+ * - **Primary**: Tabs divide available width equally, indicator width = content width.
23
+ * - **Secondary**: Tabs divide equally + full-width indicator + bottom divider line.
24
+ * - **Scrollable**: Tabs have min-width (90px), scroll horizontally with 52px edge padding.
25
+ * - **Focusout**: When focus leaves the tablist, roving focus resets to the active tab.
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * <TabsList variant="primary" scrollable={false}>
30
+ * <Tab value="tab1">Tab 1</Tab>
31
+ * <Tab value="tab2">Tab 2</Tab>
32
+ * </TabsList>
33
+ *
34
+ * <TabsList variant="secondary" scrollable={true} aria-label="Content sections">
35
+ * <Tab value="a">Alpha</Tab>
36
+ * <Tab value="b">Beta</Tab>
37
+ * </TabsList>
38
+ * ```
39
+ */
40
+ export declare const TabsList: React.NamedExoticComponent<TabsListProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @file tabs.tsx
3
+ * MD3 Expressive Tabs — Root context provider and state manager.
4
+ * Implements compound component pattern (similar to Radix UI).
5
+ * Spec: https://m3.material.io/components/tabs/overview
6
+ */
7
+ import * as React from "react";
8
+ import type { TabsContextValue, TabsProps } from "./tabs.types";
9
+ /**
10
+ * Hook to consume the Tabs context.
11
+ * Throws if used outside a `<Tabs>` root.
12
+ * @internal
13
+ */
14
+ export declare function useTabsContext(): TabsContextValue;
15
+ /**
16
+ * MD3 Expressive Tabs root component.
17
+ *
18
+ * Manages tab selection state and provides context to all
19
+ * compound sub-components. Supports both controlled and
20
+ * uncontrolled usage.
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * // Uncontrolled
25
+ * <Tabs defaultValue="flights">
26
+ * <TabsList variant="primary">
27
+ * <Tab value="flights">Flights</Tab>
28
+ * <Tab value="trips">Trips</Tab>
29
+ * </TabsList>
30
+ * <TabsContent value="flights">Flight content</TabsContent>
31
+ * <TabsContent value="trips">Trip content</TabsContent>
32
+ * </Tabs>
33
+ *
34
+ * // Controlled
35
+ * const [tab, setTab] = useState("flights");
36
+ * <Tabs value={tab} onValueChange={setTab}>...</Tabs>
37
+ *
38
+ * // Auto-activate mode (focus = select)
39
+ * <Tabs defaultValue="flights" autoActivate>...</Tabs>
40
+ * ```
41
+ *
42
+ * @see https://m3.material.io/components/tabs/overview
43
+ */
44
+ export declare const Tabs: React.NamedExoticComponent<TabsProps & React.RefAttributes<HTMLDivElement>>;
45
+ /**
46
+ * Secondary context carrying variant + scrollable from <TabsList>.
47
+ * Separate from TabsContext so Tabs root doesn't need these props —
48
+ * they belong to the list, not the root.
49
+ * @internal
50
+ */
51
+ export interface TabsListContextValue {
52
+ variant: "primary" | "secondary";
53
+ scrollable: boolean;
54
+ }
55
+ export declare const TabsListContext: React.Context<TabsListContextValue | null>;
56
+ /**
57
+ * Hook to consume TabsList-level context (variant, scrollable).
58
+ * @internal
59
+ */
60
+ export declare function useTabsListContext(): TabsListContextValue;