@bug-on/m3-expressive 1.1.0 → 1.2.1

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 (61) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/buttons.d.mts +3 -3
  3. package/dist/buttons.d.ts +3 -3
  4. package/dist/buttons.js +708 -606
  5. package/dist/buttons.js.map +1 -1
  6. package/dist/buttons.mjs +707 -605
  7. package/dist/buttons.mjs.map +1 -1
  8. package/dist/core-Bc5Wj_pc.d.ts +497 -0
  9. package/dist/core-D4048_K5.d.mts +497 -0
  10. package/dist/core.d.mts +6 -422
  11. package/dist/core.d.ts +6 -422
  12. package/dist/core.js +223 -129
  13. package/dist/core.js.map +1 -1
  14. package/dist/core.mjs +224 -130
  15. package/dist/core.mjs.map +1 -1
  16. package/dist/feedback.js +85 -61
  17. package/dist/feedback.js.map +1 -1
  18. package/dist/feedback.mjs +85 -61
  19. package/dist/feedback.mjs.map +1 -1
  20. package/dist/forms.d.mts +2 -2
  21. package/dist/forms.d.ts +2 -2
  22. package/dist/forms.js +70 -8
  23. package/dist/forms.js.map +1 -1
  24. package/dist/forms.mjs +70 -8
  25. package/dist/forms.mjs.map +1 -1
  26. package/dist/{icon-button-D-gs0gfj.d.mts → icon-button-sSt6PPLg.d.mts} +6 -0
  27. package/dist/{icon-button-D-gs0gfj.d.ts → icon-button-sSt6PPLg.d.ts} +6 -0
  28. package/dist/index.css +6 -1
  29. package/dist/index.d.mts +68 -6
  30. package/dist/index.d.ts +68 -6
  31. package/dist/index.js +1177 -580
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.mjs +1172 -583
  34. package/dist/index.mjs.map +1 -1
  35. package/dist/layout.d.mts +18 -1
  36. package/dist/layout.d.ts +18 -1
  37. package/dist/layout.js +94 -8
  38. package/dist/layout.js.map +1 -1
  39. package/dist/layout.mjs +94 -8
  40. package/dist/layout.mjs.map +1 -1
  41. package/dist/{md3-DFhj-NZj.d.mts → md3-Dty-Qcad.d.mts} +7 -1
  42. package/dist/{md3-DFhj-NZj.d.ts → md3-Dty-Qcad.d.ts} +7 -1
  43. package/dist/navigation.d.mts +134 -9
  44. package/dist/navigation.d.ts +134 -9
  45. package/dist/navigation.js +403 -93
  46. package/dist/navigation.js.map +1 -1
  47. package/dist/navigation.mjs +397 -94
  48. package/dist/navigation.mjs.map +1 -1
  49. package/dist/overlays.js +214 -189
  50. package/dist/overlays.js.map +1 -1
  51. package/dist/overlays.mjs +176 -151
  52. package/dist/overlays.mjs.map +1 -1
  53. package/dist/pickers.js +222 -197
  54. package/dist/pickers.js.map +1 -1
  55. package/dist/pickers.mjs +189 -164
  56. package/dist/pickers.mjs.map +1 -1
  57. package/dist/{split-button-trailing-uncheckable-BRPuTqi1.d.mts → split-button-trailing-uncheckable-BcPD_7uK.d.ts} +73 -7
  58. package/dist/{split-button-trailing-uncheckable-CjOFCoyW.d.ts → split-button-trailing-uncheckable-DtFJkTFr.d.mts} +73 -7
  59. package/dist/{text-field-eAIpz9z1.d.mts → text-field-T4Rg-9Bw.d.mts} +7 -0
  60. package/dist/{text-field-eAIpz9z1.d.ts → text-field-T4Rg-9Bw.d.ts} +7 -0
  61. package/package.json +4 -4
package/dist/core.js CHANGED
@@ -7,6 +7,7 @@ var materialColorUtilities = require('@material/material-color-utilities');
7
7
  var clsx = require('clsx');
