shru-design-system 0.1.2 → 0.1.4
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 +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +289 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +289 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/scripts/apply-theme-sync.js +242 -0
- package/scripts/init.js +99 -34
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
@@ -756,6 +793,30 @@ function mapToTailwindVars(cssVars) {
|
|
|
756
793
|
} else if (cssVars["--spacing-component-md"]) {
|
|
757
794
|
mapped["--spacing"] = cssVars["--spacing-component-md"];
|
|
758
795
|
}
|
|
796
|
+
if (cssVars["--spacing-component-xs"]) {
|
|
797
|
+
mapped["--spacing-component-xs"] = cssVars["--spacing-component-xs"];
|
|
798
|
+
}
|
|
799
|
+
if (cssVars["--spacing-component-sm"]) {
|
|
800
|
+
mapped["--spacing-component-sm"] = cssVars["--spacing-component-sm"];
|
|
801
|
+
}
|
|
802
|
+
if (cssVars["--spacing-component-md"]) {
|
|
803
|
+
mapped["--spacing-component-md"] = cssVars["--spacing-component-md"];
|
|
804
|
+
}
|
|
805
|
+
if (cssVars["--spacing-component-lg"]) {
|
|
806
|
+
mapped["--spacing-component-lg"] = cssVars["--spacing-component-lg"];
|
|
807
|
+
}
|
|
808
|
+
if (cssVars["--spacing-component-xl"]) {
|
|
809
|
+
mapped["--spacing-component-xl"] = cssVars["--spacing-component-xl"];
|
|
810
|
+
}
|
|
811
|
+
if (cssVars["--duration-fast"]) {
|
|
812
|
+
mapped["--duration-fast"] = cssVars["--duration-fast"];
|
|
813
|
+
}
|
|
814
|
+
if (cssVars["--duration-normal"]) {
|
|
815
|
+
mapped["--duration-normal"] = cssVars["--duration-normal"];
|
|
816
|
+
}
|
|
817
|
+
if (cssVars["--duration-slow"]) {
|
|
818
|
+
mapped["--duration-slow"] = cssVars["--duration-slow"];
|
|
819
|
+
}
|
|
759
820
|
return mapped;
|
|
760
821
|
}
|
|
761
822
|
function generateCSSString(cssVars) {
|
|
@@ -1226,6 +1287,232 @@ function ThemeRingAsync({
|
|
|
1226
1287
|
] });
|
|
1227
1288
|
}
|
|
1228
1289
|
|
|
1290
|
+
// src/themes/applyThemeSync.ts
|
|
1291
|
+
var STORAGE_KEY2 = "design-system-theme";
|
|
1292
|
+
function applyThemeSync() {
|
|
1293
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
let selectedThemes = getDefaultThemes();
|
|
1297
|
+
try {
|
|
1298
|
+
const stored = localStorage.getItem(STORAGE_KEY2);
|
|
1299
|
+
if (stored) {
|
|
1300
|
+
selectedThemes = JSON.parse(stored);
|
|
1301
|
+
}
|
|
1302
|
+
} catch {
|
|
1303
|
+
}
|
|
1304
|
+
try {
|
|
1305
|
+
const base = loadJSONSync("/tokens/base.json");
|
|
1306
|
+
const palettes = loadJSONSync("/tokens/palettes.json");
|
|
1307
|
+
const palette = palettes?.palette || {};
|
|
1308
|
+
let merged = deepMergeSync(base || {}, { palette });
|
|
1309
|
+
const categoryOrder = ["color", "typography", "shape", "density", "animation"];
|
|
1310
|
+
for (const category of categoryOrder) {
|
|
1311
|
+
const themeId = selectedThemes[category];
|
|
1312
|
+
if (!themeId) continue;
|
|
1313
|
+
try {
|
|
1314
|
+
const themeData = loadJSONSync(`/tokens/themes/${category}/${themeId}.json`);
|
|
1315
|
+
if (themeData) {
|
|
1316
|
+
merged = deepMergeSync(merged, themeData);
|
|
1317
|
+
}
|
|
1318
|
+
} catch {
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
if (selectedThemes.custom) {
|
|
1322
|
+
try {
|
|
1323
|
+
const customData = loadJSONSync(`/tokens/themes/custom/${selectedThemes.custom}.json`);
|
|
1324
|
+
if (customData) {
|
|
1325
|
+
merged = deepMergeSync(merged, customData);
|
|
1326
|
+
}
|
|
1327
|
+
} catch {
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
const resolved = resolveReferencesSync(merged, palette);
|
|
1331
|
+
const cssVars = flattenToCSSSync(resolved);
|
|
1332
|
+
const mappedVars = mapToTailwindVarsSync(cssVars);
|
|
1333
|
+
const css = `:root {
|
|
1334
|
+
${Object.entries(mappedVars).map(([key, value]) => ` ${key}: ${value};`).join("\n")}
|
|
1335
|
+
}`;
|
|
1336
|
+
let styleTag = document.getElementById("dynamic-theme");
|
|
1337
|
+
if (!styleTag) {
|
|
1338
|
+
styleTag = document.createElement("style");
|
|
1339
|
+
styleTag.id = "dynamic-theme";
|
|
1340
|
+
document.head.insertBefore(styleTag, document.head.firstChild);
|
|
1341
|
+
}
|
|
1342
|
+
styleTag.textContent = css;
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
console.warn("Sync theme application failed, will apply via React:", error);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
function loadJSONSync(path) {
|
|
1348
|
+
try {
|
|
1349
|
+
const xhr = new XMLHttpRequest();
|
|
1350
|
+
xhr.open("GET", path, false);
|
|
1351
|
+
xhr.send(null);
|
|
1352
|
+
if (xhr.status === 200 || xhr.status === 0) {
|
|
1353
|
+
return JSON.parse(xhr.responseText);
|
|
1354
|
+
}
|
|
1355
|
+
} catch {
|
|
1356
|
+
}
|
|
1357
|
+
return null;
|
|
1358
|
+
}
|
|
1359
|
+
function deepMergeSync(target, source) {
|
|
1360
|
+
const output = { ...target };
|
|
1361
|
+
if (isObjectSync(target) && isObjectSync(source)) {
|
|
1362
|
+
Object.keys(source).forEach((key) => {
|
|
1363
|
+
if (isObjectSync(source[key])) {
|
|
1364
|
+
if (!(key in target)) {
|
|
1365
|
+
Object.assign(output, { [key]: source[key] });
|
|
1366
|
+
} else {
|
|
1367
|
+
output[key] = deepMergeSync(target[key], source[key]);
|
|
1368
|
+
}
|
|
1369
|
+
} else {
|
|
1370
|
+
Object.assign(output, { [key]: source[key] });
|
|
1371
|
+
}
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
return output;
|
|
1375
|
+
}
|
|
1376
|
+
function isObjectSync(item) {
|
|
1377
|
+
return item && typeof item === "object" && !Array.isArray(item);
|
|
1378
|
+
}
|
|
1379
|
+
function resolveReferencesSync(tokens, palette) {
|
|
1380
|
+
const resolved = JSON.parse(JSON.stringify(tokens));
|
|
1381
|
+
function resolveValue(value) {
|
|
1382
|
+
if (typeof value !== "string") return value;
|
|
1383
|
+
const match = value.match(/^\{([^}]+)\}$/);
|
|
1384
|
+
if (!match) return value;
|
|
1385
|
+
const path = match[1].split(".");
|
|
1386
|
+
let current = { palette, ...resolved };
|
|
1387
|
+
for (const key of path) {
|
|
1388
|
+
if (current && typeof current === "object" && key in current) {
|
|
1389
|
+
current = current[key];
|
|
1390
|
+
} else {
|
|
1391
|
+
return value;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
return typeof current === "string" ? current : value;
|
|
1395
|
+
}
|
|
1396
|
+
function traverse(obj) {
|
|
1397
|
+
for (const key in obj) {
|
|
1398
|
+
if (typeof obj[key] === "object" && obj[key] !== null) {
|
|
1399
|
+
traverse(obj[key]);
|
|
1400
|
+
} else {
|
|
1401
|
+
obj[key] = resolveValue(obj[key]);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
traverse(resolved);
|
|
1406
|
+
return resolved;
|
|
1407
|
+
}
|
|
1408
|
+
function hexToHSL2(hex) {
|
|
1409
|
+
hex = hex.replace("#", "");
|
|
1410
|
+
const r = parseInt(hex.substring(0, 2), 16) / 255;
|
|
1411
|
+
const g = parseInt(hex.substring(2, 4), 16) / 255;
|
|
1412
|
+
const b = parseInt(hex.substring(4, 6), 16) / 255;
|
|
1413
|
+
const max = Math.max(r, g, b);
|
|
1414
|
+
const min = Math.min(r, g, b);
|
|
1415
|
+
let h = 0;
|
|
1416
|
+
let s = 0;
|
|
1417
|
+
const l = (max + min) / 2;
|
|
1418
|
+
if (max !== min) {
|
|
1419
|
+
const d = max - min;
|
|
1420
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
1421
|
+
switch (max) {
|
|
1422
|
+
case r:
|
|
1423
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
1424
|
+
break;
|
|
1425
|
+
case g:
|
|
1426
|
+
h = ((b - r) / d + 2) / 6;
|
|
1427
|
+
break;
|
|
1428
|
+
case b:
|
|
1429
|
+
h = ((r - g) / d + 4) / 6;
|
|
1430
|
+
break;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
h = Math.round(h * 360);
|
|
1434
|
+
s = Math.round(s * 100);
|
|
1435
|
+
const lPercent = Math.round(l * 100);
|
|
1436
|
+
return `${h} ${s}% ${lPercent}%`;
|
|
1437
|
+
}
|
|
1438
|
+
function isHexColor2(value) {
|
|
1439
|
+
return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value);
|
|
1440
|
+
}
|
|
1441
|
+
function flattenToCSSSync(tokens, prefix = "", result = {}, isColorContext = false) {
|
|
1442
|
+
for (const key in tokens) {
|
|
1443
|
+
const value = tokens[key];
|
|
1444
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
1445
|
+
const enteringColor = key === "color" && prefix === "";
|
|
1446
|
+
const inColorContext = isColorContext || enteringColor;
|
|
1447
|
+
if (enteringColor) {
|
|
1448
|
+
flattenToCSSSync(value, "", result, true);
|
|
1449
|
+
} else if (inColorContext) {
|
|
1450
|
+
flattenToCSSSync(value, "", result, true);
|
|
1451
|
+
} else {
|
|
1452
|
+
const newPrefix = prefix ? `${prefix}-${key}` : key;
|
|
1453
|
+
flattenToCSSSync(value, newPrefix, result, false);
|
|
1454
|
+
}
|
|
1455
|
+
} else {
|
|
1456
|
+
let cssKey;
|
|
1457
|
+
if (isColorContext || prefix === "" && key === "color") {
|
|
1458
|
+
cssKey = `--${key}`;
|
|
1459
|
+
} else if (prefix === "") {
|
|
1460
|
+
cssKey = `--${key}`;
|
|
1461
|
+
} else {
|
|
1462
|
+
cssKey = `--${prefix}-${key}`;
|
|
1463
|
+
}
|
|
1464
|
+
let finalValue = value;
|
|
1465
|
+
if (isColorContext && typeof value === "string" && isHexColor2(value)) {
|
|
1466
|
+
finalValue = hexToHSL2(value);
|
|
1467
|
+
}
|
|
1468
|
+
result[cssKey] = finalValue;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
return result;
|
|
1472
|
+
}
|
|
1473
|
+
function mapToTailwindVarsSync(cssVars) {
|
|
1474
|
+
const mapped = { ...cssVars };
|
|
1475
|
+
if (cssVars["--radius-button"]) {
|
|
1476
|
+
mapped["--radius"] = cssVars["--radius-button"];
|
|
1477
|
+
}
|
|
1478
|
+
if (cssVars["--radius-card"]) {
|
|
1479
|
+
mapped["--radius-lg"] = cssVars["--radius-card"];
|
|
1480
|
+
}
|
|
1481
|
+
if (cssVars["--font-body"]) {
|
|
1482
|
+
mapped["--font-sans"] = cssVars["--font-body"];
|
|
1483
|
+
}
|
|
1484
|
+
if (cssVars["--spacing-base"]) {
|
|
1485
|
+
mapped["--spacing"] = cssVars["--spacing-base"];
|
|
1486
|
+
} else if (cssVars["--spacing-component-md"]) {
|
|
1487
|
+
mapped["--spacing"] = cssVars["--spacing-component-md"];
|
|
1488
|
+
}
|
|
1489
|
+
if (cssVars["--spacing-component-xs"]) {
|
|
1490
|
+
mapped["--spacing-component-xs"] = cssVars["--spacing-component-xs"];
|
|
1491
|
+
}
|
|
1492
|
+
if (cssVars["--spacing-component-sm"]) {
|
|
1493
|
+
mapped["--spacing-component-sm"] = cssVars["--spacing-component-sm"];
|
|
1494
|
+
}
|
|
1495
|
+
if (cssVars["--spacing-component-md"]) {
|
|
1496
|
+
mapped["--spacing-component-md"] = cssVars["--spacing-component-md"];
|
|
1497
|
+
}
|
|
1498
|
+
if (cssVars["--spacing-component-lg"]) {
|
|
1499
|
+
mapped["--spacing-component-lg"] = cssVars["--spacing-component-lg"];
|
|
1500
|
+
}
|
|
1501
|
+
if (cssVars["--spacing-component-xl"]) {
|
|
1502
|
+
mapped["--spacing-component-xl"] = cssVars["--spacing-component-xl"];
|
|
1503
|
+
}
|
|
1504
|
+
if (cssVars["--duration-fast"]) {
|
|
1505
|
+
mapped["--duration-fast"] = cssVars["--duration-fast"];
|
|
1506
|
+
}
|
|
1507
|
+
if (cssVars["--duration-normal"]) {
|
|
1508
|
+
mapped["--duration-normal"] = cssVars["--duration-normal"];
|
|
1509
|
+
}
|
|
1510
|
+
if (cssVars["--duration-slow"]) {
|
|
1511
|
+
mapped["--duration-slow"] = cssVars["--duration-slow"];
|
|
1512
|
+
}
|
|
1513
|
+
return mapped;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1229
1516
|
exports.Badge = Badge;
|
|
1230
1517
|
exports.Button = Button;
|
|
1231
1518
|
exports.Modal = Modal;
|
|
@@ -1253,6 +1540,7 @@ exports.Tooltip = Tooltip;
|
|
|
1253
1540
|
exports.TooltipContent = TooltipContent;
|
|
1254
1541
|
exports.TooltipProvider = TooltipProvider;
|
|
1255
1542
|
exports.TooltipTrigger = TooltipTrigger;
|
|
1543
|
+
exports.applyThemeSync = applyThemeSync;
|
|
1256
1544
|
exports.badgeVariants = badgeVariants;
|
|
1257
1545
|
exports.buttonVariants = buttonVariants;
|
|
1258
1546
|
exports.getTheme = getTheme;
|