@kbach/react 0.3.4 → 0.3.6

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.
package/dist/index.js CHANGED
@@ -47,10 +47,12 @@ __export(index_exports, {
47
47
  resolve: () => resolve,
48
48
  styled: () => styled,
49
49
  updateConfig: () => updateConfig,
50
+ useBreakpoint: () => useBreakpoint,
50
51
  useColors: () => useColors,
51
52
  useGlobalDarkMode: () => useGlobalDarkMode,
52
53
  useIsDark: () => useIsDark,
53
54
  useResolvedStyle: () => useResolvedStyle,
55
+ useResponsive: () => useResponsive,
54
56
  useStyles: () => useStyles,
55
57
  useTheme: () => useTheme,
56
58
  wrapColors: () => wrapColors
@@ -635,10 +637,14 @@ var defaultTheme = {
635
637
  }
636
638
  },
637
639
  screens: {
638
- sm: 640,
640
+ sm: 390,
641
+ // large phones (iPhone 12+, most Android flagships)
639
642
  md: 768,
643
+ // tablets (iPad and up)
640
644
  lg: 1024,
645
+ // large tablets (iPad Pro)
641
646
  xl: 1280,
647
+ // desktop
642
648
  "2xl": 1536
643
649
  }
644
650
  };
@@ -1966,6 +1972,48 @@ function getPluginStandaloneMap() {
1966
1972
  return PLUGIN_STANDALONE;
1967
1973
  }
1968
1974
 
1975
+ // src/core/responsiveStore.ts
1976
+ var RESPONSIVE_KEY = "__kbach_responsive__";
1977
+ function getStore() {
1978
+ const g = globalThis;
1979
+ if (!g[RESPONSIVE_KEY]) {
1980
+ g[RESPONSIVE_KEY] = { width: 0, screens: {}, listeners: /* @__PURE__ */ new Set() };
1981
+ }
1982
+ return g[RESPONSIVE_KEY];
1983
+ }
1984
+ function syncGlobalWidth(width) {
1985
+ getStore().width = width;
1986
+ }
1987
+ function syncGlobalScreens(screens) {
1988
+ getStore().screens = screens;
1989
+ }
1990
+ function getGlobalScreens() {
1991
+ return getStore().screens;
1992
+ }
1993
+ function setGlobalWidth(width) {
1994
+ const store = getStore();
1995
+ if (store.width === width) return;
1996
+ store.width = width;
1997
+ for (const l of store.listeners) l(width);
1998
+ }
1999
+ function getGlobalWidth() {
2000
+ return getStore().width;
2001
+ }
2002
+ function subscribeGlobalWidth(listener) {
2003
+ const store = getStore();
2004
+ store.listeners.add(listener);
2005
+ return () => store.listeners.delete(listener);
2006
+ }
2007
+ function getActiveBreakpoints(width) {
2008
+ const store = getStore();
2009
+ const w = width ?? store.width;
2010
+ const active = /* @__PURE__ */ new Set();
2011
+ for (const [name, minW] of Object.entries(store.screens)) {
2012
+ if (typeof minW === "number" && w >= minW) active.add(name);
2013
+ }
2014
+ return active;
2015
+ }
2016
+
1969
2017
  // src/core/resolver.ts
1970
2018
  var styleCache = new LRUCache(1e4);
1971
2019
  var _styleEl = null;
@@ -2016,6 +2064,7 @@ var GROUP_PEER_PREFIX = {
2016
2064
  "peer-focus": ".peer:focus ~"
2017
2065
  };
2018
2066
  var MODE_MODS = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
2067
+ var RESPONSIVE_MODS = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
2019
2068
  function injectClassRule(cls, bucketKey, styles, darkMode) {
2020
2069
  const cssDecls = styleValueToCSS(styles);
2021
2070
  if (!cssDecls) return;
@@ -2025,10 +2074,12 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
2025
2074
  return;
2026
2075
  }
2027
2076
  const mods = bucketKey.split(":");
