shru-design-system 0.1.2 → 0.1.3

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.d.mts CHANGED
@@ -152,4 +152,14 @@ declare function getThemesForCategory(category: string): Promise<Record<string,
152
152
  */
153
153
  declare function getTheme(category: string, themeId: string): Promise<ThemeMetadata | null>;
154
154
 
155
- export { Badge, Button, Modal, ModalClose, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalOverlay, ModalPortal, ModalTitle, ModalTrigger, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, type ThemeMetadata$1 as ThemeMetadata, type ThemeSelection, ThemeToggle, type ThemeToggleProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buttonVariants, getTheme, getThemeCategories, getThemeFilePath, getThemesForCategory, registerTheme, useTheme, useThemeToggle };
155
+ /**
156
+ * Synchronous theme application for blocking script execution
157
+ * This runs before React hydrates to prevent theme flash
158
+ */
159
+ /**
160
+ * Apply theme synchronously using blocking XMLHttpRequest
161
+ * This prevents flash of unstyled content
162
+ */
163
+ declare function applyThemeSync(): void;
164
+
165
+ export { Badge, Button, Modal, ModalClose, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalOverlay, ModalPortal, ModalTitle, ModalTrigger, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, type ThemeMetadata$1 as ThemeMetadata, type ThemeSelection, ThemeToggle, type ThemeToggleProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, applyThemeSync, badgeVariants, buttonVariants, getTheme, getThemeCategories, getThemeFilePath, getThemesForCategory, registerTheme, useTheme, useThemeToggle };
package/dist/index.d.ts CHANGED
@@ -152,4 +152,14 @@ declare function getThemesForCategory(category: string): Promise<Record<string,
152
152
  */
153
153
  declare function getTheme(category: string, themeId: string): Promise<ThemeMetadata | null>;
154
154
 
155
- export { Badge, Button, Modal, ModalClose, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalOverlay, ModalPortal, ModalTitle, ModalTrigger, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, type ThemeMetadata$1 as ThemeMetadata, type ThemeSelection, ThemeToggle, type ThemeToggleProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buttonVariants, getTheme, getThemeCategories, getThemeFilePath, getThemesForCategory, registerTheme, useTheme, useThemeToggle };
155
+ /**
156
+ * Synchronous theme application for blocking script execution
157
+ * This runs before React hydrates to prevent theme flash
158
+ */
159
+ /**
160
+ * Apply theme synchronously using blocking XMLHttpRequest
161
+ * This prevents flash of unstyled content
162
+ */
163
+ declare function applyThemeSync(): void;
164
+
165
+ export { Badge, Button, Modal, ModalClose, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalOverlay, ModalPortal, ModalTitle, ModalTrigger, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, type ThemeMetadata$1 as ThemeMetadata, type ThemeSelection, ThemeToggle, type ThemeToggleProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, applyThemeSync, badgeVariants, buttonVariants, getTheme, getThemeCategories, getThemeFilePath, getThemesForCategory, registerTheme, useTheme, useThemeToggle };
package/dist/index.js CHANGED
@@ -712,6 +712,39 @@ function resolveReferences(tokens, palette) {
712
712
  traverse(resolved);
713
713
  return resolved;
714
714
  }
715
+ function hexToHSL(hex) {
716
+ hex = hex.replace("#", "");
717
+ const r = parseInt(hex.substring(0, 2), 16) / 255;
718
+ const g = parseInt(hex.substring(2, 4), 16) / 255;
719
+ const b = parseInt(hex.substring(4, 6), 16) / 255;
720
+ const max = Math.max(r, g, b);
721
+ const min = Math.min(r, g, b);
722
+ let h = 0;
723
+ let s = 0;
724
+ const l = (max + min) / 2;
725
+ if (max !== min) {
726
+ const d = max - min;
727
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
728
+ switch (max) {
729
+ case r:
730
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
731
+ break;
732
+ case g:
733
+ h = ((b - r) / d + 2) / 6;
734
+ break;
735
+ case b:
736
+ h = ((r - g) / d + 4) / 6;
737
+ break;
738
+ }
739
+ }
740
+ h = Math.round(h * 360);
741
+ s = Math.round(s * 100);
742
+ const lPercent = Math.round(l * 100);
743
+ return `${h} ${s}% ${lPercent}%`;
744
+ }
745
+ function isHexColor(value) {
746
+ return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value);
747
+ }
715
748
  function flattenToCSS(tokens, prefix = "", result = {}, isColorContext = false) {
716
749
  for (const key in tokens) {
717
750
  const value = tokens[key];
@@ -735,7 +768,11 @@ function flattenToCSS(tokens, prefix = "", result = {}, isColorContext = false)
735
768
  } else {
736
769
  cssKey = `--${prefix}-${key}`;
737
770
  }
738
- result[cssKey] = value;
771
+ let finalValue = value;
772
+ if (isColorContext && typeof value === "string" && isHexColor(value)) {
773
+ finalValue = hexToHSL(value);
774
+ }
775
+ result[cssKey] = finalValue;
739
776
  }
