@m3-baseui/react-tailwind 4.0.0 → 5.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.
@@ -1,7 +1,7 @@
1
1
  import * as _base_ui_react from '@base-ui/react';
2
2
  import * as react from 'react';
3
3
  import * as _m3_baseui_core from '@m3-baseui/core';
4
- export { IconButtonProps, IconButtonVariant } from '@m3-baseui/core';
4
+ export { IconButtonProps, IconButtonShape, IconButtonSize, IconButtonVariant, IconButtonWidth } from '@m3-baseui/core';
5
5
  import * as tailwind_variants from 'tailwind-variants';
6
6
  import * as tailwind_variants_dist_config_js from 'tailwind-variants/dist/config.js';
7
7
 
@@ -12,9 +12,9 @@ declare const iconButton: tailwind_variants.TVReturnType<{
12
12
  tonal: string[];
13
13
  outlined: string[];
14
14
  };
15
- selected: {
16
- true: string;
17
- false: string;
15
+ toggle: {
16
+ on: string;
17
+ off: string;
18
18
  };
19
19
  size: {
20
20
  xs: string;
@@ -28,6 +28,10 @@ declare const iconButton: tailwind_variants.TVReturnType<{
28
28
  default: string;
29
29
  wide: string;
30
30
  };
31
+ shape: {
32
+ round: string;
33
+ square: string;
34
+ };
31
35
  }, undefined, string[], tailwind_variants_dist_config_js.TVConfig<{
32
36
  variant: {
33
37
  standard: string[];
@@ -35,9 +39,9 @@ declare const iconButton: tailwind_variants.TVReturnType<{
35
39
  tonal: string[];
36
40
  outlined: string[];
37
41
  };
38
- selected: {
39
- true: string;
40
- false: string;
42
+ toggle: {
43
+ on: string;
44
+ off: string;
41
45
  };
42
46
  size: {
43
47
  xs: string;
@@ -51,6 +55,10 @@ declare const iconButton: tailwind_variants.TVReturnType<{
51
55
  default: string;
52
56
  wide: string;
53
57
  };
58
+ shape: {
59
+ round: string;
60
+ square: string;
61
+ };
54
62
  }, {
55
63
  variant: {
56
64
  standard: string[];
@@ -58,9 +66,9 @@ declare const iconButton: tailwind_variants.TVReturnType<{
58
66
  tonal: string[];
59
67
  outlined: string[];
60
68
  };
61
- selected: {
62
- true: string;
63
- false: string;
69
+ toggle: {
70
+ on: string;
71
+ off: string;
64
72
  };
65
73
  size: {
66
74
  xs: string;
@@ -74,6 +82,10 @@ declare const iconButton: tailwind_variants.TVReturnType<{
74
82
  default: string;
75
83
  wide: string;
76
84
  };
85
+ shape: {
86
+ round: string;
87
+ square: string;
88
+ };
77
89
  }>, {
78
90
  variant: {
79
91
  standard: string[];
@@ -81,9 +93,9 @@ declare const iconButton: tailwind_variants.TVReturnType<{
81
93
  tonal: string[];
82
94
  outlined: string[];
83
95
  };
84
- selected: {
85
- true: string;
86
- false: string;
96
+ toggle: {
97
+ on: string;
98
+ off: string;
87
99
  };
88
100
  size: {
89
101
  xs: string;
@@ -97,6 +109,10 @@ declare const iconButton: tailwind_variants.TVReturnType<{
97
109
  default: string;
98
110
  wide: string;
99
111
  };
112
+ shape: {
113
+ round: string;
114
+ square: string;
115
+ };
100
116
  }, undefined, tailwind_variants.TVReturnType<{
101
117
  variant: {
102
118
  standard: string[];
@@ -104,9 +120,9 @@ declare const iconButton: tailwind_variants.TVReturnType<{
104
120
  tonal: string[];
105
121
  outlined: string[];
106
122
  };
107
- selected: {
108
- true: string;
109
- false: string;
123
+ toggle: {
124
+ on: string;
125
+ off: string;
110
126
  };
111
127
  size: {
112
128
  xs: string;
@@ -120,6 +136,10 @@ declare const iconButton: tailwind_variants.TVReturnType<{
120
136
  default: string;
121
137
  wide: string;
122
138
  };
139
+ shape: {
140
+ round: string;
141
+ square: string;
142
+ };
123
143
  }, undefined, string[], tailwind_variants_dist_config_js.TVConfig<{
124
144
  variant: {
125
145
  standard: string[];
@@ -127,9 +147,9 @@ declare const iconButton: tailwind_variants.TVReturnType<{
127
147
  tonal: string[];
128
148
  outlined: string[];
129
149
  };
130
- selected: {
131
- true: string;
132
- false: string;
150
+ toggle: {
151
+ on: string;
152
+ off: string;
133
153
  };
134
154
  size: {
135
155
  xs: string;
@@ -143,6 +163,10 @@ declare const iconButton: tailwind_variants.TVReturnType<{
143
163
  default: string;
144
164
  wide: string;
145
165
  };
166
+ shape: {
167
+ round: string;
168
+ square: string;
169
+ };
146
170
  }, {
147
171
  variant: {
148
172
  standard: string[];
@@ -150,9 +174,9 @@ declare const iconButton: tailwind_variants.TVReturnType<{
150
174
  tonal: string[];
151
175
  outlined: string[];
152
176
  };
153
- selected: {
154
- true: string;
155
- false: string;
177
+ toggle: {
178
+ on: string;
179
+ off: string;
156
180
  };
157
181
  size: {
158
182
  xs: string;
@@ -166,6 +190,10 @@ declare const iconButton: tailwind_variants.TVReturnType<{
166
190
  default: string;
167
191
  wide: string;
168
192
  };
193
+ shape: {
194
+ round: string;
195
+ square: string;
196
+ };
169
197
  }>, unknown, unknown, undefined>>;
170
198
  declare const IconButton: react.ForwardRefExoticComponent<_m3_baseui_core.IconButtonOwnProps & Omit<react.ButtonHTMLAttributes<HTMLButtonElement>, "color"> & {
171
199
  render?: _base_ui_react.useRender.RenderProp;
@@ -1,6 +1,47 @@
1
1
  "use client";
2
- import { createIconButton } from '@m3-baseui/core';
3
- import { tv } from 'tailwind-variants';
2
+ import { createIconButton, toToggle } from '@m3-baseui/core';
3
+ import { tv as tv$1 } from 'tailwind-variants';
4
+
5
+ // src/components/icon-button/icon-button.ts
6
+ var TYPESCALE = [
7
+ "display-large",
8
+ "display-medium",
9
+ "display-small",
10
+ "headline-large",
11
+ "headline-medium",
12
+ "headline-small",
13
+ "title-large",
14
+ "title-medium",
15
+ "title-small",
16
+ "body-large",
17
+ "body-medium",
18
+ "body-small",
19
+ "label-large",
20
+ "label-medium",
21
+ "label-small"
22
+ ];
23
+ var SHAPE = [
24
+ "none",
25
+ "extra-small",
26
+ "small",
27
+ "medium",
28
+ "large",
29
+ "large-increased",
30
+ "extra-large",
31
+ "extra-large-increased",
32
+ "full"
33
+ ];
34
+ var tv = (options, config) => tv$1(options, {
35
+ ...config,
36
+ twMergeConfig: {
37
+ extend: {
38
+ classGroups: {
39
+ "font-size": [{ text: [...TYPESCALE] }],
40
+ rounded: [{ rounded: [...SHAPE] }]
41
+ }
42
+ }
43
+ }
44
+ });
4
45
 
5
46
  // src/components/icon-button/icon-button.ts
6
47
  var WIDTHS = {
@@ -22,6 +63,35 @@ var widthCompounds = Object.entries(WIDTHS).flatMap(
22
63
  class: klass
23
64
  }))
24
65
  );
66
+ var SQUARE_CORNER = {
67
+ xs: "rounded-medium",
68
+ // 12dp
69
+ s: "rounded-medium",
70
+ // 12dp
71
+ m: "rounded-large",
72
+ // 16dp
73
+ l: "rounded-extra-large",
74
+ // 28dp
75
+ xl: "rounded-extra-large"
76
+ // 28dp
77
+ };
78
+ var squareShapeCompounds = Object.keys(SQUARE_CORNER).map(
79
+ (size) => ({ shape: "square", size, class: SQUARE_CORNER[size] })
80
+ );
81
+ var selectedShapeCompounds = [
82
+ ...Object.keys(SQUARE_CORNER).map((size) => ({
83
+ shape: "round",
84
+ toggle: "on",
85
+ size,
86
+ class: SQUARE_CORNER[size]
87
+ })),
88
+ ...Object.keys(SQUARE_CORNER).map((size) => ({
89
+ shape: "square",
90
+ toggle: "on",
91
+ size,
92
+ class: "rounded-full"
93
+ }))
94
+ ];
25
95
  var iconButton = tv({
26
96
  base: [
27
97
  "relative inline-flex items-center justify-center shrink-0",
@@ -29,7 +99,9 @@ var iconButton = tv({
29
99
  // The state layer is already rounded (before:rounded-[inherit]); the ripple
30
100
  // self-clips.
31
101
  "rounded-full cursor-pointer select-none border-0 bg-transparent",
32
- "transition-[box-shadow,background-color,color] duration-200 ease-standard",
102
+ // Motion: Compose uses DefaultEffects (critically-damped spring, no bounce)
103
+ // for the shape/color transitions — spring-effects-default here.
104
+ "transition-[box-shadow,background-color,color,border-color,border-radius] duration-200 ease-spring-effects-default",
33
105
  // State layer overlay
34
106
  "before:absolute before:inset-0 before:rounded-[inherit] before:bg-current before:opacity-0 before:pointer-events-none",
35
107
  "before:transition-opacity before:duration-100",
@@ -40,14 +112,14 @@ var iconButton = tv({
40
112
  // Focus ring (M3: 3px secondary, 2px offset)
41
113
  "focus-visible:outline-[3px] focus-visible:outline-offset-2 focus-visible:outline-secondary",
42
114
  // Disabled: no interaction, no state layer. Per-variant disabled colors
43
- // (container on-surface/12, icon on-surface/38) live on each variant.
115
+ // (container on-surface/10, icon on-surface/38) live on each variant.
44
116
  "disabled:pointer-events-none disabled:before:opacity-0",
45
117
  "data-[disabled]:pointer-events-none data-[disabled]:before:opacity-0"
46
118
  ],
47
119
  variants: {
48
120
  // Disabled icon is on-surface/38 for every variant; filled/tonal disabled
49
- // container is on-surface/12; outlined disabled outline is on-surface/12
50
- // (material-web parity).
121
+ // container is on-surface/10 (DisabledContainerOpacity); outlined disabled
122
+ // outline stays outline-variant (Expressive DisabledOutlineColor).
51
123
  variant: {
52
124
  standard: [
53
125
  "text-on-surface-variant",
@@ -55,68 +127,93 @@ var iconButton = tv({
55
127
  ],
56
128
  filled: [
57
129
  "bg-primary text-on-primary",
58
- "disabled:bg-on-surface/12 disabled:text-on-surface/38",
59
- "data-[disabled]:bg-on-surface/12 data-[disabled]:text-on-surface/38"
130
+ "disabled:bg-on-surface/10 disabled:text-on-surface/38",
131
+ "data-[disabled]:bg-on-surface/10 data-[disabled]:text-on-surface/38"
60
132
  ],
61
133
  tonal: [
62
134
  "bg-secondary-container text-on-secondary-container",
63
- "disabled:bg-on-surface/12 disabled:text-on-surface/38",
64
- "data-[disabled]:bg-on-surface/12 data-[disabled]:text-on-surface/38"
135
+ "disabled:bg-on-surface/10 disabled:text-on-surface/38",
136
+ "data-[disabled]:bg-on-surface/10 data-[disabled]:text-on-surface/38"
65
137
  ],
66
138
  outlined: [
67
- "border border-outline text-on-surface-variant",
68
- "disabled:border-on-surface/12 disabled:text-on-surface/38",
69
- "data-[disabled]:border-on-surface/12 data-[disabled]:text-on-surface/38"
139
+ "border border-outline-variant text-on-surface-variant",
140
+ "disabled:border-outline-variant disabled:text-on-surface/38",
141
+ "data-[disabled]:border-outline-variant data-[disabled]:text-on-surface/38"
70
142
  ]
71
143
  },
72
- selected: {
73
- true: "",
74
- false: ""
144
+ // Toggle state, string-keyed so a plain (non-toggle) button — `toggle`
145
+ // unset — fires neither compound below (a boolean variant would default to
146
+ // `off` in tailwind-variants and wrongly apply the unselected look).
147
+ toggle: {
148
+ on: "",
149
+ off: ""
75
150
  },
76
151
  // Container height + icon size per M3 Expressive size. Width comes from the
77
- // (size, width) compound variants below.
152
+ // (size, width) compound variants below. The pressed corner morph
153
+ // (PressedContainerShape: XS·S small 8 / M medium 12 / L·XL large 16) rides
154
+ // on data-[pressed]/active so its attribute-selector specificity wins over
155
+ // the resting `rounded-*`.
78
156
  size: {
79
- xs: "h-8 [&>svg]:size-5",
80
- s: "h-10 [&>svg]:size-6",
81
- m: "h-14 [&>svg]:size-6",
82
- l: "h-24 [&>svg]:size-8",
83
- xl: "h-[136px] [&>svg]:size-10"
157
+ xs: "h-8 [&>svg]:size-5 data-[pressed]:rounded-small active:rounded-small",
158
+ s: "h-10 [&>svg]:size-6 data-[pressed]:rounded-small active:rounded-small",
159
+ m: "h-14 [&>svg]:size-6 data-[pressed]:rounded-medium active:rounded-medium",
160
+ l: "h-24 [&>svg]:size-8 data-[pressed]:rounded-large active:rounded-large",
161
+ xl: "h-[136px] [&>svg]:size-10 data-[pressed]:rounded-large active:rounded-large"
84
162
  },
85
163
  width: {
86
164
  narrow: "",
87
165
  default: "",
88
166
  wide: ""
167
+ },
168
+ // round = full circle; the square corner is size-specific (compounds below).
169
+ shape: {
170
+ round: "rounded-full",
171
+ square: ""
89
172
  }
90
173
  },
91
174
  compoundVariants: [
92
175
  ...widthCompounds,
93
- { variant: "standard", selected: true, class: "text-primary" },
94
- { variant: "filled", selected: false, class: "bg-surface-container-highest text-primary" },
176
+ ...squareShapeCompounds,
177
+ ...selectedShapeCompounds,
178
+ // ---- Outlined border width (OutlinedOutlineWidth: L 2 / XL 3 dp) ------
179
+ { variant: "outlined", size: "l", class: "border-2" },
180
+ { variant: "outlined", size: "xl", class: "border-[3px]" },
181
+ // ---- Toggle colors (Selected*/Unselected* tokens) ---------------------
182
+ // standard: unselected = on-surface-variant (base); selected = primary.
183
+ { variant: "standard", toggle: "on", class: "text-primary" },
184
+ // filled: base = default & selected look (primary/on-primary); unselected =
185
+ // surface-container + on-surface-variant (was surface-container-highest+primary).
95
186
  {
96
- variant: "tonal",
97
- selected: false,
98
- class: "bg-surface-container-highest text-on-surface-variant"
187
+ variant: "filled",
188
+ toggle: "off",
189
+ class: "bg-surface-container text-on-surface-variant"
99
190
  },
191
+ // tonal: base = default & unselected look (secondary-container); selected =
192
+ // secondary + on-secondary (was left at the variant default — the "selection
193
+ // not visible" bug this issue fixes).
194
+ { variant: "tonal", toggle: "on", class: "bg-secondary text-on-secondary" },
195
+ // outlined: selected fills with the inverse surface (base = unselected).
100
196
  {
101
197
  variant: "outlined",
102
- selected: true,
198
+ toggle: "on",
103
199
  class: [
104
200
  "bg-inverse-surface text-inverse-on-surface border-transparent",
105
- // M3 disabled + selected: faint on-surface/12 container, no outline
201
+ // M3 disabled + selected: faint on-surface/10 container, no outline
106
202
  // (icon falls back to on-surface/38 from the variant). NOT transparent.
107
- "disabled:bg-on-surface/12 disabled:border-transparent",
108
- "data-[disabled]:bg-on-surface/12 data-[disabled]:border-transparent"
203
+ "disabled:bg-on-surface/10 disabled:border-transparent",
204
+ "data-[disabled]:bg-on-surface/10 data-[disabled]:border-transparent"
109
205
  ]
110
206
  }
111
207
  ],
112
208
  defaultVariants: {
113
209
  variant: "standard",
114
210
  size: "s",
115
- width: "default"
211
+ width: "default",
212
+ shape: "round"
116
213
  }
117
214
  });
118
215
  var IconButton = createIconButton(
119
- ({ variant, selected, size, width }) => iconButton({ variant, selected, size, width })
216
+ ({ variant, selected, size, width, shape }) => iconButton({ variant, size, width, shape, toggle: toToggle(selected) })
120
217
  );
121
218
 
122
219
  export { IconButton, iconButton };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/icon-button/icon-button.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * icon-button.ts — tailwind-variants resolver for the M3 Icon Button.\n *\n * 40×40 circular target with a centered icon. The `selected` variant supports\n * toggle icon buttons; when `selected` is undefined the variant's default\n * (filled/active) look is used. Emits the same DOM + ripple as the VE build.\n */\nimport { createIconButton } from '@m3-baseui/core';\nimport { tv } from 'tailwind-variants';\n\n// M3 Expressive container widths (px) per size × width. Tailwind v4's dynamic\n// spacing scale resolves any integer (e.g. w-13 = 52px, w-46 = 184px).\nconst WIDTHS = {\n xs: { narrow: 'w-7', default: 'w-8', wide: 'w-10' }, // 28 / 32 / 40\n s: { narrow: 'w-8', default: 'w-10', wide: 'w-13' }, // 32 / 40 / 52\n m: { narrow: 'w-12', default: 'w-14', wide: 'w-18' }, // 48 / 56 / 72\n l: { narrow: 'w-16', default: 'w-24', wide: 'w-32' }, // 64 / 96 / 128\n xl: { narrow: 'w-26', default: 'w-34', wide: 'w-46' }, // 104 / 136 / 184\n} as const;\n\nconst widthCompounds = Object.entries(WIDTHS).flatMap(([size, w]) =>\n Object.entries(w).map(([width, klass]) => ({\n size: size as keyof typeof WIDTHS,\n width: width as 'narrow' | 'default' | 'wide',\n class: klass,\n })),\n);\n\nexport const iconButton = tv({\n base: [\n 'relative inline-flex items-center justify-center shrink-0',\n // No `overflow-hidden`: it would clip the 48dp touch target on small sizes.\n // The state layer is already rounded (before:rounded-[inherit]); the ripple\n // self-clips.\n 'rounded-full cursor-pointer select-none border-0 bg-transparent',\n 'transition-[box-shadow,background-color,color] duration-200 ease-standard',\n // State layer overlay\n 'before:absolute before:inset-0 before:rounded-[inherit] before:bg-current before:opacity-0 before:pointer-events-none',\n 'before:transition-opacity before:duration-100',\n 'hover:before:opacity-[var(--md-sys-state-hover)]',\n 'focus-visible:before:opacity-[var(--md-sys-state-focus)]',\n 'active:before:opacity-[var(--md-sys-state-pressed)]',\n 'data-[pressed]:before:opacity-[var(--md-sys-state-pressed)]',\n // Focus ring (M3: 3px secondary, 2px offset)\n 'focus-visible:outline-[3px] focus-visible:outline-offset-2 focus-visible:outline-secondary',\n // Disabled: no interaction, no state layer. Per-variant disabled colors\n // (container on-surface/12, icon on-surface/38) live on each variant.\n 'disabled:pointer-events-none disabled:before:opacity-0',\n 'data-[disabled]:pointer-events-none data-[disabled]:before:opacity-0',\n ],\n variants: {\n // Disabled icon is on-surface/38 for every variant; filled/tonal disabled\n // container is on-surface/12; outlined disabled outline is on-surface/12\n // (material-web parity).\n variant: {\n standard: [\n 'text-on-surface-variant',\n 'disabled:text-on-surface/38 data-[disabled]:text-on-surface/38',\n ],\n filled: [\n 'bg-primary text-on-primary',\n 'disabled:bg-on-surface/12 disabled:text-on-surface/38',\n 'data-[disabled]:bg-on-surface/12 data-[disabled]:text-on-surface/38',\n ],\n tonal: [\n 'bg-secondary-container text-on-secondary-container',\n 'disabled:bg-on-surface/12 disabled:text-on-surface/38',\n 'data-[disabled]:bg-on-surface/12 data-[disabled]:text-on-surface/38',\n ],\n outlined: [\n 'border border-outline text-on-surface-variant',\n 'disabled:border-on-surface/12 disabled:text-on-surface/38',\n 'data-[disabled]:border-on-surface/12 data-[disabled]:text-on-surface/38',\n ],\n },\n selected: {\n true: '',\n false: '',\n },\n // Container height + icon size per M3 Expressive size. Width comes from the\n // (size, width) compound variants below.\n size: {\n xs: 'h-8 [&>svg]:size-5',\n s: 'h-10 [&>svg]:size-6',\n m: 'h-14 [&>svg]:size-6',\n l: 'h-24 [&>svg]:size-8',\n xl: 'h-[136px] [&>svg]:size-10',\n },\n width: {\n narrow: '',\n default: '',\n wide: '',\n },\n },\n compoundVariants: [\n ...widthCompounds,\n { variant: 'standard', selected: true, class: 'text-primary' },\n { variant: 'filled', selected: false, class: 'bg-surface-container-highest text-primary' },\n {\n variant: 'tonal',\n selected: false,\n class: 'bg-surface-container-highest text-on-surface-variant',\n },\n {\n variant: 'outlined',\n selected: true,\n class: [\n 'bg-inverse-surface text-inverse-on-surface border-transparent',\n // M3 disabled + selected: faint on-surface/12 container, no outline\n // (icon falls back to on-surface/38 from the variant). NOT transparent.\n 'disabled:bg-on-surface/12 disabled:border-transparent',\n 'data-[disabled]:bg-on-surface/12 data-[disabled]:border-transparent',\n ],\n },\n ],\n defaultVariants: {\n variant: 'standard',\n size: 's',\n width: 'default',\n },\n});\n\nexport const IconButton = createIconButton(({ variant, selected, size, width }) =>\n iconButton({ variant, selected, size, width }),\n);\nexport type { IconButtonProps, IconButtonVariant } from '@m3-baseui/core';\n"]}
1
+ {"version":3,"sources":["../../../src/tv.ts","../../../src/components/icon-button/icon-button.ts"],"names":["baseTv"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * tv.ts — a tailwind-variants factory pre-configured for the M3 typescale and\n * shape scale.\n *\n * The Tailwind v4 preset exposes the 15 typescale roles as `text-<role>`\n * font-size utilities (e.g. `text-body-small`, `text-label-large`) and the\n * shape scale as `rounded-<role>` border-radius utilities (e.g. `rounded-small`,\n * `rounded-extra-large`). Stock tailwind-merge does not know these custom names,\n * so it (a) groups the typescale names with `text-<color>` and drops one when a\n * slot sets both a color *and* a typescale, and (b) fails to see two custom\n * `rounded-<role>` classes as conflicting, so a later corner override never\n * dedupes the resting one (both survive, and CSS source order — not intent —\n * decides). Either way token compliance / shape morph silently breaks.\n *\n * Teaching tailwind-merge that the typescale names belong to `font-size` and the\n * shape names belong to `rounded` keeps color and type independent and makes the\n * corner utilities override one another as expected.\n */\nimport { type TV, tv as baseTv } from 'tailwind-variants';\n\nconst TYPESCALE = [\n 'display-large',\n 'display-medium',\n 'display-small',\n 'headline-large',\n 'headline-medium',\n 'headline-small',\n 'title-large',\n 'title-medium',\n 'title-small',\n 'body-large',\n 'body-medium',\n 'body-small',\n 'label-large',\n 'label-medium',\n 'label-small',\n] as const;\n\n// M3 shape scale exposed as `rounded-<role>` (see @m3-baseui/tokens theme.css\n// `--radius-*`). `none` / `full` overlap stock Tailwind and are harmless to\n// re-list; the intermediate roles are what stock tailwind-merge misses.\nconst SHAPE = [\n 'none',\n 'extra-small',\n 'small',\n 'medium',\n 'large',\n 'large-increased',\n 'extra-large',\n 'extra-large-increased',\n 'full',\n] as const;\n\nexport const tv: TV = (options, config) =>\n baseTv(options, {\n ...config,\n twMergeConfig: {\n extend: {\n classGroups: {\n 'font-size': [{ text: [...TYPESCALE] }],\n rounded: [{ rounded: [...SHAPE] }],\n },\n },\n },\n });\n","/**\n * icon-button.ts — tailwind-variants resolver for the M3 (Expressive) Icon Button.\n *\n * Emits the same DOM + `data-*` state as the vanilla-extract build (drop-in\n * compatible); only the class strings differ. The `rounded-<role>` corner\n * utilities come from the Tailwind v4 `@theme` preset in\n * `@m3-baseui/tokens/theme.css`; the configured `tv` helper teaches\n * tailwind-merge to dedupe them so a morph override drops the resting corner.\n *\n * M3 Expressive: five sizes (XS 32 → XL 136 dp) drive height / icon size and\n * (with `width`) the container width; `shape` picks the resting corner\n * (round=full circle vs a size-specific square), the corner morphs smaller on\n * press, and toggle buttons (`selected`) swap to the opposite shape\n * (round↔square) plus the Selected/Unselected color set.\n */\nimport { createIconButton, toToggle } from '@m3-baseui/core';\nimport { tv } from '../../tv';\n\n// M3 Expressive container widths (px) per size × width. Tailwind v4's dynamic\n// spacing scale resolves any integer (e.g. w-13 = 52px, w-46 = 184px).\nconst WIDTHS = {\n xs: { narrow: 'w-7', default: 'w-8', wide: 'w-10' }, // 28 / 32 / 40\n s: { narrow: 'w-8', default: 'w-10', wide: 'w-13' }, // 32 / 40 / 52\n m: { narrow: 'w-12', default: 'w-14', wide: 'w-18' }, // 48 / 56 / 72\n l: { narrow: 'w-16', default: 'w-24', wide: 'w-32' }, // 64 / 96 / 128\n xl: { narrow: 'w-26', default: 'w-34', wide: 'w-46' }, // 104 / 136 / 184\n} as const;\n\nconst widthCompounds = Object.entries(WIDTHS).flatMap(([size, w]) =>\n Object.entries(w).map(([width, klass]) => ({\n size: size as keyof typeof WIDTHS,\n width: width as 'narrow' | 'default' | 'wide',\n class: klass,\n })),\n);\n\n// Resting square corner (ContainerShapeSquare) per size bucket.\nconst SQUARE_CORNER = {\n xs: 'rounded-medium', // 12dp\n s: 'rounded-medium', // 12dp\n m: 'rounded-large', // 16dp\n l: 'rounded-extra-large', // 28dp\n xl: 'rounded-extra-large', // 28dp\n} as const;\n\n// Resting square corner: `shape: square` maps to the size-specific corner.\nconst squareShapeCompounds = (Object.keys(SQUARE_CORNER) as (keyof typeof SQUARE_CORNER)[]).map(\n (size) => ({ shape: 'square' as const, size, class: SQUARE_CORNER[size] }),\n);\n\n// Selected shape morph (Expressive's signature behavior). Listed after the\n// resting corner so tailwind-merge keeps these: a selected `round` container\n// morphs to the square corner; a selected `square` container morphs to `full`.\nconst selectedShapeCompounds = [\n ...(Object.keys(SQUARE_CORNER) as (keyof typeof SQUARE_CORNER)[]).map((size) => ({\n shape: 'round' as const,\n toggle: 'on' as const,\n size,\n class: SQUARE_CORNER[size],\n })),\n ...(Object.keys(SQUARE_CORNER) as (keyof typeof SQUARE_CORNER)[]).map((size) => ({\n shape: 'square' as const,\n toggle: 'on' as const,\n size,\n class: 'rounded-full',\n })),\n];\n\nexport const iconButton = tv({\n base: [\n 'relative inline-flex items-center justify-center shrink-0',\n // No `overflow-hidden`: it would clip the 48dp touch target on small sizes.\n // The state layer is already rounded (before:rounded-[inherit]); the ripple\n // self-clips.\n 'rounded-full cursor-pointer select-none border-0 bg-transparent',\n // Motion: Compose uses DefaultEffects (critically-damped spring, no bounce)\n // for the shape/color transitions — spring-effects-default here.\n 'transition-[box-shadow,background-color,color,border-color,border-radius] duration-200 ease-spring-effects-default',\n // State layer overlay\n 'before:absolute before:inset-0 before:rounded-[inherit] before:bg-current before:opacity-0 before:pointer-events-none',\n 'before:transition-opacity before:duration-100',\n 'hover:before:opacity-[var(--md-sys-state-hover)]',\n 'focus-visible:before:opacity-[var(--md-sys-state-focus)]',\n 'active:before:opacity-[var(--md-sys-state-pressed)]',\n 'data-[pressed]:before:opacity-[var(--md-sys-state-pressed)]',\n // Focus ring (M3: 3px secondary, 2px offset)\n 'focus-visible:outline-[3px] focus-visible:outline-offset-2 focus-visible:outline-secondary',\n // Disabled: no interaction, no state layer. Per-variant disabled colors\n // (container on-surface/10, icon on-surface/38) live on each variant.\n 'disabled:pointer-events-none disabled:before:opacity-0',\n 'data-[disabled]:pointer-events-none data-[disabled]:before:opacity-0',\n ],\n variants: {\n // Disabled icon is on-surface/38 for every variant; filled/tonal disabled\n // container is on-surface/10 (DisabledContainerOpacity); outlined disabled\n // outline stays outline-variant (Expressive DisabledOutlineColor).\n variant: {\n standard: [\n 'text-on-surface-variant',\n 'disabled:text-on-surface/38 data-[disabled]:text-on-surface/38',\n ],\n filled: [\n 'bg-primary text-on-primary',\n 'disabled:bg-on-surface/10 disabled:text-on-surface/38',\n 'data-[disabled]:bg-on-surface/10 data-[disabled]:text-on-surface/38',\n ],\n tonal: [\n 'bg-secondary-container text-on-secondary-container',\n 'disabled:bg-on-surface/10 disabled:text-on-surface/38',\n 'data-[disabled]:bg-on-surface/10 data-[disabled]:text-on-surface/38',\n ],\n outlined: [\n 'border border-outline-variant text-on-surface-variant',\n 'disabled:border-outline-variant disabled:text-on-surface/38',\n 'data-[disabled]:border-outline-variant data-[disabled]:text-on-surface/38',\n ],\n },\n // Toggle state, string-keyed so a plain (non-toggle) button — `toggle`\n // unset — fires neither compound below (a boolean variant would default to\n // `off` in tailwind-variants and wrongly apply the unselected look).\n toggle: {\n on: '',\n off: '',\n },\n // Container height + icon size per M3 Expressive size. Width comes from the\n // (size, width) compound variants below. The pressed corner morph\n // (PressedContainerShape: XS·S small 8 / M medium 12 / L·XL large 16) rides\n // on data-[pressed]/active so its attribute-selector specificity wins over\n // the resting `rounded-*`.\n size: {\n xs: 'h-8 [&>svg]:size-5 data-[pressed]:rounded-small active:rounded-small',\n s: 'h-10 [&>svg]:size-6 data-[pressed]:rounded-small active:rounded-small',\n m: 'h-14 [&>svg]:size-6 data-[pressed]:rounded-medium active:rounded-medium',\n l: 'h-24 [&>svg]:size-8 data-[pressed]:rounded-large active:rounded-large',\n xl: 'h-[136px] [&>svg]:size-10 data-[pressed]:rounded-large active:rounded-large',\n },\n width: {\n narrow: '',\n default: '',\n wide: '',\n },\n // round = full circle; the square corner is size-specific (compounds below).\n shape: {\n round: 'rounded-full',\n square: '',\n },\n },\n compoundVariants: [\n ...widthCompounds,\n ...squareShapeCompounds,\n ...selectedShapeCompounds,\n // ---- Outlined border width (OutlinedOutlineWidth: L 2 / XL 3 dp) ------\n { variant: 'outlined', size: 'l', class: 'border-2' },\n { variant: 'outlined', size: 'xl', class: 'border-[3px]' },\n // ---- Toggle colors (Selected*/Unselected* tokens) ---------------------\n // standard: unselected = on-surface-variant (base); selected = primary.\n { variant: 'standard', toggle: 'on', class: 'text-primary' },\n // filled: base = default & selected look (primary/on-primary); unselected =\n // surface-container + on-surface-variant (was surface-container-highest+primary).\n {\n variant: 'filled',\n toggle: 'off',\n class: 'bg-surface-container text-on-surface-variant',\n },\n // tonal: base = default & unselected look (secondary-container); selected =\n // secondary + on-secondary (was left at the variant default — the \"selection\n // not visible\" bug this issue fixes).\n { variant: 'tonal', toggle: 'on', class: 'bg-secondary text-on-secondary' },\n // outlined: selected fills with the inverse surface (base = unselected).\n {\n variant: 'outlined',\n toggle: 'on',\n class: [\n 'bg-inverse-surface text-inverse-on-surface border-transparent',\n // M3 disabled + selected: faint on-surface/10 container, no outline\n // (icon falls back to on-surface/38 from the variant). NOT transparent.\n 'disabled:bg-on-surface/10 disabled:border-transparent',\n 'data-[disabled]:bg-on-surface/10 data-[disabled]:border-transparent',\n ],\n },\n ],\n defaultVariants: {\n variant: 'standard',\n size: 's',\n width: 'default',\n shape: 'round',\n },\n});\n\nexport const IconButton = createIconButton(({ variant, selected, size, width, shape }) =>\n iconButton({ variant, size, width, shape, toggle: toToggle(selected) }),\n);\nexport type {\n IconButtonProps,\n IconButtonVariant,\n IconButtonSize,\n IconButtonWidth,\n IconButtonShape,\n} from '@m3-baseui/core';\n"]}
package/dist/index.js CHANGED
@@ -231,14 +231,45 @@ var widthCompounds = Object.entries(WIDTHS).flatMap(
231
231
  class: klass
232
232
  }))
233
233
  );
234
- var iconButton = tv$1({
234
+ var SQUARE_CORNER = {
235
+ xs: "rounded-medium",
236
+ // 12dp
237
+ s: "rounded-medium",
238
+ // 12dp
239
+ m: "rounded-large",
240
+ // 16dp
241
+ l: "rounded-extra-large",
242
+ // 28dp
243
+ xl: "rounded-extra-large"
244
+ // 28dp
245
+ };
246
+ var squareShapeCompounds = Object.keys(SQUARE_CORNER).map(
247
+ (size) => ({ shape: "square", size, class: SQUARE_CORNER[size] })
248
+ );
249
+ var selectedShapeCompounds = [
250
+ ...Object.keys(SQUARE_CORNER).map((size) => ({
251
+ shape: "round",
252
+ toggle: "on",
253
+ size,
254
+ class: SQUARE_CORNER[size]
255
+ })),
256
+ ...Object.keys(SQUARE_CORNER).map((size) => ({
257
+ shape: "square",
258
+ toggle: "on",
259
+ size,
260
+ class: "rounded-full"
261
+ }))
262
+ ];
263
+ var iconButton = tv({
235
264
  base: [
236
265
  "relative inline-flex items-center justify-center shrink-0",
237
266
  // No `overflow-hidden`: it would clip the 48dp touch target on small sizes.
238
267
  // The state layer is already rounded (before:rounded-[inherit]); the ripple
239
268
  // self-clips.
240
269
  "rounded-full cursor-pointer select-none border-0 bg-transparent",
241
- "transition-[box-shadow,background-color,color] duration-200 ease-standard",
270
+ // Motion: Compose uses DefaultEffects (critically-damped spring, no bounce)
271
+ // for the shape/color transitions — spring-effects-default here.
272
+ "transition-[box-shadow,background-color,color,border-color,border-radius] duration-200 ease-spring-effects-default",
242
273
  // State layer overlay
243
274
  "before:absolute before:inset-0 before:rounded-[inherit] before:bg-current before:opacity-0 before:pointer-events-none",
244
275
  "before:transition-opacity before:duration-100",
@@ -249,14 +280,14 @@ var iconButton = tv$1({
249
280
  // Focus ring (M3: 3px secondary, 2px offset)
250
281
  "focus-visible:outline-[3px] focus-visible:outline-offset-2 focus-visible:outline-secondary",
251
282
  // Disabled: no interaction, no state layer. Per-variant disabled colors
252
- // (container on-surface/12, icon on-surface/38) live on each variant.
283
+ // (container on-surface/10, icon on-surface/38) live on each variant.
253
284
  "disabled:pointer-events-none disabled:before:opacity-0",
254
285
  "data-[disabled]:pointer-events-none data-[disabled]:before:opacity-0"
255
286
  ],
256
287
  variants: {
257
288
  // Disabled icon is on-surface/38 for every variant; filled/tonal disabled
258
- // container is on-surface/12; outlined disabled outline is on-surface/12
259
- // (material-web parity).
289
+ // container is on-surface/10 (DisabledContainerOpacity); outlined disabled
290
+ // outline stays outline-variant (Expressive DisabledOutlineColor).
260
291
  variant: {
261
292
  standard: [
262
293
  "text-on-surface-variant",
@@ -264,68 +295,93 @@ var iconButton = tv$1({
264
295
  ],
265
296
  filled: [
266
297
  "bg-primary text-on-primary",
267
- "disabled:bg-on-surface/12 disabled:text-on-surface/38",
268
- "data-[disabled]:bg-on-surface/12 data-[disabled]:text-on-surface/38"
298
+ "disabled:bg-on-surface/10 disabled:text-on-surface/38",
299
+ "data-[disabled]:bg-on-surface/10 data-[disabled]:text-on-surface/38"
269
300
  ],
270
301
  tonal: [
271
302
  "bg-secondary-container text-on-secondary-container",
272
- "disabled:bg-on-surface/12 disabled:text-on-surface/38",
273
- "data-[disabled]:bg-on-surface/12 data-[disabled]:text-on-surface/38"
303
+ "disabled:bg-on-surface/10 disabled:text-on-surface/38",
304
+ "data-[disabled]:bg-on-surface/10 data-[disabled]:text-on-surface/38"
274
305
  ],
275
306
  outlined: [
276
- "border border-outline text-on-surface-variant",
277
- "disabled:border-on-surface/12 disabled:text-on-surface/38",
278
- "data-[disabled]:border-on-surface/12 data-[disabled]:text-on-surface/38"
307
+ "border border-outline-variant text-on-surface-variant",
308
+ "disabled:border-outline-variant disabled:text-on-surface/38",
309
+ "data-[disabled]:border-outline-variant data-[disabled]:text-on-surface/38"
279
310
  ]
280
311
  },
281
- selected: {
282
- true: "",
283
- false: ""
312
+ // Toggle state, string-keyed so a plain (non-toggle) button — `toggle`
313
+ // unset — fires neither compound below (a boolean variant would default to
314
+ // `off` in tailwind-variants and wrongly apply the unselected look).
315
+ toggle: {
316
+ on: "",
317
+ off: ""
284
318
  },
285
319
  // Container height + icon size per M3 Expressive size. Width comes from the
286
- // (size, width) compound variants below.
320
+ // (size, width) compound variants below. The pressed corner morph
321
+ // (PressedContainerShape: XS·S small 8 / M medium 12 / L·XL large 16) rides
322
+ // on data-[pressed]/active so its attribute-selector specificity wins over
323
+ // the resting `rounded-*`.
287
324
  size: {
288
- xs: "h-8 [&>svg]:size-5",
289
- s: "h-10 [&>svg]:size-6",
290
- m: "h-14 [&>svg]:size-6",
291
- l: "h-24 [&>svg]:size-8",
292
- xl: "h-[136px] [&>svg]:size-10"
325
+ xs: "h-8 [&>svg]:size-5 data-[pressed]:rounded-small active:rounded-small",
326
+ s: "h-10 [&>svg]:size-6 data-[pressed]:rounded-small active:rounded-small",
327
+ m: "h-14 [&>svg]:size-6 data-[pressed]:rounded-medium active:rounded-medium",
328
+ l: "h-24 [&>svg]:size-8 data-[pressed]:rounded-large active:rounded-large",
329
+ xl: "h-[136px] [&>svg]:size-10 data-[pressed]:rounded-large active:rounded-large"
293
330
  },
294
331
  width: {
295
332
  narrow: "",
296
333
  default: "",
297
334
  wide: ""
335
+ },
336
+ // round = full circle; the square corner is size-specific (compounds below).
337
+ shape: {
338
+ round: "rounded-full",
339
+ square: ""
298
340
  }
299
341
  },
300
342
  compoundVariants: [
301
343
  ...widthCompounds,
302
- { variant: "standard", selected: true, class: "text-primary" },
303
- { variant: "filled", selected: false, class: "bg-surface-container-highest text-primary" },
344
+ ...squareShapeCompounds,
345
+ ...selectedShapeCompounds,
346
+ // ---- Outlined border width (OutlinedOutlineWidth: L 2 / XL 3 dp) ------
347
+ { variant: "outlined", size: "l", class: "border-2" },
348
+ { variant: "outlined", size: "xl", class: "border-[3px]" },
349
+ // ---- Toggle colors (Selected*/Unselected* tokens) ---------------------
350
+ // standard: unselected = on-surface-variant (base); selected = primary.
351
+ { variant: "standard", toggle: "on", class: "text-primary" },
352
+ // filled: base = default & selected look (primary/on-primary); unselected =
353
+ // surface-container + on-surface-variant (was surface-container-highest+primary).
304
354
  {
305
- variant: "tonal",
306
- selected: false,
307
- class: "bg-surface-container-highest text-on-surface-variant"
355
+ variant: "filled",
356
+ toggle: "off",
357
+ class: "bg-surface-container text-on-surface-variant"
308
358
  },
359
+ // tonal: base = default & unselected look (secondary-container); selected =
360
+ // secondary + on-secondary (was left at the variant default — the "selection
361
+ // not visible" bug this issue fixes).
362
+ { variant: "tonal", toggle: "on", class: "bg-secondary text-on-secondary" },
363
+ // outlined: selected fills with the inverse surface (base = unselected).
309
364
  {
310
365
  variant: "outlined",
311
- selected: true,
366
+ toggle: "on",
312
367
  class: [
313
368
  "bg-inverse-surface text-inverse-on-surface border-transparent",
314
- // M3 disabled + selected: faint on-surface/12 container, no outline
369
+ // M3 disabled + selected: faint on-surface/10 container, no outline
315
370
  // (icon falls back to on-surface/38 from the variant). NOT transparent.
316
- "disabled:bg-on-surface/12 disabled:border-transparent",
317
- "data-[disabled]:bg-on-surface/12 data-[disabled]:border-transparent"
371
+ "disabled:bg-on-surface/10 disabled:border-transparent",
372
+ "data-[disabled]:bg-on-surface/10 data-[disabled]:border-transparent"
318
373
  ]
319
374
  }
320
375
  ],
321
376
  defaultVariants: {
322
377
  variant: "standard",
323
378
  size: "s",
324
- width: "default"
379
+ width: "default",
380
+ shape: "round"
325
381
  }
326
382
  });
327
383
  var IconButton = createIconButton(
328
- ({ variant, selected, size, width }) => iconButton({ variant, selected, size, width })
384
+ ({ variant, selected, size, width, shape }) => iconButton({ variant, size, width, shape, toggle: toToggle(selected) })
329
385
  );
330
386
  var switchTv = tv$1({
331
387
  slots: {