2028
- const hasDark = mods.includes("dark") || mods.includes("not-light");
2029
- const hasLight = mods.includes("light") || mods.includes("not-dark");
2030
- const ancestorMods = mods.filter((m) => m in GROUP_PEER_PREFIX);
2031
- const pseudoMods = mods.filter((m) => !MODE_MODS.has(m) && !(m in GROUP_PEER_PREFIX));
2077
+ const responsiveMods = mods.filter((m) => RESPONSIVE_MODS.has(m));
2078
+ const nonResponsiveMods = mods.filter((m) => !RESPONSIVE_MODS.has(m));
2079
+ const hasDark = nonResponsiveMods.includes("dark") || nonResponsiveMods.includes("not-light");
2080
+ const hasLight = nonResponsiveMods.includes("light") || nonResponsiveMods.includes("not-dark");
2081
+ const ancestorMods = nonResponsiveMods.filter((m) => m in GROUP_PEER_PREFIX);
2082
+ const pseudoMods = nonResponsiveMods.filter((m) => !MODE_MODS.has(m) && !(m in GROUP_PEER_PREFIX));
2032
2083
  const pseudoSuffix = pseudoMods.map((p) => MODIFIER_TO_PSEUDO[p] ?? `:${p}`).join("");
2033
2084
  const elementSelector = `.${escaped}${pseudoSuffix}`;
2034
2085
  const ancestorPrefix = ancestorMods.length ? ancestorMods.map((m) => GROUP_PEER_PREFIX[m]).join(" ") + " " : "";
@@ -2053,6 +2104,13 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
2053
2104
  } else {
2054
2105
  rule = `${selector} { ${cssDecls} }`;
2055
2106
  }
2107
+ if (responsiveMods.length > 0) {
2108
+ const screens = getGlobalScreens();
2109
+ const minWidth = Math.max(...responsiveMods.map((m) => screens[m] ?? 0));
2110
+ if (minWidth > 0) {
2111
+ rule = `@media (min-width: ${minWidth}px) { ${rule} }`;
2112
+ }
2113
+ }
2056
2114
  injectRule(rule);
2057
2115
  }
