@kbach/react 0.2.5 → 0.2.7

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.
@@ -1043,6 +1043,25 @@ function resolveColor(value, colors, isArbitrary) {
1043
1043
  }
1044
1044
  }
1045
1045
  }
1046
+ if (hex && !hex.startsWith("#") && !hex.startsWith("rgb") && hex !== "transparent" && hex !== "currentColor") {
1047
+ if (hex in colors) {
1048
+ const alias = colors[hex];
1049
+ if (typeof alias === "string") hex = alias;
1050
+ else if (typeof alias === "object" && "6" in alias) hex = alias["6"] ?? hex;
1051
+ } else {
1052
+ const aliasLastDash = hex.lastIndexOf("-");
1053
+ if (aliasLastDash > 0) {
1054
+ const aliasColorName = hex.slice(0, aliasLastDash);
1055
+ const aliasShade = hex.slice(aliasLastDash + 1);
1056
+ const aliasScale = colors[aliasColorName];
1057
+ if (aliasScale && typeof aliasScale === "object") {
1058
+ hex = aliasScale[aliasShade] ?? null;
1059
+ } else if (typeof aliasScale === "string") {
1060
+ hex = aliasScale;
1061
+ }
1062
+ }
1063
+ }
1064
+ }
1046
1065
  if (!hex) return null;
1047
1066
  if (!opacityPart) return hex;
1048
1067
  let alpha;
@@ -1664,6 +1683,45 @@ var RESOLVERS = {
1664
1683
  const ms = isArbitrary ? value : `${value}ms`;
1665
1684
  return { transitionDelay: ms };
1666
1685
  },
1686
+ // ── Ring (web-only: box-shadow outline ring) ──────────────────────────────
1687
+ ring: ({ value, isArbitrary }, { colors }) => {
1688
+ if (!isWeb) return null;
1689
+ const DEFAULT_COLOR = "rgba(59, 130, 246, 0.5)";
1690
+ const DEFAULT_WIDTH = 3;
1691
+ if (!value) {
1692
+ return { boxShadow: `0 0 0 ${DEFAULT_WIDTH}px ${DEFAULT_COLOR}` };
1693
+ }
1694
+ if (value === "inset") {
1695
+ return { boxShadow: `inset 0 0 0 ${DEFAULT_WIDTH}px ${DEFAULT_COLOR}` };
1696
+ }
1697
+ const widthTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
1698
+ if (!isArbitrary && value in widthTokens) {
1699
+ const w = widthTokens[value];
1700
+ return { boxShadow: w === 0 ? "none" : `0 0 0 ${w}px ${DEFAULT_COLOR}` };
1701
+ }
1702
+ if (isArbitrary) {
1703
+ const numMatch = /^(\d+(?:\.\d+)?)(px|rem|em|vw|vh)?$/.exec(value);
1704
+ if (numMatch) {
1705
+ const unit = numMatch[2] ?? "px";
1706
+ return { boxShadow: `0 0 0 ${numMatch[1]}${unit} ${DEFAULT_COLOR}` };
1707
+ }
1708
+ return { boxShadow: value.replace(/_/g, " ") };
1709
+ }
1710
+ const color = resolveColor(value, colors, false);
1711
+ if (color) return { boxShadow: `0 0 0 ${DEFAULT_WIDTH}px ${color}` };
1712
+ return null;
1713
+ },
1714
+ "ring-offset": ({ value, isArbitrary }, _theme) => {
1715
+ if (!isWeb) return null;
1716
+ const DEFAULT_RING_COLOR = "rgba(59, 130, 246, 0.5)";
1717
+ const DEFAULT_RING_WIDTH = 3;
1718
+ const offsetTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
1719
+ const offsetWidth = !isArbitrary ? value in offsetTokens ? offsetTokens[value] : null : parseFloat(value);
1720
+ if (offsetWidth === null || isNaN(offsetWidth)) return null;
1721
+ return {
1722
+ boxShadow: offsetWidth === 0 ? `0 0 0 ${DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}` : `0 0 0 ${offsetWidth}px #fff, 0 0 0 ${offsetWidth + DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}`
1723
+ };
1724
+ },
1667
1725
  // ── Space between (uses CSS gap or margin fallback) ────────────────────────