8
8
  var tailwindMerge = require('tailwind-merge');
9
9
  var react = require('motion/react');
10
+ var reactSlot = require('@radix-ui/react-slot');
10
11
 
11
12
  function _interopNamespace(e) {
12
13
  if (e && e.__esModule) return e;
@@ -172,66 +173,110 @@ function resolveMode(mode) {
172
173
  if (typeof window === "undefined") return "light";
173
174
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
174
175
  }
175
- function generateM3Theme(sourceColorHex, mode = "light") {
176
- const sourceColor = materialColorUtilities.argbFromHex(sourceColorHex);
177
- const theme = materialColorUtilities.themeFromSourceColor(sourceColor);
178
- const scheme = mode === "light" ? theme.schemes.light : theme.schemes.dark;
179
- const palettes = theme.palettes;
180
- const tone = (palette, t) => materialColorUtilities.hexFromArgb(palette.tone(t));
176
+ function createDynamicScheme(sourceColorHex, isDark, variant = "expressive", contrastLevel = 0) {
177
+ const hct = materialColorUtilities.Hct.fromInt(argbFromHex(sourceColorHex));
178
+ const spec = "2025";
179
+ switch (variant) {
180
+ case "tonal_spot":
181
+ return new materialColorUtilities.SchemeTonalSpot(hct, isDark, contrastLevel, spec);
182
+ case "vibrant":
183
+ return new materialColorUtilities.SchemeVibrant(hct, isDark, contrastLevel, spec);
184
+ case "fidelity":
185
+ return new materialColorUtilities.SchemeFidelity(hct, isDark, contrastLevel, spec);
186
+ case "content":
187
+ return new materialColorUtilities.SchemeContent(hct, isDark, contrastLevel, spec);
188
+ case "monochrome":
189
+ return new materialColorUtilities.SchemeMonochrome(hct, isDark, contrastLevel, spec);
190
+ case "neutral":
191
+ return new materialColorUtilities.SchemeNeutral(hct, isDark, contrastLevel, spec);
192
+ default:
193
+ return new materialColorUtilities.SchemeExpressive(hct, isDark, contrastLevel, spec);
194
+ }
195
+ }
196
+ function argbFromHex(hex) {
197
+ const sanitized = hex.replace(/^#/, "");
198
+ const r = Number.parseInt(sanitized.substring(0, 2), 16);
199
+ const g = Number.parseInt(sanitized.substring(2, 4), 16);
200
+ const b = Number.parseInt(sanitized.substring(4, 6), 16);
201
+ return (4278190080 | r << 16 | g << 8 | b) >>> 0;
202
+ }
203
+ function generateM3Theme(sourceColorHex, mode = "light", options = {}) {
204
+ const { variant = "expressive", contrastLevel = 0 } = options;
205
+ const isDark = mode === "dark";
206
+ const scheme = createDynamicScheme(
207
+ sourceColorHex,
208
+ isDark,
209
+ variant,
210
+ contrastLevel
211
+ );
212
+ const hex = (argb) => materialColorUtilities.hexFromArgb(argb);
181
213
  return {
182
- primary: materialColorUtilities.hexFromArgb(scheme.primary),
183
- onPrimary: materialColorUtilities.hexFromArgb(scheme.onPrimary),
184
- primaryContainer: materialColorUtilities.hexFromArgb(scheme.primaryContainer),
185
- onPrimaryContainer: materialColorUtilities.hexFromArgb(scheme.onPrimaryContainer),
186
- inversePrimary: materialColorUtilities.hexFromArgb(scheme.inversePrimary),
187
- primaryFixed: tone(palettes.primary, 90),
188
- primaryFixedDim: tone(palettes.primary, 80),
189
- onPrimaryFixed: tone(palettes.primary, 10),
190
- onPrimaryFixedVariant: tone(palettes.primary, 30),
191
- secondary: materialColorUtilities.hexFromArgb(scheme.secondary),
192
- onSecondary: materialColorUtilities.hexFromArgb(scheme.onSecondary),
193
- secondaryContainer: materialColorUtilities.hexFromArgb(scheme.secondaryContainer),
194
- onSecondaryContainer: materialColorUtilities.hexFromArgb(scheme.onSecondaryContainer),
195
- secondaryFixed: tone(palettes.secondary, 90),
196
- secondaryFixedDim: tone(palettes.secondary, 80),
197
- onSecondaryFixed: tone(palettes.secondary, 10),
198
- onSecondaryFixedVariant: tone(palettes.secondary, 30),
199
- tertiary: materialColorUtilities.hexFromArgb(scheme.tertiary),
200
- onTertiary: materialColorUtilities.hexFromArgb(scheme.onTertiary),
201
- tertiaryContainer: materialColorUtilities.hexFromArgb(scheme.tertiaryContainer),
202
- onTertiaryContainer: materialColorUtilities.hexFromArgb(scheme.onTertiaryContainer),
203
- tertiaryFixed: tone(palettes.tertiary, 90),
204
- tertiaryFixedDim: tone(palettes.tertiary, 80),
205
- onTertiaryFixed: tone(palettes.tertiary, 10),
206
- onTertiaryFixedVariant: tone(palettes.tertiary, 30),
207
- error: materialColorUtilities.hexFromArgb(scheme.error),
208
- onError: materialColorUtilities.hexFromArgb(scheme.onError),
209
- errorContainer: materialColorUtilities.hexFromArgb(scheme.errorContainer),
210
- onErrorContainer: materialColorUtilities.hexFromArgb(scheme.onErrorContainer),
211
- surface: materialColorUtilities.hexFromArgb(scheme.surface),
212
- onSurface: materialColorUtilities.hexFromArgb(scheme.onSurface),
213
- surfaceVariant: materialColorUtilities.hexFromArgb(scheme.surfaceVariant),
214
- onSurfaceVariant: materialColorUtilities.hexFromArgb(scheme.onSurfaceVariant),
215
- surfaceTint: materialColorUtilities.hexFromArgb(scheme.primary),
216
- // Surface container roles from neutral palette tones
217
- surfaceContainerLowest: mode === "light" ? tone(palettes.neutral, 100) : tone(palettes.neutral, 4),
218
- surfaceContainerLow: mode === "light" ? tone(palettes.neutral, 96) : tone(palettes.neutral, 10),
219
- surfaceContainer: mode === "light" ? tone(palettes.neutral, 94) : tone(palettes.neutral, 12),
220
- surfaceContainerHigh: mode === "light" ? tone(palettes.neutral, 92) : tone(palettes.neutral, 17),
221
- surfaceContainerHighest: mode === "light" ? tone(palettes.neutral, 90) : tone(palettes.neutral, 22),
222
- inverseSurface: materialColorUtilities.hexFromArgb(scheme.inverseSurface),
223
- inverseOnSurface: materialColorUtilities.hexFromArgb(scheme.inverseOnSurface),
224
- background: materialColorUtilities.hexFromArgb(scheme.background),
225
- onBackground: materialColorUtilities.hexFromArgb(scheme.onBackground),
226
- outline: materialColorUtilities.hexFromArgb(scheme.outline),
227
- outlineVariant: materialColorUtilities.hexFromArgb(scheme.outlineVariant),
228
- shadow: materialColorUtilities.hexFromArgb(scheme.shadow),
229
- scrim: materialColorUtilities.hexFromArgb(scheme.scrim)
214
+ // Primary
215
+ primary: hex(scheme.primary),
216
+ onPrimary: hex(scheme.onPrimary),
217
+ primaryContainer: hex(scheme.primaryContainer),
218
+ onPrimaryContainer: hex(scheme.onPrimaryContainer),
219
+ inversePrimary: hex(scheme.inversePrimary),
220
+ primaryFixed: hex(scheme.primaryFixed),
221
+ primaryFixedDim: hex(scheme.primaryFixedDim),
222
+ onPrimaryFixed: hex(scheme.onPrimaryFixed),
223
+ onPrimaryFixedVariant: hex(scheme.onPrimaryFixedVariant),
224
+ primaryDim: hex(scheme.primaryDim),
225
+ // Secondary
226
+ secondary: hex(scheme.secondary),
227
+ onSecondary: hex(scheme.onSecondary),
228
+ secondaryContainer: hex(scheme.secondaryContainer),
229
+ onSecondaryContainer: hex(scheme.onSecondaryContainer),
230
+ secondaryFixed: hex(scheme.secondaryFixed),
231
+ secondaryFixedDim: hex(scheme.secondaryFixedDim),
232
+ onSecondaryFixed: hex(scheme.onSecondaryFixed),
233
+ onSecondaryFixedVariant: hex(scheme.onSecondaryFixedVariant),
234
+ secondaryDim: hex(scheme.secondaryDim),
235
+ // Tertiary
236
+ tertiary: hex(scheme.tertiary),
237
+ onTertiary: hex(scheme.onTertiary),
238
+ tertiaryContainer: hex(scheme.tertiaryContainer),
239
+ onTertiaryContainer: hex(scheme.onTertiaryContainer),
240
+ tertiaryFixed: hex(scheme.tertiaryFixed),
241
+ tertiaryFixedDim: hex(scheme.tertiaryFixedDim),
242
+ onTertiaryFixed: hex(scheme.onTertiaryFixed),
243
+ onTertiaryFixedVariant: hex(scheme.onTertiaryFixedVariant),
244
+ tertiaryDim: hex(scheme.tertiaryDim),
245
+ // Error
246
+ error: hex(scheme.error),
247
+ onError: hex(scheme.onError),
248
+ errorContainer: hex(scheme.errorContainer),
249
+ onErrorContainer: hex(scheme.onErrorContainer),
250
+ // Surface
251
+ surface: hex(scheme.surface),
252
+ onSurface: hex(scheme.onSurface),
253
+ surfaceVariant: hex(scheme.surfaceVariant),
254
+ onSurfaceVariant: hex(scheme.onSurfaceVariant),
255
+ surfaceTint: hex(scheme.surfaceTint),
256
+ surfaceDim: hex(scheme.surfaceDim),
257
+ surfaceBright: hex(scheme.surfaceBright),
258
+ surfaceContainerLowest: hex(scheme.surfaceContainerLowest),
259
+ surfaceContainerLow: hex(scheme.surfaceContainerLow),
260
+ surfaceContainer: hex(scheme.surfaceContainer),
261
+ surfaceContainerHigh: hex(scheme.surfaceContainerHigh),
262
+ surfaceContainerHighest: hex(scheme.surfaceContainerHighest),
263
+ // Inverse
264
+ inverseSurface: hex(scheme.inverseSurface),
265
+ inverseOnSurface: hex(scheme.inverseOnSurface),
266
+ // Background
267
+ background: hex(scheme.background),
268
+ onBackground: hex(scheme.onBackground),
269
+ // Outline
270
+ outline: hex(scheme.outline),
271
+ outlineVariant: hex(scheme.outlineVariant),
272
+ // Utility
273
+ shadow: hex(scheme.shadow),
274
+ scrim: hex(scheme.scrim)
230
275
  };
231
276
  }
232
- function applyTheme(sourceColorHex, mode = "light", root = document.documentElement) {
277
+ function applyTheme(sourceColorHex, mode = "light", root = document.documentElement, options = {}) {
233
278
  const resolved = resolveMode(mode);
234
- const colors = generateM3Theme(sourceColorHex, resolved);
279
+ const colors = generateM3Theme(sourceColorHex, resolved, options);
235
280
  for (const [key, value] of Object.entries(colors)) {
236
281
  const kebabKey = key.replace(/[A-Z]/g, (m7) => `-${m7.toLowerCase()}`);
237
282
  root.style.setProperty(`--md-sys-color-${kebabKey}`, value);
@@ -1457,6 +1502,7 @@ var IconButtonComponent = React11__namespace.forwardRef(
1457
1502
  loading = false,
1458
1503
  loadingVariant = "loading-indicator",
1459
1504
  iconSize,
1505
+ asChild = false,
1460
1506
  children,
1461
1507
  onClick,
1462
1508
  onKeyDown,
@@ -1472,6 +1518,7 @@ var IconButtonComponent = React11__namespace.forwardRef(
1472
1518
  "loading",
1473
1519
  "loadingVariant",
1474
1520
  "iconSize",
1521
+ "asChild",
1475
1522
  "children",
1476
1523
  "onClick",
1477
1524
  "onKeyDown",
@@ -1533,7 +1580,87 @@ var IconButtonComponent = React11__namespace.forwardRef(
1533
1580
  },
1534
1581
  [loading, onClick, onKeyDown]
1535
1582
  );
1536
- return /* @__PURE__ */ jsxRuntime.jsx(react.LazyMotion, { features: react.domMax, strict: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
1583
+ const innerContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1584
+ needsTouchTarget && /* @__PURE__ */ jsxRuntime.jsx(TouchTarget, {}),
1585
+ /* @__PURE__ */ jsxRuntime.jsx(Ripple, { ripples, onRippleDone: removeRipple }),
1586
+ /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { mode: "wait", initial: false, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(
1587
+ react.m.span,
1588
+ __spreadProps(__spreadValues({}, ICON_SPAN_VARIANTS), {
1589
+ transition: SPRING_TRANSITION,
1590
+ className: cn(
1591
+ "flex items-center justify-center shrink-0",
1592
+ iconSize != null ? void 0 : iconClass
1593
+ ),
1594
+ style: isCustomSize ? { width: `${iconSize}px`, height: `${iconSize}px` } : void 0,
1595
+ children: loadingVariant === "loading-indicator" ? /* @__PURE__ */ jsxRuntime.jsx(
1596
+ LoadingIndicator,
1597
+ {
1598
+ size: typeof iconPx === "number" ? iconPx : defaultIconPx,
1599
+ color: "currentColor",
1600
+ "aria-label": "Loading"
1601
+ }
1602
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1603
+ ProgressIndicator,
1604
+ {
1605
+ variant: "circular",
1606
+ size: typeof iconPx === "number" ? iconPx : defaultIconPx,
1607
+ color: "currentColor",
1608
+ trackColor: "transparent",
1609
+ "aria-label": "Loading"
1610
+ }
1611
+ )
1612
+ }),
1613
+ "loading"
1614
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1615
+ react.m.span,
1616
+ __spreadProps(__spreadValues({}, ICON_SPAN_VARIANTS), {
1617
+ transition: SPRING_TRANSITION,
1618
+ "aria-hidden": "true",
1619
+ className: cn(
1620
+ "flex items-center justify-center shrink-0 [&_svg]:w-full [&_svg]:h-full [&_.md-icon]:text-[length:inherit]!",
1621
+ iconSize != null ? void 0 : iconClass
1622
+ ),
1623
+ style: {
1624
+ fontSize: typeof iconPx === "number" ? `${iconPx}px` : iconPx,
1625
+ width: isCustomSize ? `${iconSize}px` : void 0,
1626
+ height: isCustomSize ? `${iconSize}px` : void 0
1627
+ },
1628
+ children: asChild ? React11__namespace.Children.only(children).props.children : children
1629
+ }),
1630
+ "content"
1631
+ ) })
1632
+ ] });
1633
+ const containerClassName = cn(
1634
+ baseIconButtonClasses,
1635
+ resolvedColorClass,
1636
+ outlineWidthClass,
1637
+ disabledBgClass,
1638
+ "overflow-hidden",
1639
+ SIZE_STYLES[size],
1640
+ loading && "pointer-events-none opacity-75 cursor-not-allowed",
1641
+ className
1642
+ );
1643
+ if (asChild) {
1644
+ const child = React11__namespace.Children.only(children);
1645
+ return /* @__PURE__ */ jsxRuntime.jsx(react.LazyMotion, { features: react.domMax, strict: true, children: /* @__PURE__ */ jsxRuntime.jsx(
1646
+ reactSlot.Slot,
1647
+ __spreadProps(__spreadValues({
1648
+ ref,
1649
+ "aria-pressed": isToggle ? isSelected : void 0,
1650
+ "aria-label": ariaLabel,
1651
+ "aria-busy": loading || void 0,
1652
+ "aria-disabled": loading || restProps.disabled,
1653
+ onClick: handleClick,
1654
+ onPointerDown,
1655
+ onKeyDown: handleKeyDown,
1656
+ style: __spreadProps(__spreadValues({}, style), { borderRadius: `${animateRadius}px` }),
1657
+ className: containerClassName
1658
+ }, restProps), {
1659
+ children: React11__namespace.cloneElement(child, { children: innerContent })
1660
+ })
1661
+ ) });
1662
+ }
1663
+ return /* @__PURE__ */ jsxRuntime.jsx(react.LazyMotion, { features: react.domMax, strict: true, children: /* @__PURE__ */ jsxRuntime.jsx(
1537
1664
  react.m.button,
1538
1665
  __spreadProps(__spreadValues({
1539
1666
  ref,
@@ -1549,67 +1676,9 @@ var IconButtonComponent = React11__namespace.forwardRef(
1549
1676
  animate: { borderRadius: animateRadius },
1550
1677
  whileTap: { borderRadius: pressedRadius },
1551
1678
  transition: { borderRadius: SPRING_TRANSITION_FAST },
1552
- className: cn(
1553
- baseIconButtonClasses,
1554
- resolvedColorClass,
1555
- outlineWidthClass,
1556
- disabledBgClass,
1557
- "overflow-hidden",
1558
- SIZE_STYLES[size],
1559
- loading && "pointer-events-none opacity-75 cursor-not-allowed",
1560
- className
1561
- )
1679
+ className: containerClassName
1562
1680
  }, restProps), {
1563
- children: [
1564
- needsTouchTarget && /* @__PURE__ */ jsxRuntime.jsx(TouchTarget, {}),
1565
- /* @__PURE__ */ jsxRuntime.jsx(Ripple, { ripples, onRippleDone: removeRipple }),
1566
- /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { mode: "wait", initial: false, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(
1567
- react.m.span,
1568
- __spreadProps(__spreadValues({}, ICON_SPAN_VARIANTS), {
1569
- transition: SPRING_TRANSITION,
1570
- className: cn(
1571
- "flex items-center justify-center shrink-0",
1572
- iconSize != null ? void 0 : iconClass
1573
- ),
1574
- style: isCustomSize ? { width: `${iconSize}px`, height: `${iconSize}px` } : void 0,
1575
- children: loadingVariant === "loading-indicator" ? /* @__PURE__ */ jsxRuntime.jsx(
1576
- LoadingIndicator,
1577
- {
1578
- size: typeof iconPx === "number" ? iconPx : defaultIconPx,
1579
- color: "currentColor",
1580
- "aria-label": "Loading"
1581
- }
1582
- ) : /* @__PURE__ */ jsxRuntime.jsx(
1583
- ProgressIndicator,
1584
- {
1585
- variant: "circular",
1586
- size: typeof iconPx === "number" ? iconPx : defaultIconPx,
1587
- color: "currentColor",
1588
- trackColor: "transparent",
1589
- "aria-label": "Loading"
1590
- }
1591
- )
1592
- }),
1593
- "loading"
1594
- ) : /* @__PURE__ */ jsxRuntime.jsx(
1595
- react.m.span,
1596
- __spreadProps(__spreadValues({}, ICON_SPAN_VARIANTS), {
1597
- transition: SPRING_TRANSITION,
1598
- "aria-hidden": "true",
1599
- className: cn(
1600
- "flex items-center justify-center shrink-0 [&_svg]:w-full [&_svg]:h-full [&_.md-icon]:text-[length:inherit]!",
1601
- iconSize != null ? void 0 : iconClass
1602
- ),
1603
- style: {
1604
- fontSize: typeof iconPx === "number" ? `${iconPx}px` : iconPx,
1605
- width: isCustomSize ? `${iconSize}px` : void 0,
1606
- height: isCustomSize ? `${iconSize}px` : void 0
1607
- },
1608
- children
1609
- }),
1610
- "content"
1611
- ) })
1612
- ]
1681
+ children: innerContent
1613
1682
  })
1614
1683
  ) });
1615
1684
  }
@@ -2490,6 +2559,8 @@ function MD3ThemeProvider({
2490
2559
  sourceColor: initialSourceColor = "#6750A4",
2491
2560
  defaultMode = "light",
2492
2561
  persistToLocalStorage = false,
2562
+ variant = "expressive",
2563
+ contrastLevel = 0,
2493
2564
  typography: typographyProp,
2494
2565
  fontFamily,
2495
2566
  fontVariationAxes,
@@ -2498,6 +2569,17 @@ function MD3ThemeProvider({
2498
2569
  const [sourceColor, setSourceColor] = React11.useState(initialSourceColor);
2499
2570
  const [mode, setMode] = React11.useState(defaultMode);
2500
2571
  const [isHydrated, setIsHydrated] = React11.useState(!persistToLocalStorage);
2572
+ const [systemMode, setSystemMode] = React11.useState("light");
2573
+ React11.useEffect(() => {
2574
+ if (typeof window === "undefined") return;
2575
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
2576
+ const updateSystemMode = () => {
2577
+ setSystemMode(mediaQuery.matches ? "dark" : "light");
2578
+ };
2579
+ updateSystemMode();
2580
+ mediaQuery.addEventListener("change", updateSystemMode);
2581
+ return () => mediaQuery.removeEventListener("change", updateSystemMode);
2582
+ }, []);
2501
2583
  React11.useEffect(() => {
2502
2584
  if (!persistToLocalStorage) return;
2503
2585
  const savedColor = localStorage.getItem(STORAGE_KEY_COLOR);
@@ -2509,25 +2591,37 @@ function MD3ThemeProvider({
2509
2591
  setMode(savedMode);
2510
2592
  setIsHydrated(true);
2511
2593
  }, [persistToLocalStorage]);
2594
+ const effectiveMode = mode === "system" ? systemMode : mode;
2512
2595
  React11.useEffect(() => {
2513
2596
  if (!isHydrated) return;
2514
- applyTheme(sourceColor, mode);
2597
+ applyTheme(sourceColor, effectiveMode, document.documentElement, {
2598
+ variant,
2599
+ contrastLevel
2600
+ });
2515
2601
  if (persistToLocalStorage) {
2516
2602
  localStorage.setItem(STORAGE_KEY_COLOR, sourceColor);
2517
2603
  localStorage.setItem(STORAGE_KEY_MODE, mode);
2518
2604
  }
2519
- }, [sourceColor, mode, persistToLocalStorage, isHydrated]);
2520
- React11.useEffect(() => {
2521
- if (mode !== "system" || typeof window === "undefined") return;
2522
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
2523
- const handleChange = () => applyTheme(sourceColor, "system");
2524
- mediaQuery.addEventListener("change", handleChange);
2525
- return () => mediaQuery.removeEventListener("change", handleChange);
2526
- }, [mode, sourceColor]);
2527
- const effectiveMode = resolveMode(mode);
2605
+ }, [
2606
+ sourceColor,
2607
+ mode,
2608
+ effectiveMode,
2609
+ persistToLocalStorage,
2610
+ isHydrated,
2611
+ variant,
2612
+ contrastLevel
2613
+ ]);
2528
2614
  const themeValue = React11.useMemo(
2529
- () => ({ sourceColor, setSourceColor, mode, setMode, effectiveMode }),
2530
- [sourceColor, mode, effectiveMode]
2615
+ () => ({
2616
+ sourceColor,
2617
+ setSourceColor,
2618
+ mode,
2619
+ setMode,
2620
+ effectiveMode,
2621
+ variant,
2622
+ contrastLevel
2623
+ }),
2624
+ [sourceColor, mode, effectiveMode, variant, contrastLevel]
2531
2625
  );
2532
2626
  const typographyValue = React11.useMemo(() => {
2533
2627
  if (typographyProp) return typographyProp;