2058
2116
  var RN_ONLY_PROPS = /* @__PURE__ */ new Set([
@@ -2121,18 +2179,18 @@ function resolve(classString, theme, darkMode = "attribute") {
2121
2179
  }
2122
2180
  return result;
2123
2181
  }
2124
- function flatten(resolved, isDark, state = {}) {
2182
+ function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new Set()) {
2125
2183
  const result = { ...resolved.base ?? {} };
2126
2184
  for (const [key, styles] of Object.entries(resolved)) {
2127
2185
  if (key === "base" || !styles) continue;
2128
2186
  const mods = key.split(":");
2129
- if (matchesMods(mods, isDark, state)) {
2187
+ if (matchesMods(mods, isDark, state, breakpoints)) {
2130
2188
  Object.assign(result, styles);
2131
2189
  }
2132
2190
  }
2133
2191
  return result;
2134
2192
  }
2135
- function matchesMods(mods, isDark, state) {
2193
+ function matchesMods(mods, isDark, state, breakpoints) {
2136
2194
  return mods.every((mod) => {
2137
2195
  switch (mod) {
2138
2196
  case "dark":
@@ -2170,6 +2228,12 @@ function matchesMods(mods, isDark, state) {
2170
2228
  return !state.visited;
2171
2229
  case "placeholder":
2172
2230
  return !!state.placeholder;
2231
+ case "sm":
2232
+ case "md":
2233
+ case "lg":
2234
+ case "xl":
2235
+ case "2xl":
2236
+ return breakpoints.has(mod);
2173
2237
  default:
2174
2238
  return false;
2175
2239
  }
@@ -2329,7 +2393,7 @@ var customVariants = new Proxy(
2329
2393
 
2330
2394
  // src/core/darkModeStore.ts
2331
2395
  var KEY = "__kbach_dark_store__";
2332
- function getStore() {
2396
+ function getStore2() {
2333
2397
  const g = globalThis;
2334
2398
  if (!g[KEY]) {
2335
2399
  g[KEY] = { isDark: false, lastNotified: false, subscribers: /* @__PURE__ */ new Set() };
@@ -2337,20 +2401,20 @@ function getStore() {
2337
2401
  return g[KEY];
2338
2402
  }
2339
2403
  function syncGlobalDarkMode(isDark) {
2340
- getStore().isDark = isDark;
2404
+ getStore2().isDark = isDark;
2341
2405
  }
2342
2406
  function setGlobalDarkMode(isDark) {
2343
- const store = getStore();
2407
+ const store = getStore2();
2344
2408
  store.isDark = isDark;
2345
2409
  if (store.lastNotified === isDark) return;
2346
2410
  store.lastNotified = isDark;
2347
2411
  for (const sub of store.subscribers) sub();
2348
2412
  }
2349
2413
  function getGlobalDarkMode() {
2350
- return getStore().isDark;
2414
+ return getStore2().isDark;
2351
2415
  }
2352
2416
  function subscribeGlobalDarkMode(callback) {
2353
- const store = getStore();
2417
+ const store = getStore2();
2354
2418
  store.subscribers.add(callback);
2355
2419
  return () => store.subscribers.delete(callback);
2356
2420
  }
@@ -2386,12 +2450,36 @@ function ThemeProvider({
2386
2450
  children,
2387
2451
  defaultMode = "system",
2388
2452
  colorScheme,
2453
+ windowWidth: windowWidthProp,
2389
2454
  config: configOverride,
2390
2455
  disablePersistence = false
2391
2456
  }) {
2392
2457
  const [resolvedConfig, setResolvedConfig] = (0, import_react3.useState)(
2393
2458
  () => configOverride ? buildConfig(configOverride) : getConfig()
2394
2459
  );
2460
+ const [webWidth, setWebWidth] = (0, import_react3.useState)(
2461
+ () => isWeb && typeof window !== "undefined" ? window.innerWidth : 0
2462
+ );
2463
+ const effectiveWidth = windowWidthProp ?? (isWeb ? webWidth : 0);
2464
+ syncGlobalWidth(effectiveWidth);
2465
+ syncGlobalScreens(resolvedConfig.theme.screens);
2466
+ (0, import_react3.useEffect)(() => {
2467
+ if (!isWeb || typeof window === "undefined") return;
2468
+ let raf = 0;
2469
+ const update = () => {
2470
+ cancelAnimationFrame(raf);
2471
+ raf = requestAnimationFrame(() => {
2472
+ const w = window.innerWidth;
2473
+ setWebWidth(w);
2474
+ setGlobalWidth(w);
2475
+ });
2476
+ };
2477
+ window.addEventListener("resize", update);
2478
+ return () => {
2479
+ window.removeEventListener("resize", update);
2480
+ cancelAnimationFrame(raf);
2481
+ };
2482
+ }, []);
2395
2483
  const [webScheme, setWebScheme] = (0, import_react3.useState)(() => {
2396
2484
  if (isWeb && typeof window !== "undefined") {
2397
2485
  return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
@@ -2660,33 +2748,59 @@ function styled(Component, baseClasses = "") {
2660
2748
  }
2661
2749
 
2662
2750
  // src/useStyles.ts
2663
- var import_react6 = require("react");
2751
+ var import_react7 = require("react");
2752
+
2753
+ // src/useGlobalWidth.ts
2754
+ var import_react6 = __toESM(require("react"));
2755
+ var useSyncExternalStore = import_react6.default.useSyncExternalStore ?? function useSyncExternalStoreFallback(subscribe, getSnapshot, _getServerSnapshot) {
2756
+ const [, forceUpdate] = import_react6.default.useReducer((n) => n + 1, 0);
2757
+ import_react6.default.useEffect(() => subscribe(forceUpdate), [subscribe]);
2758
+ return getSnapshot();
2759
+ };
2760
+ var NOOP_SUB = (_) => () => {
2761
+ };
2762
+ var ZERO_SNAP = () => 0;
2763
+ var EMPTY_BREAKPOINTS = /* @__PURE__ */ new Set();
2764
+ function useGlobalWidth() {
2765
+ return useSyncExternalStore(subscribeGlobalWidth, getGlobalWidth, ZERO_SNAP);
2766
+ }
2767
+ function useConditionalWidth(active) {
2768
+ return useSyncExternalStore(
2769
+ active ? subscribeGlobalWidth : NOOP_SUB,
2770
+ active ? getGlobalWidth : ZERO_SNAP,
2771
+ ZERO_SNAP
2772
+ );
2773
+ }
2774
+
2775
+ // src/useStyles.ts
2664
2776
  function useStyles(classString, state = {}) {
2665
2777
  const { isDark, config } = useTheme();
2778
+ const width = useGlobalWidth();
2779
+ const breakpoints = isWeb ? EMPTY_BREAKPOINTS : getActiveBreakpoints(width);
2666
2780
  const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
2667
- return (0, import_react6.useMemo)(() => {
2781
+ return (0, import_react7.useMemo)(() => {
2668
2782
  const resolved = resolve(normalised, config.theme, config.darkMode);
2669
- return flatten(resolved, isDark, state);
2670
- }, [normalised, isDark, config, state.hover, state.focus, state.pressed, state.active, state.disabled, state.checked, state.visited, state.placeholder]);
2783
+ return flatten(resolved, isDark, state, breakpoints);
2784
+ }, [normalised, isDark, config, width, state.hover, state.focus, state.pressed, state.active, state.disabled, state.checked, state.visited, state.placeholder]);
2671
2785
  }
2672
2786
  function useResolvedStyle(classString) {
2673
2787
  const { config } = useTheme();
2674
2788
  const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
2675
- return (0, import_react6.useMemo)(
2789
+ return (0, import_react7.useMemo)(
2676
2790
  () => resolve(normalised, config.theme, config.darkMode),
2677
2791
  [normalised, config]
2678
2792
  );
2679
2793
  }
2680
2794
 
2681
2795
  // src/useGlobalDarkMode.ts
2682
- var import_react7 = __toESM(require("react"));
2683
- var useSyncExternalStore = import_react7.default.useSyncExternalStore ?? function useSyncExternalStoreFallback(subscribe, getSnapshot, _getServerSnapshot) {
2684
- const [, forceUpdate] = import_react7.default.useReducer((n) => n + 1, 0);
2685
- import_react7.default.useEffect(() => subscribe(forceUpdate), [subscribe]);
2796
+ var import_react8 = __toESM(require("react"));
2797
+ var useSyncExternalStore2 = import_react8.default.useSyncExternalStore ?? function useSyncExternalStoreFallback2(subscribe, getSnapshot, _getServerSnapshot) {
2798
+ const [, forceUpdate] = import_react8.default.useReducer((n) => n + 1, 0);
2799
+ import_react8.default.useEffect(() => subscribe(forceUpdate), [subscribe]);
2686
2800
  return getSnapshot();
2687
2801
  };
2688
2802
  function useGlobalDarkMode() {
2689
- return useSyncExternalStore(
2803
+ return useSyncExternalStore2(
2690
2804
  subscribeGlobalDarkMode,
2691
2805
  getGlobalDarkMode,
2692
2806
  () => false
@@ -2694,15 +2808,47 @@ function useGlobalDarkMode() {
2694
2808
  );
2695
2809
  }
2696
2810
 
2811
+ // src/useBreakpoint.ts
2812
+ function useBreakpoint() {
2813
+ const { config } = useTheme();
2814
+ const width = useGlobalWidth();
2815
+ const screens = config.theme.screens;
2816
+ const sorted = Object.entries(screens).filter(([, v]) => typeof v === "number").sort(([, a], [, b]) => b - a);
2817
+ for (const [name, minW] of sorted) {
2818
+ if (width >= minW) return name;
2819
+ }
2820
+ return "xs";
2821
+ }
2822
+ function useResponsive() {
2823
+ const { config } = useTheme();
2824
+ const width = useGlobalWidth();
2825
+ const screens = config.theme.screens;
2826
+ const result = {};
2827
+ for (const [name, minW] of Object.entries(screens)) {
2828
+ if (typeof minW === "number") result[name] = width >= minW;
2829
+ }
2830
+ return result;
2831
+ }
2832
+
2697
2833
  // src/InteractiveWrapper.tsx
2698
- var import_react8 = __toESM(require("react"));
2834
+ var import_react9 = __toESM(require("react"));
2699
2835
  function chain(original, extra) {
2700
2836
  return (...args) => {
2701
2837
  original?.(...args);
2702
2838
  extra();
2703
2839
  };
2704
2840
  }
2705
- var InteractiveWrapper = (0, import_react8.forwardRef)(
2841
+ var RESPONSIVE_MODS2 = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
2842
+ function hasResponsiveBuckets(resolved) {
2843
+ for (const key of Object.keys(resolved)) {
2844
+ if (key === "base") continue;
2845
+ for (const mod of key.split(":")) {
2846
+ if (RESPONSIVE_MODS2.has(mod)) return true;
2847
+ }
2848
+ }
2849
+ return false;
2850
+ }
2851
+ var InteractiveWrapper = (0, import_react9.forwardRef)(
2706
2852
  function InteractiveWrapper2({
2707
2853
  Component,
2708
2854
  resolvedStyle,
@@ -2717,18 +2863,22 @@ var InteractiveWrapper = (0, import_react8.forwardRef)(
2717
2863
  ...rest
2718
2864
  }, ref) {
2719
2865
  const isDark = useGlobalDarkMode();
2720
- const [pressed, setPressed] = (0, import_react8.useState)(false);
2721
- const [hovered, setHovered] = (0, import_react8.useState)(false);
2722
- const [focused, setFocused] = (0, import_react8.useState)(false);
2723
- const handlePressIn = (0, import_react8.useCallback)(chain(onPressIn, () => setPressed(true)), [onPressIn]);
2724
- const handlePressOut = (0, import_react8.useCallback)(chain(onPressOut, () => setPressed(false)), [onPressOut]);
2725
- const handleMouseEnter = (0, import_react8.useCallback)(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
2726
- const handleMouseLeave = (0, import_react8.useCallback)(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
2727
- const handleFocus = (0, import_react8.useCallback)(chain(onFocus, () => setFocused(true)), [onFocus]);
2728
- const handleBlur = (0, import_react8.useCallback)(chain(onBlur, () => setFocused(false)), [onBlur]);
2729
- const computedStyle = (0, import_react8.useMemo)(
2730
- () => flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused }),
2731
- [resolvedStyle, isDark, pressed, hovered, focused]
2866
+ const needsWidth = !isWeb && hasResponsiveBuckets(resolvedStyle);
2867
+ const width = useConditionalWidth(needsWidth);
2868
+ const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
2869
+ const [pressed, setPressed] = (0, import_react9.useState)(false);
2870
+ const [hovered, setHovered] = (0, import_react9.useState)(false);
2871
+ const [focused, setFocused] = (0, import_react9.useState)(false);
2872
+ const handlePressIn = (0, import_react9.useCallback)(chain(onPressIn, () => setPressed(true)), [onPressIn]);
2873
+ const handlePressOut = (0, import_react9.useCallback)(chain(onPressOut, () => setPressed(false)), [onPressOut]);
2874
+ const handleMouseEnter = (0, import_react9.useCallback)(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
2875
+ const handleMouseLeave = (0, import_react9.useCallback)(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
2876
+ const handleFocus = (0, import_react9.useCallback)(chain(onFocus, () => setFocused(true)), [onFocus]);
2877
+ const handleBlur = (0, import_react9.useCallback)(chain(onBlur, () => setFocused(false)), [onBlur]);
2878
+ const computedStyle = (0, import_react9.useMemo)(
2879
+ () => flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused }, breakpoints),
2880
+ [resolvedStyle, isDark, pressed, hovered, focused, width]
2881
+ // eslint-disable-line react-hooks/exhaustive-deps
2732
2882
  );
2733
2883
  const finalStyle = styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
2734
2884
  const { children, ...restWithoutChildren } = rest;
@@ -2744,7 +2894,7 @@ var InteractiveWrapper = (0, import_react8.forwardRef)(
2744
2894
  onFocus: handleFocus,
2745
2895
  onBlur: handleBlur
2746
2896
  };
2747
- return Array.isArray(children) ? import_react8.default.createElement(Component, componentProps, ...children) : import_react8.default.createElement(Component, componentProps, children);
2897
+ return Array.isArray(children) ? import_react9.default.createElement(Component, componentProps, ...children) : import_react9.default.createElement(Component, componentProps, children);
2748
2898
  }
2749
2899
  );
2750
2900
  InteractiveWrapper.displayName = "Kbach.InteractiveWrapper";
@@ -2780,10 +2930,12 @@ function cx(...classes) {
2780
2930
  resolve,
2781
2931
  styled,
2782
2932
  updateConfig,
2933
+ useBreakpoint,
2783
2934
  useColors,
2784
2935
  useGlobalDarkMode,
2785
2936
  useIsDark,
2786
2937
  useResolvedStyle,
2938
+ useResponsive,
2787
2939
  useStyles,
2788
2940
  useTheme,
2789
2941
  wrapColors
package/dist/index.mjs CHANGED
@@ -1,10 +1,12 @@
1
1
  import {
2
+ EMPTY_BREAKPOINTS,
2
3
  InteractiveWrapper,
3
4
  __require,
4
5
  buildConfig,
5
6
  defaultColors,
6
7
  defaultTheme,
7
8
  flatten,
9
+ getActiveBreakpoints,
8
10
  getConfig,
9
11
  initConfig,
10
12
  isWeb,
@@ -13,10 +15,14 @@ import {
13
15
  parseClasses,
14
16
  resolve,
15
17
  setGlobalDarkMode,
18
+ setGlobalWidth,
16
19
  syncGlobalDarkMode,
20
+ syncGlobalScreens,
21
+ syncGlobalWidth,
17
22
  updateConfig,
18
- useGlobalDarkMode
19
- } from "./chunk-ADRZPFQC.mjs";
23
+ useGlobalDarkMode,
24
+ useGlobalWidth
25
+ } from "./chunk-UFSME6UC.mjs";
20
26
 
21
27
  // src/context.tsx
22
28
  import { createContext, useContext } from "react";
@@ -129,12 +135,36 @@ function ThemeProvider({
129
135
  children,
130
136
  defaultMode = "system",
131
137
  colorScheme,
138
+ windowWidth: windowWidthProp,
132
139
  config: configOverride,
133
140
  disablePersistence = false
134
141
  }) {
135
142
  const [resolvedConfig, setResolvedConfig] = useState(
136
143
  () => configOverride ? buildConfig(configOverride) : getConfig()
137
144
  );
145
+ const [webWidth, setWebWidth] = useState(
146
+ () => isWeb && typeof window !== "undefined" ? window.innerWidth : 0
147
+ );
148
+ const effectiveWidth = windowWidthProp ?? (isWeb ? webWidth : 0);
149
+ syncGlobalWidth(effectiveWidth);
150
+ syncGlobalScreens(resolvedConfig.theme.screens);
151
+ useEffect(() => {
152
+ if (!isWeb || typeof window === "undefined") return;
153
+ let raf = 0;
154
+ const update = () => {
155
+ cancelAnimationFrame(raf);
156
+ raf = requestAnimationFrame(() => {
157
+ const w = window.innerWidth;
158
+ setWebWidth(w);
159
+ setGlobalWidth(w);
160
+ });
161
+ };
162
+ window.addEventListener("resize", update);
163
+ return () => {
164
+ window.removeEventListener("resize", update);
165
+ cancelAnimationFrame(raf);
166
+ };
167
+ }, []);
138
168
  const [webScheme, setWebScheme] = useState(() => {
139
169
  if (isWeb && typeof window !== "undefined") {
140
170
  return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
@@ -411,11 +441,13 @@ function styled(Component, baseClasses = "") {
411
441
  import { useMemo as useMemo4 } from "react";
412
442
  function useStyles(classString, state = {}) {
413
443
  const { isDark, config } = useTheme();
444
+ const width = useGlobalWidth();
445
+ const breakpoints = isWeb ? EMPTY_BREAKPOINTS : getActiveBreakpoints(width);
414
446
  const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
415
447
  return useMemo4(() => {
416
448
  const resolved = resolve(normalised, config.theme, config.darkMode);
417
- return flatten(resolved, isDark, state);
418
- }, [normalised, isDark, config, state.hover, state.focus, state.pressed, state.active, state.disabled, state.checked, state.visited, state.placeholder]);
449
+ return flatten(resolved, isDark, state, breakpoints);
450
+ }, [normalised, isDark, config, width, state.hover, state.focus, state.pressed, state.active, state.disabled, state.checked, state.visited, state.placeholder]);
419
451
  }
420
452
  function useResolvedStyle(classString) {
421
453
  const { config } = useTheme();
@@ -426,6 +458,28 @@ function useResolvedStyle(classString) {
426
458
  );
427
459
  }
428
460
 
461
+ // src/useBreakpoint.ts
462
+ function useBreakpoint() {
463
+ const { config } = useTheme();
464
+ const width = useGlobalWidth();
465
+ const screens = config.theme.screens;
466
+ const sorted = Object.entries(screens).filter(([, v]) => typeof v === "number").sort(([, a], [, b]) => b - a);
467
+ for (const [name, minW] of sorted) {
468
+ if (width >= minW) return name;
469
+ }
470
+ return "xs";
471
+ }
472
+ function useResponsive() {
473
+ const { config } = useTheme();
474
+ const width = useGlobalWidth();
475
+ const screens = config.theme.screens;
476
+ const result = {};
477
+ for (const [name, minW] of Object.entries(screens)) {
478
+ if (typeof minW === "number") result[name] = width >= minW;
479
+ }
480
+ return result;
481
+ }
482
+
429
483
  // src/kb.ts
430
484
  function kb(classString, isDark = false) {
431
485
  const config = getConfig();
@@ -456,10 +510,12 @@ export {
456
510
  resolve,
457
511
  styled,
458
512
  updateConfig,
513
+ useBreakpoint,
459
514
  useColors,
460
515
  useGlobalDarkMode,
461
516
  useIsDark,
462
517
  useResolvedStyle,
518
+ useResponsive,
463
519
  useStyles,
464
520
  useTheme,
465
521
  wrapColors