@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,94 @@
1
+ /**
2
+ * @file tabs.tokens.ts
3
+ * MD3 Expressive Tabs — Design tokens ported from:
4
+ * - PrimaryNavigationTabTokens.kt (v0_162)
5
+ * - SecondaryNavigationTabTokens.kt (v0_162)
6
+ *
7
+ * All dimensional values are in px (dp equivalent for web).
8
+ * @see docs/m3/tabs/PrimaryNavigationTabTokens.kt
9
+ * @see docs/m3/tabs/SecondaryNavigationTabTokens.kt
10
+ */
11
+ /**
12
+ * Dimensional design tokens for the MD3 Tabs component.
13
+ *
14
+ * Maps directly from the `.kt` token files to CSS/JS values.
15
+ * Use as the single source of truth for sizing.
16
+ */
17
+ export declare const TabsTokens: {
18
+ /** ContainerHeight = 48dp (text-only tab) */
19
+ readonly containerHeight: 48;
20
+ /** IconAndLabelTextContainerHeight = 64dp (tab with icon + label stacked) */
21
+ readonly containerHeightWithIcon: 64;
22
+ /** ActiveIndicatorHeight (Primary) = 3dp */
23
+ readonly primaryIndicatorHeight: 3;
24
+ /** ActiveIndicatorHeight (Secondary) = 2dp */
25
+ readonly secondaryIndicatorHeight: 2;
26
+ /**
27
+ * ActiveIndicatorShape = 3dp top-left and top-right (per MD3 token, not a full pill).
28
+ * Google reference: `var(--_active-indicator-shape)` resolves to `3px 3px 0 0` effectively.
29
+ */
30
+ readonly indicatorBorderRadius: "3px 3px 0 0";
31
+ /** IconSize = 24dp */
32
+ readonly iconSize: 24;
33
+ /**
34
+ * Edge start/end padding for scrollable mode = 52px.
35
+ * Per MD3 spec: tabs have padding on both leading and trailing edges.
36
+ */
37
+ readonly scrollableEdgePadding: 52;
38
+ /** Minimum tab width in scrollable mode = 90px. */
39
+ readonly scrollableMinTabWidth: 90;
40
+ /** DividerHeight = 1dp */
41
+ readonly dividerHeight: 1;
42
+ /**
43
+ * Focus ring border-radius = 8px.
44
+ * Google reference: `focus-ring.theme({ shape: 8px })` in _tab.scss.
45
+ */
46
+ readonly focusRingBorderRadius: 8;
47
+ };
48
+ /**
49
+ * CSS custom property references for Tabs colors.
50
+ * Maps to `--md-sys-color-*` tokens in the MD3 theme system.
51
+ *
52
+ * DO NOT hardcode hex values — use these references for automatic
53
+ * light/dark theme adaptation.
54
+ */
55
+ export declare const TabsColors: {
56
+ /** Primary: ActiveLabelTextColor / ActiveIconColor = Primary */
57
+ readonly primaryActiveText: "var(--md-sys-color-primary)";
58
+ /** Primary: InactiveLabelTextColor / InactiveIconColor = OnSurfaceVariant */
59
+ readonly primaryInactiveText: "var(--md-sys-color-on-surface-variant)";
60
+ /** Primary: ActiveIndicatorColor = Primary */
61
+ readonly primaryIndicator: "var(--md-sys-color-primary)";
62
+ /** Secondary: ActiveLabelTextColor / ActiveIconColor = OnSurface */
63
+ readonly secondaryActiveText: "var(--md-sys-color-on-surface)";
64
+ /** Secondary: InactiveLabelTextColor / InactiveIconColor = OnSurfaceVariant */
65
+ readonly secondaryInactiveText: "var(--md-sys-color-on-surface-variant)";
66
+ /** Secondary: Indicator color = Primary (same as primary variant) */
67
+ readonly secondaryIndicator: "var(--md-sys-color-primary)";
68
+ /** Secondary: DividerColor = SurfaceVariant */
69
+ readonly divider: "var(--md-sys-color-surface-variant)";
70
+ /** ContainerColor = Surface */
71
+ readonly container: "var(--md-sys-color-surface)";
72
+ /** Focus ring indicator = Secondary */
73
+ readonly focusIndicator: "var(--md-sys-color-secondary)";
74
+ /** Hover state layer (primary active) */
75
+ readonly primaryActiveHover: "var(--md-sys-color-primary)";
76
+ /** Hover state layer (inactive, both variants) */
77
+ readonly inactiveHover: "var(--md-sys-color-on-surface)";
78
+ };
79
+ /** Spring transition for the sliding indicator (FastSpatial equivalent). */
80
+ export declare const TABS_INDICATOR_SPRING: {
81
+ readonly type: "spring";
82
+ readonly stiffness: 500;
83
+ readonly damping: 40;
84
+ };
85
+ /** Color transition for label/icon color animate (active ↔ inactive). */
86
+ export declare const TABS_COLOR_TRANSITION: {
87
+ readonly duration: 0.2;
88
+ readonly ease: "easeInOut";
89
+ };
90
+ /** Content fade transition when switching tabs. */
91
+ export declare const TABS_CONTENT_TRANSITION: {
92
+ readonly duration: 0.15;
93
+ readonly ease: "easeInOut";
94
+ };
@@ -0,0 +1,172 @@
1
+ /**
2
+ * @file tabs.types.ts
3
+ * MD3 Expressive Tabs — TypeScript prop definitions.
4
+ * Spec: https://m3.material.io/components/tabs/overview
5
+ */
6
+ import type * as React from "react";
7
+ /** Visual variant: primary (content-width indicator) or secondary (full-width indicator + divider). */
8
+ export type TabsVariant = "primary" | "secondary";
9
+ /**
10
+ * Internal context shared across all compound components.
11
+ * @internal
12
+ */
13
+ export interface TabsContextValue {
14
+ /** Currently selected tab value. */
15
+ value: string;
16
+ /** Callback to change the selected tab. */
17
+ onValueChange: (value: string) => void;
18
+ /** Currently keyboard-focused tab value (for roving tabindex). */
19
+ focusedValue: string;
20
+ /** Sets the focused tab value (keyboard nav only — does NOT select). */
21
+ setFocusedValue: (value: string) => void;
22
+ /** Ordered list of all registered tab values (for ArrowKey nav). */
23
+ tabValues: string[];
24
+ /** Register a tab value when a <Tab> mounts. */
25
+ registerTab: (value: string) => void;
26
+ /** Unregister a tab value when a <Tab> unmounts. */
27
+ unregisterTab: (value: string) => void;
28
+ /** Unique layout group ID scoped to this Tabs instance. */
29
+ layoutGroupId: string;
30
+ /**
31
+ * Set of currently disabled tab values.
32
+ * Used by keyboard navigation to skip disabled tabs.
33
+ */
34
+ disabledValues: Set<string>;
35
+ /**
36
+ * Mark or unmark a tab value as disabled.
37
+ * Called by <Tab> on mount and when `disabled` prop changes.
38
+ */
39
+ markTabDisabled: (value: string, disabled: boolean) => void;
40
+ /**
41
+ * When true, focus moving via ArrowKey also selects the tab immediately.
42
+ * Mirrors Google's `autoActivate` attribute on <md-tabs>.
43
+ * @default false
44
+ */
45
+ autoActivate: boolean;
46
+ }
47
+ /**
48
+ * Props for the `<Tabs>` root component.
49
+ *
50
+ * Supports both controlled (`value` + `onValueChange`) and
51
+ * uncontrolled (`defaultValue`) usage patterns.
52
+ *
53
+ * @example
54
+ * ```tsx
55
+ * // Controlled
56
+ * <Tabs value={tab} onValueChange={setTab}>...</Tabs>
57
+ *
58
+ * // Uncontrolled
59
+ * <Tabs defaultValue="flights">...</Tabs>
60
+ *
61
+ * // Auto-activate (focus = select)
62
+ * <Tabs defaultValue="flights" autoActivate>...</Tabs>
63
+ * ```
64
+ */
65
+ export interface TabsProps {
66
+ /** Controlled selected value. Use with `onValueChange`. */
67
+ value?: string;
68
+ /** Initial value for uncontrolled usage. */
69
+ defaultValue?: string;
70
+ /** Called when the selected tab changes. */
71
+ onValueChange?: (value: string) => void;
72
+ /**
73
+ * When true, ArrowKey navigation also selects the focused tab immediately.
74
+ * Mirrors Google's `auto-activate` attribute on `<md-tabs>`.
75
+ * @default false
76
+ */
77
+ autoActivate?: boolean;
78
+ /** Tab compound components as children. */
79
+ children: React.ReactNode;
80
+ /** Additional CSS class names for the root wrapper. */
81
+ className?: string;
82
+ }
83
+ /**
84
+ * Props for the `<TabsList>` container component.
85
+ *
86
+ * @example
87
+ * ```tsx
88
+ * <TabsList variant="primary" scrollable={false}>
89
+ * <Tab value="tab1">Tab 1</Tab>
90
+ * </TabsList>
91
+ * ```
92
+ */
93
+ export interface TabsListProps {
94
+ /** Visual style variant. @required */
95
+ variant: TabsVariant;
96
+ /**
97
+ * When true, tabs scroll horizontally with 52px edge padding (MD3 spec).
98
+ * When false, tabs divide the available width equally (flex-1).
99
+ * @default false
100
+ */
101
+ scrollable?: boolean;
102
+ /**
103
+ * Background color override for the tab bar.
104
+ * @default "var(--md-sys-color-surface)"
105
+ */
106
+ backgroundColor?: string;
107
+ /** Tab components as children. */
108
+ children: React.ReactNode;
109
+ /** Additional CSS class names. */
110
+ className?: string;
111
+ /** Forwarded aria-label for the tablist. */
112
+ "aria-label"?: string;
113
+ }
114
+ /**
115
+ * Props for an individual `<Tab>` component.
116
+ *
117
+ * @example
118
+ * ```tsx
119
+ * <Tab value="flights" icon={<Icon name="flight" />}>Flights</Tab>
120
+ * <Tab value="trips" disabled>Trips</Tab>
121
+ * <Tab value="hotels" icon={<Icon name="hotel" />} inlineIcon>Hotels</Tab>
122
+ * ```
123
+ */
124
+ export interface TabProps {
125
+ /** Unique value identifying this tab. Must match a `<TabsContent value>`. */
126
+ value: string;
127
+ /**
128
+ * Optional icon rendered with the label text.
129
+ * - Default (stacked): icon above label, height increases to 64dp.
130
+ * - With `inlineIcon`: icon beside label (same row), height stays 48dp.
131
+ */
132
+ icon?: React.ReactNode;
133
+ /**
134
+ * When true, icon is placed inline (same row) with the label text.
135
+ * Container height stays at 48dp (does NOT increase to 64dp).
136
+ * Mirrors the `inline-icon` attribute on `<md-primary-tab>`.
137
+ * @default false
138
+ */
139
+ inlineIcon?: boolean;
140
+ /**
141
+ * When true, disables interaction.
142
+ * Disabled tabs are skipped entirely in keyboard navigation (ArrowKey).
143
+ */
144
+ disabled?: boolean;
145
+ /** Additional CSS class names. */
146
+ className?: string;
147
+ /**
148
+ * Optional badge element overlaid on the tab content.
149
+ * Handled via `BadgedBox`:
150
+ * - Stacked icon: Overlaps icon's top-trailing corner.
151
+ * - Inline/Text-only: Placed next to the text.
152
+ */
153
+ badge?: React.ReactNode;
154
+ /** Label text rendered inside the tab. */
155
+ children: React.ReactNode;
156
+ }
157
+ /**
158
+ * Props for the `<TabsContent>` panel component.
159
+ *
160
+ * @example
161
+ * ```tsx
162
+ * <TabsContent value="flights">Flight content here</TabsContent>
163
+ * ```
164
+ */
165
+ export interface TabsContentProps {
166
+ /** Must match the `value` of a sibling `<Tab>`. */
167
+ value: string;
168
+ /** Additional CSS class names. */
169
+ className?: string;
170
+ /** Panel content. */
171
+ children: React.ReactNode;
172
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @file index.ts
3
+ * Barrel export for the TextField component.
4
+ *
5
+ * @example
6
+ * ```tsx
7
+ * import { TextField } from '@bug-on/md3-react';
8
+ * ```
9
+ */
10
+ export { TextField } from "./text-field";
11
+ export type { TextFieldHandle, TextFieldInputType, TextFieldProps, TextFieldTrailingIconMode, TextFieldVariant, } from "./text-field.types";
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @file active-indicator.tsx
3
+ * Animated bottom border line for the MD3 Filled TextField.
4
+ * Expands height from 1px → 2px and color changes on focus.
5
+ */
6
+ import * as React from "react";
7
+ export interface ActiveIndicatorProps {
8
+ isFocused: boolean;
9
+ isError: boolean;
10
+ isDisabled: boolean;
11
+ isHovered: boolean;
12
+ prefersReduced: boolean;
13
+ }
14
+ /**
15
+ * MD3 Active Indicator — the bottom border line for Filled TextField.
16
+ *
17
+ * Animates:
18
+ * - `height`: 1px (enabled) → 2px (focused)
19
+ * - `backgroundColor`: on-surface-variant → primary (focused) → error
20
+ * - `scaleX`: 0 → 1 expanding from center on focus in
21
+ *
22
+ * @see https://m3.material.io/components/text-fields/specs#filled-text-field
23
+ */
24
+ export declare const ActiveIndicator: React.NamedExoticComponent<ActiveIndicatorProps>;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @file floating-label.tsx
3
+ * Animated floating label for MD3 TextField.
4
+ * Animates between inline (body large) and floated (body small) positions.
5
+ */
6
+ import * as React from "react";
7
+ export interface FloatingLabelProps {
8
+ /** The label text content. */
9
+ text: string;
10
+ /** Whether the label is in the floated (small) position. */
11
+ isFloated: boolean;
12
+ /** Whether the field is currently focused. */
13
+ isFocused: boolean;
14
+ /** Whether the field is in error state. */
15
+ isError: boolean;
16
+ /** Whether the field is disabled. */
17
+ isDisabled: boolean;
18
+ /** 'filled' or 'outlined' — determines vertical y offset. */
19
+ variant: "filled" | "outlined";
20
+ /** Container height in px (56 normal, 48 dense). */
21
+ containerHeight: number;
22
+ /** Whether to skip animations (prefers-reduced-motion). */
23
+ prefersReduced: boolean;
24
+ /** Whether the required asterisk should be shown. */
25
+ showAsterisk: boolean;
26
+ /** ID of the label element, for associating with a containing element. */
27
+ htmlFor?: string;
28
+ /** Ref callback so the parent can measure label width for the outlined notch. */
29
+ labelRef?: React.Ref<HTMLSpanElement>;
30
+ /** Whether there is a leading icon. */
31
+ hasLeading?: boolean;
32
+ }
33
+ /**
34
+ * MD3 Expressive Floating Label.
35
+ *
36
+ * Animates y-position, scale, and color when the label floats.
37
+ * Uses `transformOrigin: 'left center'` so scaling anchors at the start.
38
+ *
39
+ * @accessibility
40
+ * Rendered as a `<label>` with `htmlFor` linking to the `<input>`.
41
+ * When floated, visual size changes but the semantic label is unchanged.
42
+ */
43
+ export declare const FloatingLabel: React.NamedExoticComponent<FloatingLabelProps>;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @file leading-icon.tsx
3
+ * Leading icon slot for MD3 TextField.
4
+ */
5
+ import * as React from "react";
6
+ export interface LeadingIconProps {
7
+ /** Icon node — should be 24×24px. */
8
+ children: React.ReactNode;
9
+ /** Whether the field is in error state (changes icon color). */
10
+ isError: boolean;
11
+ /** Whether the field is disabled. */
12
+ isDisabled: boolean;
13
+ }
14
+ /**
15
+ * MD3 Leading Icon wrapper.
16
+ *
17
+ * Decorative — `aria-hidden="true"`.
18
+ * Color: `on-surface-variant` (default), `error` (error state).
19
+ * Size: 24×24px icon, 48×56px touch target via flex alignment.
20
+ *
21
+ * @see https://m3.material.io/components/text-fields/specs#anatomy
22
+ */
23
+ export declare const LeadingIcon: React.NamedExoticComponent<LeadingIconProps>;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @file outline-container.tsx
3
+ * MD3-compliant outlined border with animated notch for the Outlined TextField.
4
+ *
5
+ * Implementation: 3-segment approach inspired by Material Web's fieldset/legend pattern.
6
+ * The top border is split into: [left-segment] [notch-gap] [right-segment].
7
+ * The notch-gap width animates from 0 → (labelWidth × scaleRatio + 8px) when label floats.
8
+ *
9
+ * This mirrors Material Web's implementation without requiring <fieldset> semantics.
10
+ *
11
+ * @see https://github.com/material-components/material-web/tree/main/textfield
12
+ * @see https://m3.material.io/components/text-fields/specs#outlined-text-field
13
+ */
14
+ import * as React from "react";
15
+ export interface OutlineContainerProps {
16
+ /** Whether the label is in the floated position. */
17
+ isFloated: boolean;
18
+ /** Whether the field is focused. */
19
+ isFocused: boolean;
20
+ /** Whether the field is in error state. */
21
+ isError: boolean;
22
+ /** Whether the field is disabled. */
23
+ isDisabled: boolean;
24
+ /** Whether the field is hovered. */
25
+ isHovered: boolean;
26
+ /**
27
+ * Measured width of the label element in its full (unfloated) size.
28
+ * The notch width = labelWidth × scaleRatio + 2×notchPadding.
29
+ */
30
+ labelWidth: number;
31
+ /** Whether to disable animations. */
32
+ prefersReduced: boolean;
33
+ }
34
+ /**
35
+ * MD3 Outlined TextField container with animated notch.
36
+ *
37
+ * The notch gap expands/collapses in sync with the FloatingLabel animation,
38
+ * creating the visual effect of the label breaking through the border.
39
+ *
40
+ * Accessibility: `aria-hidden="true"` — purely decorative border.
41
+ */
42
+ export declare const OutlineContainer: React.NamedExoticComponent<OutlineContainerProps>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @file prefix-suffix.tsx
3
+ * Prefix and suffix text for MD3 TextField.
4
+ * Visible only when the label is floated (or when there is no label).
5
+ */
6
+ import * as React from "react";
7
+ export interface PrefixSuffixProps {
8
+ text: string;
9
+ type: "prefix" | "suffix";
10
+ /** Whether the label is floated (controls visibility). */
11
+ visible: boolean;
12
+ /** Disable animations. */
13
+ prefersReduced: boolean;
14
+ }
15
+ /**
16
+ * MD3 Prefix / Suffix Text.
17
+ *
18
+ * Animates in/out in sync with the floating label using AnimatePresence.
19
+ * Hidden when label is in the inline position (would overlap the label).
20
+ *
21
+ * @accessibility
22
+ * `aria-hidden="true"` — decorative. Screen readers read the full value in context.
23
+ */
24
+ export declare const PrefixSuffix: React.NamedExoticComponent<PrefixSuffixProps>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @file supporting-text.tsx
3
+ * Supporting text, error text, and character counter for MD3 TextField.
4
+ *
5
+ * Animates in/out using AnimatePresence. Uses aria-live for accessibility.
6
+ */
7
+ import * as React from "react";
8
+ export interface SupportingTextProps {
9
+ /** Helper text shown in normal state. */
10
+ supportingText?: string;
11
+ /** Error message — shown instead of supportingText when isError=true. */
12
+ errorText?: string;
13
+ /** Whether field is in error state. */
14
+ isError: boolean;
15
+ /** Current character count (value.length). */
16
+ charCount?: number;
17
+ /** Maximum character limit. Counter shown only when maxLength is set. */
18
+ maxLength?: number;
19
+ /** ID for aria-describedby linking from the input. */
20
+ id: string;
21
+ /** Disable animations. */
22
+ prefersReduced: boolean;
23
+ }
24
+ /**
25
+ * MD3 Supporting Text area.
26
+ *
27
+ * Layout: [helper/error text] [character counter]
28
+ * - Error text replaces supporting text when isError=true.
29
+ * - Character counter shows only when maxLength is provided.
30
+ * - Both use aria-live="polite" to announce changes to screen readers.
31
+ * - Animates in/out with opacity + y-offset via AnimatePresence.
32
+ *
33
+ * @accessibility
34
+ * - Error text: `aria-live="polite"` — screen readers announce when error appears.
35
+ * - Counter: `aria-live="polite"` — announces count changes.
36
+ */
37
+ export declare const SupportingText: React.NamedExoticComponent<SupportingTextProps>;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @file trailing-icon.tsx
3
+ * Trailing icon slot for MD3 TextField.
4
+ *
5
+ * Supports three built-in modes:
6
+ * - 'clear': ✕ button, visible when field has value
7
+ * - 'password-toggle': eye icon, toggles password visibility
8
+ * - 'custom': renders the `children` prop
9
+ */
10
+ import * as React from "react";
11
+ import type { TextFieldTrailingIconMode } from "../text-field.types";
12
+ export interface TrailingIconProps {
13
+ mode: TextFieldTrailingIconMode;
14
+ /** Custom icon content (used when mode='custom'). */
15
+ children?: React.ReactNode;
16
+ /** Current input value — used to determine if clear button is visible. */
17
+ value: string;
18
+ /** Whether password is currently visible (for password-toggle mode). */
19
+ showPassword?: boolean;
20
+ /** Fires when clear button is clicked. */
21
+ onClear?: () => void;
22
+ /** Fires when password visibility toggle is clicked. */
23
+ onPasswordToggle?: () => void;
24
+ /** Whether the field is in error state. */
25
+ isError: boolean;
26
+ /** Whether the field is disabled. */
27
+ isDisabled: boolean;
28
+ /** Disable animations. */
29
+ prefersReduced: boolean;
30
+ }
31
+ /**
32
+ * MD3 Trailing Icon.
33
+ *
34
+ * Touch target: 48×48px (padding extends the hit area beyond the 24×24 icon).
35
+ *
36
+ * @accessibility
37
+ * - Clear button: `aria-label="Clear input"`
38
+ * - Password toggle: `aria-label="Show password"` / `"Hide password"`
39
+ * - Custom: no aria — consumer provides accessible markup
40
+ */
41
+ export declare const TrailingIcon: React.NamedExoticComponent<TrailingIconProps>;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @file text-field.tsx
3
+ * MD3 Expressive TextField — Filled & Outlined variants.
4
+ *
5
+ * Features:
6
+ * - Floating label animation (Framer Motion)
7
+ * - Filled (active indicator) and Outlined (notch border) variants
8
+ * - Controlled & uncontrolled value handling
9
+ * - Built-in clear button, password toggle
10
+ * - Prefix / suffix text
11
+ * - Supporting text, error text, character counter
12
+ * - Leading & trailing icon slots
13
+ * - Textarea support
14
+ * - Native form validation + imperative handle
15
+ * - Full WCAG AA accessibility
16
+ * - prefers-reduced-motion support
17
+ *
18
+ * @see https://m3.material.io/components/text-fields/overview
19
+ */
20
+ import * as React from "react";
21
+ import type { TextFieldHandle, TextFieldProps } from "./text-field.types";
22
+ /**
23
+ * MD3 Expressive TextField.
24
+ *
25
+ * Supports `filled` and `outlined` variants, floating label animation,
26
+ * prefix/suffix, leading/trailing icons, supporting text, character counter,
27
+ * textarea mode, clear button, password toggle, and native form validation.
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * // Filled (default)
32
+ * <TextField label="Email" type="email" />
33
+ *
34
+ * // Outlined with error
35
+ * <TextField variant="outlined" label="Username" error errorText="Already taken" />
36
+ *
37
+ * // With clear button
38
+ * <TextField label="Search" trailingIconMode="clear" />
39
+ *
40
+ * // Password with toggle
41
+ * <TextField label="Password" type="password" trailingIconMode="password-toggle" />
42
+ *
43
+ * // With character counter
44
+ * <TextField label="Bio" maxLength={140} supportingText="Describe yourself" />
45
+ * ```
46
+ *
47
+ * @see https://m3.material.io/components/text-fields/overview
48
+ */
49
+ export declare const TextField: React.NamedExoticComponent<Omit<TextFieldProps, "ref"> & React.RefAttributes<TextFieldHandle>>;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @file text-field.tokens.ts
3
+ * MD3 design token mapping for the TextField component.
4
+ *
5
+ * All color values reference CSS custom properties defined by the MD3 theme.
6
+ * Do NOT use raw hex values — always use CSS variables.
7
+ *
8
+ * @see https://m3.material.io/components/text-fields/specs
9
+ */
10
+ export declare const TF_COLORS: {
11
+ /** Filled container background */
12
+ readonly filledBg: "var(--color-m3-surface-container-highest)";
13
+ /** Input text color */
14
+ readonly inputText: "var(--color-m3-on-surface)";
15
+ /** Label (unfloated) + icons + prefix/suffix + supporting text */
16
+ readonly onSurfaceVariant: "var(--color-m3-on-surface-variant)";
17
+ /** Focused active indicator / outline, floated label */
18
+ readonly primary: "var(--color-m3-primary)";
19
+ /** Error indicator / outline / label / icon / supporting text */
20
+ readonly error: "var(--color-m3-error)";
21
+ /** Outlined border (enabled) */
22
+ readonly outline: "var(--color-m3-outline)";
23
+ /** Transparent */
24
+ readonly transparent: "transparent";
25
+ };
26
+ export declare const TF_SIZE: {
27
+ /** Container height — normal */
28
+ readonly height: 56;
29
+ /** Container height — dense variant */
30
+ readonly denseHeight: 48;
31
+ /** Active indicator height — enabled */
32
+ readonly indicatorThin: 1;
33
+ /** Active indicator height — focused */
34
+ readonly indicatorThick: 2;
35
+ /** Outline stroke width — enabled */
36
+ readonly outlineThin: 1;
37
+ /** Outline stroke width — focused */
38
+ readonly outlineThick: 2;
39
+ /** Corner radius — all variants */
40
+ readonly cornerRadius: 4;
41
+ /** Leading/Trailing icon size */
42
+ readonly iconSize: 24;
43
+ /** Padding inline start (no leading icon) */
44
+ readonly paddingStart: 16;
45
+ /** Padding inline start (with leading icon) */
46
+ readonly paddingStartWithIcon: 12;
47
+ /** Padding inline end */
48
+ readonly paddingEnd: 16;
49
+ /** Padding inline end (with trailing icon) */
50
+ readonly paddingEndWithIcon: 12;
51
+ /** Notch extra padding per side on outlined label gap */
52
+ readonly notchPadding: 4;
53
+ };
54
+ export declare const TF_TYPOGRAPHY: {
55
+ /** Body Large — input text, unfloated label */
56
+ readonly bodyLargePx: 16;
57
+ /** Body Small — floated label, supporting text, counter */
58
+ readonly bodySmallPx: 12;
59
+ /** Scale ratio when label floats: 12/16 = 0.75 */
60
+ readonly labelScaleRatio: number;
61
+ };
62
+ /**
63
+ * Tailwind utility classes for key TextField elements.
64
+ * Use via cn() — do NOT use these as standalone strings without merging.
65
+ */
66
+ export declare const TF_CLASSES: {
67
+ readonly filledContainer: "bg-[var(--color-m3-surface-container-highest)] rounded-tl-[4px] rounded-tr-[4px] rounded-bl-none rounded-br-none";
68
+ readonly outlinedContainer: "bg-transparent rounded-[4px]";
69
+ readonly input: "bg-transparent outline-none w-full text-base text-[var(--color-m3-on-surface)] caret-[var(--color-m3-primary)] placeholder:text-[var(--color-m3-on-surface-variant)]";
70
+ readonly inputNoSpinner: "[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none";
71
+ readonly disabled: "opacity-[0.38] pointer-events-none cursor-not-allowed";
72
+ readonly supportingText: "text-xs text-[var(--color-m3-on-surface-variant)] px-4";
73
+ readonly errorText: "text-xs text-[var(--color-m3-error)] px-4";
74
+ readonly prefixSuffix: "text-base text-[var(--color-m3-on-surface-variant)] select-none shrink-0";
75
+ readonly stateLayer: "absolute inset-0 bg-[var(--color-m3-on-surface)] opacity-0 transition-opacity duration-150 pointer-events-none rounded-[inherit]";
76
+ };