740
777
  }
741
778
  return result;
@@ -1226,6 +1263,208 @@ function ThemeRingAsync({
1226
1263
  ] });
1227
1264
  }
1228
1265
 
1266
+ // src/themes/applyThemeSync.ts
1267
+ var STORAGE_KEY2 = "design-system-theme";
1268
+ function applyThemeSync() {
1269
+ if (typeof window === "undefined" || typeof document === "undefined") {
1270
+ return;
1271
+ }
1272
+ let selectedThemes = getDefaultThemes();
1273
+ try {
1274
+ const stored = localStorage.getItem(STORAGE_KEY2);
1275
+ if (stored) {
1276
+ selectedThemes = JSON.parse(stored);
1277
+ }
1278
+ } catch {
1279
+ }
1280
+ try {
1281
+ const base = loadJSONSync("/tokens/base.json");
1282
+ const palettes = loadJSONSync("/tokens/palettes.json");
1283
+ const palette = palettes?.palette || {};
1284
+ let merged = deepMergeSync(base || {}, { palette });
1285
+ const categoryOrder = ["color", "typography", "shape", "density", "animation"];
1286
+ for (const category of categoryOrder) {
1287
+ const themeId = selectedThemes[category];
1288
+ if (!themeId) continue;
1289
+ try {
1290
+ const themeData = loadJSONSync(`/tokens/themes/${category}/${themeId}.json`);
1291
+ if (themeData) {
1292
+ merged = deepMergeSync(merged, themeData);
1293
+ }
1294
+ } catch {
1295
+ }
1296
+ }
1297
+ if (selectedThemes.custom) {
1298
+ try {
1299
+ const customData = loadJSONSync(`/tokens/themes/custom/${selectedThemes.custom}.json`);
1300
+ if (customData) {
1301
+ merged = deepMergeSync(merged, customData);
1302
+ }
1303
+ } catch {
1304
+ }
1305
+ }
1306
+ const resolved = resolveReferencesSync(merged, palette);
1307
+ const cssVars = flattenToCSSSync(resolved);
1308
+ const mappedVars = mapToTailwindVarsSync(cssVars);
1309
+ const css = `:root {
1310
+ ${Object.entries(mappedVars).map(([key, value]) => ` ${key}: ${value};`).join("\n")}
1311
+ }`;
1312
+ let styleTag = document.getElementById("dynamic-theme");
1313
+ if (!styleTag) {
1314
+ styleTag = document.createElement("style");
1315
+ styleTag.id = "dynamic-theme";
1316
+ document.head.insertBefore(styleTag, document.head.firstChild);
1317
+ }
1318
+ styleTag.textContent = css;
1319
+ } catch (error) {
1320
+ console.warn("Sync theme application failed, will apply via React:", error);
1321
+ }
1322
+ }
1323
+ function loadJSONSync(path) {
1324
+ try {
1325
+ const xhr = new XMLHttpRequest();
1326
+ xhr.open("GET", path, false);
1327
+ xhr.send(null);
1328
+ if (xhr.status === 200 || xhr.status === 0) {
1329
+ return JSON.parse(xhr.responseText);
1330
+ }
1331
+ } catch {
1332
+ }
1333
+ return null;
1334
+ }
1335
+ function deepMergeSync(target, source) {
1336
+ const output = { ...target };
1337
+ if (isObjectSync(target) && isObjectSync(source)) {
1338
+ Object.keys(source).forEach((key) => {
1339
+ if (isObjectSync(source[key])) {
1340
+ if (!(key in target)) {
1341
+ Object.assign(output, { [key]: source[key] });
1342
+ } else {
1343
+ output[key] = deepMergeSync(target[key], source[key]);
1344
+ }
1345
+ } else {
1346
+ Object.assign(output, { [key]: source[key] });
1347
+ }
1348
+ });
1349
+ }
1350
+ return output;
1351
+ }
1352
+ function isObjectSync(item) {
1353
+ return item && typeof item === "object" && !Array.isArray(item);
1354
+ }
1355
+ function resolveReferencesSync(tokens, palette) {
1356
+ const resolved = JSON.parse(JSON.stringify(tokens));
1357
+ function resolveValue(value) {
1358
+ if (typeof value !== "string") return value;
1359
+ const match = value.match(/^\{([^}]+)\}$/);
1360
+ if (!match) return value;
1361
+ const path = match[1].split(".");
1362
+ let current = { palette, ...resolved };
1363
+ for (const key of path) {
1364
+ if (current && typeof current === "object" && key in current) {
1365
+ current = current[key];
1366
+ } else {
1367
+ return value;
1368
+ }
1369
+ }
1370
+ return typeof current === "string" ? current : value;
1371
+ }
1372
+ function traverse(obj) {
1373
+ for (const key in obj) {
1374
+ if (typeof obj[key] === "object" && obj[key] !== null) {
1375
+ traverse(obj[key]);
1376
+ } else {
1377
+ obj[key] = resolveValue(obj[key]);
1378
+ }
1379
+ }
1380
+ }
1381
+ traverse(resolved);
1382
+ return resolved;
1383
+ }
1384
+ function hexToHSL2(hex) {
1385
+ hex = hex.replace("#", "");
1386
+ const r = parseInt(hex.substring(0, 2), 16) / 255;
1387
+ const g = parseInt(hex.substring(2, 4), 16) / 255;
1388
+ const b = parseInt(hex.substring(4, 6), 16) / 255;
1389
+ const max = Math.max(r, g, b);
1390
+ const min = Math.min(r, g, b);
1391
+ let h = 0;
1392
+ let s = 0;
1393
+ const l = (max + min) / 2;
1394
+ if (max !== min) {
1395
+ const d = max - min;
1396
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
1397
+ switch (max) {
1398
+ case r:
1399
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
1400
+ break;
1401
+ case g:
1402
+ h = ((b - r) / d + 2) / 6;
1403
+ break;
1404
+ case b:
1405
+ h = ((r - g) / d + 4) / 6;
1406
+ break;
1407
+ }
1408
+ }
1409
+ h = Math.round(h * 360);
1410
+ s = Math.round(s * 100);
1411
+ const lPercent = Math.round(l * 100);
1412
+ return `${h} ${s}% ${lPercent}%`;
1413
+ }
1414
+ function isHexColor2(value) {
1415
+ return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value);
1416
+ }
1417
+ function flattenToCSSSync(tokens, prefix = "", result = {}, isColorContext = false) {
1418
+ for (const key in tokens) {
1419
+ const value = tokens[key];
1420
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
1421
+ const enteringColor = key === "color" && prefix === "";
1422
+ const inColorContext = isColorContext || enteringColor;
1423
+ if (enteringColor) {
1424
+ flattenToCSSSync(value, "", result, true);
1425
+ } else if (inColorContext) {
1426
+ flattenToCSSSync(value, "", result, true);
1427
+ } else {
1428
+ const newPrefix = prefix ? `${prefix}-${key}` : key;
1429
+ flattenToCSSSync(value, newPrefix, result, false);
1430
+ }
1431
+ } else {
1432
+ let cssKey;
1433
+ if (isColorContext || prefix === "" && key === "color") {
1434
+ cssKey = `--${key}`;
1435
+ } else if (prefix === "") {
1436
+ cssKey = `--${key}`;
1437
+ } else {
1438
+ cssKey = `--${prefix}-${key}`;
1439
+ }
1440
+ let finalValue = value;
1441
+ if (isColorContext && typeof value === "string" && isHexColor2(value)) {
1442
+ finalValue = hexToHSL2(value);
1443
+ }
1444
+ result[cssKey] = finalValue;
1445
+ }
1446
+ }
1447
+ return result;
1448
+ }
1449
+ function mapToTailwindVarsSync(cssVars) {
1450
+ const mapped = { ...cssVars };
1451
+ if (cssVars["--radius-button"]) {
1452
+ mapped["--radius"] = cssVars["--radius-button"];
1453
+ }
1454
+ if (cssVars["--radius-card"]) {
1455
+ mapped["--radius-lg"] = cssVars["--radius-card"];
1456
+ }
1457
+ if (cssVars["--font-body"]) {
1458
+ mapped["--font-sans"] = cssVars["--font-body"];
1459
+ }
1460
+ if (cssVars["--spacing-base"]) {
1461
+ mapped["--spacing"] = cssVars["--spacing-base"];
1462
+ } else if (cssVars["--spacing-component-md"]) {
1463
+ mapped["--spacing"] = cssVars["--spacing-component-md"];
1464
+ }
1465
+ return mapped;
1466
+ }
1467
+
1229
1468
  exports.Badge = Badge;
1230
1469
  exports.Button = Button;
1231
1470
  exports.Modal = Modal;
@@ -1253,6 +1492,7 @@ exports.Tooltip = Tooltip;
1253
1492
  exports.TooltipContent = TooltipContent;
1254
1493
  exports.TooltipProvider = TooltipProvider;
1255
1494
  exports.TooltipTrigger = TooltipTrigger;
1495
+ exports.applyThemeSync = applyThemeSync;
1256
1496
  exports.badgeVariants = badgeVariants;
1257
1497
  exports.buttonVariants = buttonVariants;
1258
1498
  exports.getTheme = getTheme;