1668
1726
  "space-x": ({ value, negative, isArbitrary }, { spacing }) => {
1669
1727
  const v = resolveSpacing(value, negative, spacing, isArbitrary);
@@ -1741,42 +1799,35 @@ function injectRule(rule) {
1741
1799
  _injectedRules.add(rule);
1742
1800
  getStyleEl().sheet?.insertRule(rule, getStyleEl().sheet.cssRules.length);
1743
1801
  }
1744
- function injectCSS(classString, resolved, darkMode) {
1745
- if (!isWeb) return;
1746
- for (const [modifierKey, styles] of Object.entries(resolved)) {
1747
- if (!styles || Object.keys(styles).length === 0) continue;
1748
- const classes = classString.trim().split(/\s+/);
1749
- for (const cls of classes) {
1750
- const cssDecls = styleValueToCSS(styles);
1751
- if (!cssDecls) continue;
1752
- const escaped = escapeCSSSelector(cls);
1753
- if (modifierKey === "base") {
1754
- injectRule(`.${escaped} { ${cssDecls} }`);
1755
- continue;
1756
- }
1757
- const mods = modifierKey.split(":");
1758
- const pseudos = mods.filter((m) => !["dark", "light"].includes(m));
1759
- const hasDark = mods.includes("dark");
1760
- const pseudoSuffix = pseudos.map((p) => `:${p}`).join("");
1761
- let selector = `.${escaped}${pseudoSuffix}`;
1762
- let rule;
1763
- if (hasDark) {
1764
- if (darkMode === "media") {
1765
- rule = `@media (prefers-color-scheme: dark) { ${selector} { ${cssDecls} } }`;
1766
- } else if (darkMode === "class") {
1767
- rule = `.dark ${selector} { ${cssDecls} }`;
1768
- } else {
1769
- rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
1770
- }
1771
- } else {
1772
- rule = `${selector} { ${cssDecls} }`;
1773
- }
1774
- injectRule(rule);
1802
+ function injectClassRule(cls, bucketKey, styles, darkMode) {
1803
+ const cssDecls = styleValueToCSS(styles);
1804
+ if (!cssDecls) return;
1805
+ const escaped = escapeCSSSelector(cls);
1806
+ if (bucketKey === "base") {
1807
+ injectRule(`.${escaped} { ${cssDecls} }`);
1808
+ return;
1809
+ }
1810
+ const mods = bucketKey.split(":");
1811
+ const pseudos = mods.filter((m) => !["dark", "light"].includes(m));
1812
+ const hasDark = mods.includes("dark");
1813
+ const pseudoSuffix = pseudos.map((p) => `:${p}`).join("");
1814
+ const selector = `.${escaped}${pseudoSuffix}`;
1815
+ let rule;
1816
+ if (hasDark) {
1817
+ if (darkMode === "media") {
1818
+ rule = `@media (prefers-color-scheme: dark) { ${selector} { ${cssDecls} } }`;
1819
+ } else if (darkMode === "class") {
1820
+ rule = `.dark ${selector} { ${cssDecls} }`;
1821
+ } else {
1822
+ rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
1775
1823
  }
1824
+ } else {
1825
+ rule = `${selector} { ${cssDecls} }`;
1776
1826
  }
1827
+ injectRule(rule);
1777
1828
  }
1778
1829
  function styleValueToCSS(styles) {
1779
- return Object.entries(styles).filter(([, v]) => v !== void 0 && v !== null).map(([prop, val]) => `${camelToKebab(prop)}: ${val}`).join("; ");
1830
+ return Object.entries(styles).filter(([, v]) => v !== void 0 && v !== null && typeof v !== "object").map(([prop, val]) => `${camelToKebab(prop)}: ${val}`).join("; ");
1780
1831
  }
1781
1832
  function camelToKebab(str) {
1782
1833
  return str.replace(/([A-Z])/g, "-$1").toLowerCase();
@@ -1787,16 +1838,24 @@ function resolve(classString, theme, darkMode = "attribute") {
1787
1838
  if (cached) return cached;
1788
1839
  const result = {};
1789
1840
  const parsedClasses = parseClasses(classString);
1841
+ const injectQueue = [];
1790
1842
  for (const parsed of parsedClasses) {
1791
1843
  const styles = resolveUtility(parsed, theme);
1792
1844
  if (!styles) continue;
1793
1845
  const bucketKey = parsed.modifiers.length === 0 ? "base" : parsed.modifiers.join(":");
1794
1846
  if (!result[bucketKey]) result[bucketKey] = {};
1795
1847
  Object.assign(result[bucketKey], styles);
1848
+ if (isWeb) {
1849
+ injectQueue.push({ cls: parsed.original, bucketKey, styles });
1850
+ }
1796
1851
  }
1797
1852
  styleCache.set(cacheKey, result);
1798
- if (isWeb) {
1799
- Promise.resolve().then(() => injectCSS(classString, result, darkMode));
1853
+ if (isWeb && injectQueue.length > 0) {
1854
+ Promise.resolve().then(() => {
1855
+ for (const { cls, bucketKey, styles } of injectQueue) {
1856
+ injectClassRule(cls, bucketKey, styles, darkMode);
1857
+ }
1858
+ });
1800
1859
  }
1801
1860
  return result;
1802
1861
  }
@@ -5,7 +5,7 @@ import {
5
5
  isWeb,
6
6
  resolve,
7
7
  useGlobalDarkMode
8
- } from "./chunk-IYY3U6L2.mjs";
8
+ } from "./chunk-LYGD2GSW.mjs";
9
9
 
10
10
  // src/jsx-runtime.tsx
11
11
  import { jsx as _jsx, jsxs as _jsxs, Fragment } from "react/jsx-runtime";
package/dist/index.js CHANGED
@@ -1143,6 +1143,25 @@ function resolveColor(value, colors, isArbitrary) {
1143
1143
  }
1144
1144
  }
1145
1145
  }
1146
+ if (hex && !hex.startsWith("#") && !hex.startsWith("rgb") && hex !== "transparent" && hex !== "currentColor") {
1147
+ if (hex in colors) {
1148
+ const alias = colors[hex];
1149
+ if (typeof alias === "string") hex = alias;
1150
+ else if (typeof alias === "object" && "6" in alias) hex = alias["6"] ?? hex;
1151
+ } else {
1152
+ const aliasLastDash = hex.lastIndexOf("-");
1153
+ if (aliasLastDash > 0) {
1154
+ const aliasColorName = hex.slice(0, aliasLastDash);
1155
+ const aliasShade = hex.slice(aliasLastDash + 1);
1156
+ const aliasScale = colors[aliasColorName];
1157
+ if (aliasScale && typeof aliasScale === "object") {
1158
+ hex = aliasScale[aliasShade] ?? null;
1159
+ } else if (typeof aliasScale === "string") {
1160
+ hex = aliasScale;
1161
+ }
1162
+ }
1163
+ }
1164
+ }
1146
1165
  if (!hex) return null;
1147
1166
  if (!opacityPart) return hex;
1148
1167
  let alpha;
@@ -1764,6 +1783,45 @@ var RESOLVERS = {
1764
1783
  const ms = isArbitrary ? value : `${value}ms`;
1765
1784
  return { transitionDelay: ms };
1766
1785
  },
1786
+ // ── Ring (web-only: box-shadow outline ring) ──────────────────────────────
1787
+ ring: ({ value, isArbitrary }, { colors }) => {
1788
+ if (!isWeb) return null;
1789
+ const DEFAULT_COLOR = "rgba(59, 130, 246, 0.5)";
1790
+ const DEFAULT_WIDTH = 3;
1791
+ if (!value) {
1792
+ return { boxShadow: `0 0 0 ${DEFAULT_WIDTH}px ${DEFAULT_COLOR}` };
1793
+ }
1794
+ if (value === "inset") {
1795
+ return { boxShadow: `inset 0 0 0 ${DEFAULT_WIDTH}px ${DEFAULT_COLOR}` };
1796
+ }
1797
+ const widthTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
1798
+ if (!isArbitrary && value in widthTokens) {
1799
+ const w = widthTokens[value];
1800
+ return { boxShadow: w === 0 ? "none" : `0 0 0 ${w}px ${DEFAULT_COLOR}` };
1801
+ }
1802
+ if (isArbitrary) {
1803
+ const numMatch = /^(\d+(?:\.\d+)?)(px|rem|em|vw|vh)?$/.exec(value);
1804
+ if (numMatch) {
1805
+ const unit = numMatch[2] ?? "px";
1806
+ return { boxShadow: `0 0 0 ${numMatch[1]}${unit} ${DEFAULT_COLOR}` };
1807
+ }
1808
+ return { boxShadow: value.replace(/_/g, " ") };
1809
+ }
1810
+ const color = resolveColor(value, colors, false);
1811
+ if (color) return { boxShadow: `0 0 0 ${DEFAULT_WIDTH}px ${color}` };
1812
+ return null;
1813
+ },
1814
+ "ring-offset": ({ value, isArbitrary }, _theme) => {
1815
+ if (!isWeb) return null;
1816
+ const DEFAULT_RING_COLOR = "rgba(59, 130, 246, 0.5)";
1817
+ const DEFAULT_RING_WIDTH = 3;
1818
+ const offsetTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
1819
+ const offsetWidth = !isArbitrary ? value in offsetTokens ? offsetTokens[value] : null : parseFloat(value);
1820
+ if (offsetWidth === null || isNaN(offsetWidth)) return null;
1821
+ return {
1822
+ boxShadow: offsetWidth === 0 ? `0 0 0 ${DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}` : `0 0 0 ${offsetWidth}px #fff, 0 0 0 ${offsetWidth + DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}`
1823
+ };
1824
+ },
1767
1825
  // ── Space between (uses CSS gap or margin fallback) ────────────────────────
1768
1826
  "space-x": ({ value, negative, isArbitrary }, { spacing }) => {
1769
1827
  const v = resolveSpacing(value, negative, spacing, isArbitrary);
@@ -1805,42 +1863,35 @@ function injectRule(rule) {
1805
1863
  _injectedRules.add(rule);
1806
1864
  getStyleEl().sheet?.insertRule(rule, getStyleEl().sheet.cssRules.length);
1807
1865
  }
1808
- function injectCSS(classString, resolved, darkMode) {
1809
- if (!isWeb) return;
1810
- for (const [modifierKey, styles] of Object.entries(resolved)) {
1811
- if (!styles || Object.keys(styles).length === 0) continue;
1812
- const classes = classString.trim().split(/\s+/);
1813
- for (const cls of classes) {
1814
- const cssDecls = styleValueToCSS(styles);
1815
- if (!cssDecls) continue;
1816
- const escaped = escapeCSSSelector(cls);
1817
- if (modifierKey === "base") {
1818
- injectRule(`.${escaped} { ${cssDecls} }`);
1819
- continue;
1820
- }
1821
- const mods = modifierKey.split(":");
1822
- const pseudos = mods.filter((m) => !["dark", "light"].includes(m));
1823
- const hasDark = mods.includes("dark");
1824
- const pseudoSuffix = pseudos.map((p) => `:${p}`).join("");
1825
- let selector = `.${escaped}${pseudoSuffix}`;
1826
- let rule;
1827
- if (hasDark) {
1828
- if (darkMode === "media") {
1829
- rule = `@media (prefers-color-scheme: dark) { ${selector} { ${cssDecls} } }`;
1830
- } else if (darkMode === "class") {
1831
- rule = `.dark ${selector} { ${cssDecls} }`;
1832
- } else {
1833
- rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
1834
- }
1835
- } else {
1836
- rule = `${selector} { ${cssDecls} }`;
1837
- }
1838
- injectRule(rule);
1866
+ function injectClassRule(cls, bucketKey, styles, darkMode) {
1867
+ const cssDecls = styleValueToCSS(styles);
1868
+ if (!cssDecls) return;
1869
+ const escaped = escapeCSSSelector(cls);
1870
+ if (bucketKey === "base") {
1871
+ injectRule(`.${escaped} { ${cssDecls} }`);
1872
+ return;
1873
+ }
1874
+ const mods = bucketKey.split(":");
1875
+ const pseudos = mods.filter((m) => !["dark", "light"].includes(m));
1876
+ const hasDark = mods.includes("dark");
1877
+ const pseudoSuffix = pseudos.map((p) => `:${p}`).join("");
1878
+ const selector = `.${escaped}${pseudoSuffix}`;
1879
+ let rule;
1880
+ if (hasDark) {
1881
+ if (darkMode === "media") {
1882
+ rule = `@media (prefers-color-scheme: dark) { ${selector} { ${cssDecls} } }`;
1883
+ } else if (darkMode === "class") {
1884
+ rule = `.dark ${selector} { ${cssDecls} }`;
1885
+ } else {
1886
+ rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
1839
1887
  }
1888
+ } else {
1889
+ rule = `${selector} { ${cssDecls} }`;
1840
1890
  }
1891
+ injectRule(rule);
1841
1892
  }
1842
1893
  function styleValueToCSS(styles) {
1843
- return Object.entries(styles).filter(([, v]) => v !== void 0 && v !== null).map(([prop, val]) => `${camelToKebab(prop)}: ${val}`).join("; ");
1894
+ return Object.entries(styles).filter(([, v]) => v !== void 0 && v !== null && typeof v !== "object").map(([prop, val]) => `${camelToKebab(prop)}: ${val}`).join("; ");
1844
1895
  }
1845
1896
  function camelToKebab(str) {
1846
1897
  return str.replace(/([A-Z])/g, "-$1").toLowerCase();
@@ -1851,16 +1902,24 @@ function resolve(classString, theme, darkMode = "attribute") {
1851
1902
  if (cached) return cached;
1852
1903
  const result = {};
1853
1904
  const parsedClasses = parseClasses(classString);
1905
+ const injectQueue = [];
1854
1906
  for (const parsed of parsedClasses) {
1855
1907
  const styles = resolveUtility(parsed, theme);
1856
1908
  if (!styles) continue;
1857
1909
  const bucketKey = parsed.modifiers.length === 0 ? "base" : parsed.modifiers.join(":");
1858
1910
  if (!result[bucketKey]) result[bucketKey] = {};
1859
1911
  Object.assign(result[bucketKey], styles);
1912
+ if (isWeb) {
1913
+ injectQueue.push({ cls: parsed.original, bucketKey, styles });
1914
+ }
1860
1915
  }
1861
1916
  styleCache.set(cacheKey, result);
1862
- if (isWeb) {
1863
- Promise.resolve().then(() => injectCSS(classString, result, darkMode));
1917
+ if (isWeb && injectQueue.length > 0) {
1918
+ Promise.resolve().then(() => {
1919
+ for (const { cls, bucketKey, styles } of injectQueue) {
1920
+ injectClassRule(cls, bucketKey, styles, darkMode);
1921
+ }
1922
+ });
1864
1923
  }
1865
1924
  return result;
1866
1925
  }
package/dist/index.mjs CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  syncGlobalDarkMode,
16
16
  updateConfig,
17
17
  useGlobalDarkMode
18
- } from "./chunk-IYY3U6L2.mjs";
18
+ } from "./chunk-LYGD2GSW.mjs";
19
19
 
20
20
  // src/context.tsx
21
21
  import { createContext, useContext } from "react";
@@ -1112,6 +1112,25 @@ function resolveColor(value, colors, isArbitrary) {
1112
1112
  }
1113
1113
  }
1114
1114
  }
1115
+ if (hex && !hex.startsWith("#") && !hex.startsWith("rgb") && hex !== "transparent" && hex !== "currentColor") {
1116
+ if (hex in colors) {
1117
+ const alias = colors[hex];
1118
+ if (typeof alias === "string") hex = alias;
1119
+ else if (typeof alias === "object" && "6" in alias) hex = alias["6"] ?? hex;
1120
+ } else {
1121
+ const aliasLastDash = hex.lastIndexOf("-");
1122
+ if (aliasLastDash > 0) {
1123
+ const aliasColorName = hex.slice(0, aliasLastDash);
1124
+ const aliasShade = hex.slice(aliasLastDash + 1);
1125
+ const aliasScale = colors[aliasColorName];
1126
+ if (aliasScale && typeof aliasScale === "object") {
1127
+ hex = aliasScale[aliasShade] ?? null;
1128
+ } else if (typeof aliasScale === "string") {
1129
+ hex = aliasScale;
1130
+ }
1131
+ }
1132
+ }
1133
+ }
1115
1134
  if (!hex) return null;
1116
1135
  if (!opacityPart) return hex;
1117
1136
  let alpha;
@@ -1733,6 +1752,45 @@ var RESOLVERS = {
1733
1752
  const ms = isArbitrary ? value : `${value}ms`;
1734
1753
  return { transitionDelay: ms };
1735
1754
  },
1755
+ // ── Ring (web-only: box-shadow outline ring) ──────────────────────────────
1756
+ ring: ({ value, isArbitrary }, { colors }) => {
1757
+ if (!isWeb) return null;
1758
+ const DEFAULT_COLOR = "rgba(59, 130, 246, 0.5)";
1759
+ const DEFAULT_WIDTH = 3;
1760
+ if (!value) {
1761
+ return { boxShadow: `0 0 0 ${DEFAULT_WIDTH}px ${DEFAULT_COLOR}` };
1762
+ }
1763
+ if (value === "inset") {
1764
+ return { boxShadow: `inset 0 0 0 ${DEFAULT_WIDTH}px ${DEFAULT_COLOR}` };
1765
+ }
1766
+ const widthTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
1767
+ if (!isArbitrary && value in widthTokens) {
1768
+ const w = widthTokens[value];
1769
+ return { boxShadow: w === 0 ? "none" : `0 0 0 ${w}px ${DEFAULT_COLOR}` };
1770
+ }
1771
+ if (isArbitrary) {
1772
+ const numMatch = /^(\d+(?:\.\d+)?)(px|rem|em|vw|vh)?$/.exec(value);
1773
+ if (numMatch) {
1774
+ const unit = numMatch[2] ?? "px";
1775
+ return { boxShadow: `0 0 0 ${numMatch[1]}${unit} ${DEFAULT_COLOR}` };
1776
+ }
1777
+ return { boxShadow: value.replace(/_/g, " ") };
1778
+ }
1779
+ const color = resolveColor(value, colors, false);
1780
+ if (color) return { boxShadow: `0 0 0 ${DEFAULT_WIDTH}px ${color}` };
1781
+ return null;
1782
+ },
1783
+ "ring-offset": ({ value, isArbitrary }, _theme) => {
1784
+ if (!isWeb) return null;
1785
+ const DEFAULT_RING_COLOR = "rgba(59, 130, 246, 0.5)";
1786
+ const DEFAULT_RING_WIDTH = 3;
1787
+ const offsetTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
1788
+ const offsetWidth = !isArbitrary ? value in offsetTokens ? offsetTokens[value] : null : parseFloat(value);
1789
+ if (offsetWidth === null || isNaN(offsetWidth)) return null;
1790
+ return {
1791
+ boxShadow: offsetWidth === 0 ? `0 0 0 ${DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}` : `0 0 0 ${offsetWidth}px #fff, 0 0 0 ${offsetWidth + DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}`
1792
+ };
1793
+ },
1736
1794
  // ── Space between (uses CSS gap or margin fallback) ────────────────────────
1737
1795
  "space-x": ({ value, negative, isArbitrary }, { spacing }) => {
1738
1796
  const v = resolveSpacing(value, negative, spacing, isArbitrary);
@@ -1774,42 +1832,35 @@ function injectRule(rule) {
1774
1832
  _injectedRules.add(rule);
1775
1833
  getStyleEl().sheet?.insertRule(rule, getStyleEl().sheet.cssRules.length);
1776
1834
  }
1777
- function injectCSS(classString, resolved, darkMode) {
1778
- if (!isWeb) return;
1779
- for (const [modifierKey, styles] of Object.entries(resolved)) {
1780
- if (!styles || Object.keys(styles).length === 0) continue;
1781
- const classes = classString.trim().split(/\s+/);
1782
- for (const cls of classes) {
1783
- const cssDecls = styleValueToCSS(styles);
1784
- if (!cssDecls) continue;
1785
- const escaped = escapeCSSSelector(cls);
1786
- if (modifierKey === "base") {
1787
- injectRule(`.${escaped} { ${cssDecls} }`);
1788
- continue;
1789
- }
1790
- const mods = modifierKey.split(":");
1791
- const pseudos = mods.filter((m) => !["dark", "light"].includes(m));
1792
- const hasDark = mods.includes("dark");
1793
- const pseudoSuffix = pseudos.map((p) => `:${p}`).join("");
1794
- let selector = `.${escaped}${pseudoSuffix}`;
1795
- let rule;
1796
- if (hasDark) {
1797
- if (darkMode === "media") {
1798
- rule = `@media (prefers-color-scheme: dark) { ${selector} { ${cssDecls} } }`;
1799
- } else if (darkMode === "class") {
1800
- rule = `.dark ${selector} { ${cssDecls} }`;
1801
- } else {
1802
- rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
1803
- }
1804
- } else {
1805
- rule = `${selector} { ${cssDecls} }`;
1806
- }
1807
- injectRule(rule);
1835
+ function injectClassRule(cls, bucketKey, styles, darkMode) {
1836
+ const cssDecls = styleValueToCSS(styles);
1837
+ if (!cssDecls) return;
1838
+ const escaped = escapeCSSSelector(cls);
1839
+ if (bucketKey === "base") {
1840
+ injectRule(`.${escaped} { ${cssDecls} }`);
1841
+ return;
1842
+ }
1843
+ const mods = bucketKey.split(":");
1844
+ const pseudos = mods.filter((m) => !["dark", "light"].includes(m));
1845
+ const hasDark = mods.includes("dark");
1846
+ const pseudoSuffix = pseudos.map((p) => `:${p}`).join("");
1847
+ const selector = `.${escaped}${pseudoSuffix}`;
1848
+ let rule;
1849
+ if (hasDark) {
1850
+ if (darkMode === "media") {
1851
+ rule = `@media (prefers-color-scheme: dark) { ${selector} { ${cssDecls} } }`;
1852
+ } else if (darkMode === "class") {
1853
+ rule = `.dark ${selector} { ${cssDecls} }`;
1854
+ } else {
1855
+ rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
1808
1856
  }
1857
+ } else {
1858
+ rule = `${selector} { ${cssDecls} }`;
1809
1859
  }
1860
+ injectRule(rule);
1810
1861
  }
1811
1862
  function styleValueToCSS(styles) {
1812
- return Object.entries(styles).filter(([, v]) => v !== void 0 && v !== null).map(([prop, val]) => `${camelToKebab(prop)}: ${val}`).join("; ");
1863
+ return Object.entries(styles).filter(([, v]) => v !== void 0 && v !== null && typeof v !== "object").map(([prop, val]) => `${camelToKebab(prop)}: ${val}`).join("; ");
1813
1864
  }
1814
1865
  function camelToKebab(str) {
1815
1866
  return str.replace(/([A-Z])/g, "-$1").toLowerCase();
@@ -1820,16 +1871,24 @@ function resolve(classString, theme, darkMode = "attribute") {
1820
1871
  if (cached) return cached;
1821
1872
  const result = {};
1822
1873
  const parsedClasses = parseClasses(classString);
1874
+ const injectQueue = [];
1823
1875
  for (const parsed of parsedClasses) {
1824
1876
  const styles = resolveUtility(parsed, theme);
1825
1877
  if (!styles) continue;
1826
1878
  const bucketKey = parsed.modifiers.length === 0 ? "base" : parsed.modifiers.join(":");
1827
1879
  if (!result[bucketKey]) result[bucketKey] = {};
1828
1880
  Object.assign(result[bucketKey], styles);
1881
+ if (isWeb) {
1882
+ injectQueue.push({ cls: parsed.original, bucketKey, styles });
1883
+ }
1829
1884
  }
1830
1885
  styleCache.set(cacheKey, result);
1831
- if (isWeb) {
1832
- Promise.resolve().then(() => injectCSS(classString, result, darkMode));
1886
+ if (isWeb && injectQueue.length > 0) {
1887
+ Promise.resolve().then(() => {
1888
+ for (const { cls, bucketKey, styles } of injectQueue) {
1889
+ injectClassRule(cls, bucketKey, styles, darkMode);
1890
+ }
1891
+ });
1833
1892
  }
1834
1893
  return result;
1835
1894
  }
@@ -2,8 +2,8 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-23GRUDYB.mjs";
6
- import "./chunk-IYY3U6L2.mjs";
5
+ } from "./chunk-QMJ4XUJA.mjs";
6
+ import "./chunk-LYGD2GSW.mjs";
7
7
 
8
8
  // src/jsx-dev-runtime.tsx
9
9
  function jsxDEV(type, props, key, isStaticChildren, _source, _self) {
@@ -1111,6 +1111,25 @@ function resolveColor(value, colors, isArbitrary) {
1111
1111
  }
1112
1112
  }
1113
1113
  }
1114
+ if (hex && !hex.startsWith("#") && !hex.startsWith("rgb") && hex !== "transparent" && hex !== "currentColor") {
1115
+ if (hex in colors) {
1116
+ const alias = colors[hex];
1117
+ if (typeof alias === "string") hex = alias;
1118
+ else if (typeof alias === "object" && "6" in alias) hex = alias["6"] ?? hex;
1119
+ } else {
1120
+ const aliasLastDash = hex.lastIndexOf("-");
1121
+ if (aliasLastDash > 0) {
1122
+ const aliasColorName = hex.slice(0, aliasLastDash);
1123
+ const aliasShade = hex.slice(aliasLastDash + 1);
1124
+ const aliasScale = colors[aliasColorName];
1125
+ if (aliasScale && typeof aliasScale === "object") {
1126
+ hex = aliasScale[aliasShade] ?? null;
1127
+ } else if (typeof aliasScale === "string") {
1128
+ hex = aliasScale;
1129
+ }
1130
+ }
1131
+ }
1132
+ }
1114
1133
  if (!hex) return null;
1115
1134
  if (!opacityPart) return hex;
1116
1135
  let alpha;
@@ -1732,6 +1751,45 @@ var RESOLVERS = {
1732
1751
  const ms = isArbitrary ? value : `${value}ms`;
1733
1752
  return { transitionDelay: ms };
1734
1753
  },
1754
+ // ── Ring (web-only: box-shadow outline ring) ──────────────────────────────
1755
+ ring: ({ value, isArbitrary }, { colors }) => {
1756
+ if (!isWeb) return null;
1757
+ const DEFAULT_COLOR = "rgba(59, 130, 246, 0.5)";
1758
+ const DEFAULT_WIDTH = 3;
1759
+ if (!value) {
1760
+ return { boxShadow: `0 0 0 ${DEFAULT_WIDTH}px ${DEFAULT_COLOR}` };
1761
+ }
1762
+ if (value === "inset") {
1763
+ return { boxShadow: `inset 0 0 0 ${DEFAULT_WIDTH}px ${DEFAULT_COLOR}` };
1764
+ }
1765
+ const widthTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
1766
+ if (!isArbitrary && value in widthTokens) {
1767
+ const w = widthTokens[value];
1768
+ return { boxShadow: w === 0 ? "none" : `0 0 0 ${w}px ${DEFAULT_COLOR}` };
1769
+ }
1770
+ if (isArbitrary) {
1771
+ const numMatch = /^(\d+(?:\.\d+)?)(px|rem|em|vw|vh)?$/.exec(value);
1772
+ if (numMatch) {
1773
+ const unit = numMatch[2] ?? "px";
1774
+ return { boxShadow: `0 0 0 ${numMatch[1]}${unit} ${DEFAULT_COLOR}` };
1775
+ }
1776
+ return { boxShadow: value.replace(/_/g, " ") };
1777
+ }
1778
+ const color = resolveColor(value, colors, false);
1779
+ if (color) return { boxShadow: `0 0 0 ${DEFAULT_WIDTH}px ${color}` };
1780
+ return null;
1781
+ },
1782
+ "ring-offset": ({ value, isArbitrary }, _theme) => {
1783
+ if (!isWeb) return null;
1784
+ const DEFAULT_RING_COLOR = "rgba(59, 130, 246, 0.5)";
1785
+ const DEFAULT_RING_WIDTH = 3;
1786
+ const offsetTokens = { "0": 0, "1": 1, "2": 2, "4": 4, "8": 8 };
1787
+ const offsetWidth = !isArbitrary ? value in offsetTokens ? offsetTokens[value] : null : parseFloat(value);
1788
+ if (offsetWidth === null || isNaN(offsetWidth)) return null;
1789
+ return {
1790
+ boxShadow: offsetWidth === 0 ? `0 0 0 ${DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}` : `0 0 0 ${offsetWidth}px #fff, 0 0 0 ${offsetWidth + DEFAULT_RING_WIDTH}px ${DEFAULT_RING_COLOR}`
1791
+ };
1792
+ },
1735
1793
  // ── Space between (uses CSS gap or margin fallback) ────────────────────────
1736
1794
  "space-x": ({ value, negative, isArbitrary }, { spacing }) => {
1737
1795
  const v = resolveSpacing(value, negative, spacing, isArbitrary);
@@ -1773,42 +1831,35 @@ function injectRule(rule) {
1773
1831
  _injectedRules.add(rule);
1774
1832
  getStyleEl().sheet?.insertRule(rule, getStyleEl().sheet.cssRules.length);
1775
1833
  }
1776
- function injectCSS(classString, resolved, darkMode) {
1777
- if (!isWeb) return;
1778
- for (const [modifierKey, styles] of Object.entries(resolved)) {
1779
- if (!styles || Object.keys(styles).length === 0) continue;
1780
- const classes = classString.trim().split(/\s+/);
1781
- for (const cls of classes) {
1782
- const cssDecls = styleValueToCSS(styles);
1783
- if (!cssDecls) continue;
1784
- const escaped = escapeCSSSelector(cls);
1785
- if (modifierKey === "base") {
1786
- injectRule(`.${escaped} { ${cssDecls} }`);
1787
- continue;
1788
- }
1789
- const mods = modifierKey.split(":");
1790
- const pseudos = mods.filter((m) => !["dark", "light"].includes(m));
1791
- const hasDark = mods.includes("dark");
1792
- const pseudoSuffix = pseudos.map((p) => `:${p}`).join("");
1793
- let selector = `.${escaped}${pseudoSuffix}`;
1794
- let rule;
1795
- if (hasDark) {
1796
- if (darkMode === "media") {
1797
- rule = `@media (prefers-color-scheme: dark) { ${selector} { ${cssDecls} } }`;
1798
- } else if (darkMode === "class") {
1799
- rule = `.dark ${selector} { ${cssDecls} }`;
1800
- } else {
1801
- rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
1802
- }
1803
- } else {
1804
- rule = `${selector} { ${cssDecls} }`;
1805
- }
1806
- injectRule(rule);
1834
+ function injectClassRule(cls, bucketKey, styles, darkMode) {
1835
+ const cssDecls = styleValueToCSS(styles);
1836
+ if (!cssDecls) return;
1837
+ const escaped = escapeCSSSelector(cls);
1838
+ if (bucketKey === "base") {
1839
+ injectRule(`.${escaped} { ${cssDecls} }`);
1840
+ return;
1841
+ }
1842
+ const mods = bucketKey.split(":");
1843
+ const pseudos = mods.filter((m) => !["dark", "light"].includes(m));
1844
+ const hasDark = mods.includes("dark");
1845
+ const pseudoSuffix = pseudos.map((p) => `:${p}`).join("");
1846
+ const selector = `.${escaped}${pseudoSuffix}`;
1847
+ let rule;
1848
+ if (hasDark) {
1849
+ if (darkMode === "media") {
1850
+ rule = `@media (prefers-color-scheme: dark) { ${selector} { ${cssDecls} } }`;
1851
+ } else if (darkMode === "class") {
1852
+ rule = `.dark ${selector} { ${cssDecls} }`;
1853
+ } else {
1854
+ rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
1807
1855
  }
1856
+ } else {
1857
+ rule = `${selector} { ${cssDecls} }`;
1808
1858
  }
1859
+ injectRule(rule);
1809
1860
  }
1810
1861
  function styleValueToCSS(styles) {
1811
- return Object.entries(styles).filter(([, v]) => v !== void 0 && v !== null).map(([prop, val]) => `${camelToKebab(prop)}: ${val}`).join("; ");
1862
+ return Object.entries(styles).filter(([, v]) => v !== void 0 && v !== null && typeof v !== "object").map(([prop, val]) => `${camelToKebab(prop)}: ${val}`).join("; ");
1812
1863
  }
1813
1864
  function camelToKebab(str) {
1814
1865
  return str.replace(/([A-Z])/g, "-$1").toLowerCase();
@@ -1819,16 +1870,24 @@ function resolve(classString, theme, darkMode = "attribute") {
1819
1870
  if (cached) return cached;
1820
1871
  const result = {};
1821
1872
  const parsedClasses = parseClasses(classString);
1873
+ const injectQueue = [];
1822
1874
  for (const parsed of parsedClasses) {
1823
1875
  const styles = resolveUtility(parsed, theme);
1824
1876
  if (!styles) continue;
1825
1877
  const bucketKey = parsed.modifiers.length === 0 ? "base" : parsed.modifiers.join(":");
1826
1878
  if (!result[bucketKey]) result[bucketKey] = {};
1827
1879
  Object.assign(result[bucketKey], styles);
1880
+ if (isWeb) {
1881
+ injectQueue.push({ cls: parsed.original, bucketKey, styles });
1882
+ }
1828
1883
  }
1829
1884
  styleCache.set(cacheKey, result);
1830
- if (isWeb) {
1831
- Promise.resolve().then(() => injectCSS(classString, result, darkMode));
1885
+ if (isWeb && injectQueue.length > 0) {
1886
+ Promise.resolve().then(() => {
1887
+ for (const { cls, bucketKey, styles } of injectQueue) {
1888
+ injectClassRule(cls, bucketKey, styles, darkMode);
1889
+ }
1890
+ });
1832
1891
  }
1833
1892
  return result;
1834
1893
  }
@@ -2,8 +2,8 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-23GRUDYB.mjs";
6
- import "./chunk-IYY3U6L2.mjs";
5
+ } from "./chunk-QMJ4XUJA.mjs";
6
+ import "./chunk-LYGD2GSW.mjs";
7
7
  export {
8
8
  Fragment,
9
9
  jsx,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kbach/react",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "React / React Native components and hooks for the Kbach framework",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./dist/index.js",