shru-design-system 0.1.10 → 0.1.12
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/README.md +21 -4
- package/dist/index.js +416 -37
- package/dist/index.mjs +416 -37
- package/package.json +1 -1
- package/scripts/apply-theme-sync.js +33 -11
- package/scripts/init.js +3 -57
package/README.md
CHANGED
|
@@ -88,18 +88,36 @@ Local test environment for developing and testing the library:
|
|
|
88
88
|
|
|
89
89
|
## Usage
|
|
90
90
|
|
|
91
|
-
Install
|
|
91
|
+
Install:
|
|
92
92
|
```bash
|
|
93
93
|
npm install shru-design-system
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
### Theme setup (new flow)
|
|
97
|
+
- Tokens are embedded in the bundle, so the theme system works out-of-the-box (no `/public/tokens` required).
|
|
98
|
+
- If you want to self-host tokens (for custom themes or CDN):
|
|
99
|
+
- Set `window.__THEME_TOKENS_BASE__ = "/tokens"` (or your URL) before the app renders, or set `DESIGN_SYSTEM_TOKENS_BASE` env at build/runtime.
|
|
100
|
+
- Serve token JSONs at `<base>/base.json`, `<base>/palettes.json`, `<base>/themes/<category>/<theme>.json`.
|
|
101
|
+
- Blocking HTML injection is removed; the runtime/applyThemeSync appends `#dynamic-theme` at the end of `<head>` so its CSS vars override compiled CSS.
|
|
102
|
+
|
|
103
|
+
Optional token copy:
|
|
104
|
+
```bash
|
|
105
|
+
npx shru-design-system-init # copies tokens into public/tokens if you want static hosting
|
|
106
|
+
```
|
|
97
107
|
|
|
98
108
|
Import components:
|
|
99
109
|
```tsx
|
|
100
|
-
import { Button, Modal, ThemeToggle } from 'shru-design-system'
|
|
110
|
+
import { Button, Modal, ThemeToggle, registerThemeFromFile } from 'shru-design-system'
|
|
101
111
|
```
|
|
102
112
|
|
|
113
|
+
Registering themes:
|
|
114
|
+
- Add a custom theme file at your token base, e.g. `/tokens/themes/custom/my-brand.json`.
|
|
115
|
+
- Call at runtime (after page load):
|
|
116
|
+
```ts
|
|
117
|
+
registerThemeFromFile('custom', 'my-brand', '/tokens/themes/custom/my-brand.json')
|
|
118
|
+
```
|
|
119
|
+
The theme toggle will pick it up automatically.
|
|
120
|
+
|
|
103
121
|
## Development
|
|
104
122
|
|
|
105
123
|
Build the library:
|
|
@@ -111,4 +129,3 @@ Test locally:
|
|
|
111
129
|
```bash
|
|
112
130
|
cd test && npm run dev
|
|
113
131
|
```
|
|
114
|
-
|
package/dist/index.js
CHANGED
|
@@ -762,6 +762,331 @@ async function getTheme(category, themeId) {
|
|
|
762
762
|
return categories[category]?.themes[themeId] || null;
|
|
763
763
|
}
|
|
764
764
|
|
|
765
|
+
// src/tokens/base.json
|
|
766
|
+
var base_default = {
|
|
767
|
+
_createdBy: "shru-design-system library",
|
|
768
|
+
color: {
|
|
769
|
+
primary: "{palette.blue.500}",
|
|
770
|
+
"primary-hover": "{palette.blue.600}",
|
|
771
|
+
"primary-foreground": "{palette.white}",
|
|
772
|
+
secondary: "{palette.gray.100}",
|
|
773
|
+
"secondary-foreground": "{palette.gray.900}",
|
|
774
|
+
background: "{palette.white}",
|
|
775
|
+
foreground: "{palette.gray.900}",
|
|
776
|
+
card: "{palette.white}",
|
|
777
|
+
"card-foreground": "{palette.gray.900}",
|
|
778
|
+
popover: "{palette.white}",
|
|
779
|
+
"popover-foreground": "{palette.gray.900}",
|
|
780
|
+
muted: "{palette.gray.100}",
|
|
781
|
+
"muted-foreground": "{palette.gray.500}",
|
|
782
|
+
accent: "{palette.gray.100}",
|
|
783
|
+
"accent-foreground": "{palette.gray.900}",
|
|
784
|
+
destructive: "{palette.red.500}",
|
|
785
|
+
"destructive-foreground": "{palette.white}",
|
|
786
|
+
border: "{palette.gray.200}",
|
|
787
|
+
input: "{palette.gray.200}",
|
|
788
|
+
ring: "{palette.gray.400}",
|
|
789
|
+
skeleton: "{palette.gray.200}"
|
|
790
|
+
},
|
|
791
|
+
spacing: {
|
|
792
|
+
component: {
|
|
793
|
+
xs: "0.25rem",
|
|
794
|
+
sm: "0.5rem",
|
|
795
|
+
md: "1rem",
|
|
796
|
+
lg: "1.5rem",
|
|
797
|
+
xl: "2rem"
|
|
798
|
+
},
|
|
799
|
+
base: "0.25rem"
|
|
800
|
+
},
|
|
801
|
+
font: {
|
|
802
|
+
body: "var(--font-sans)",
|
|
803
|
+
sans: "var(--font-sans)",
|
|
804
|
+
mono: "var(--font-mono)"
|
|
805
|
+
},
|
|
806
|
+
radius: {
|
|
807
|
+
button: "0.375rem",
|
|
808
|
+
card: "0.5rem",
|
|
809
|
+
input: "0.375rem"
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
// src/tokens/palettes.json
|
|
814
|
+
var palettes_default = {
|
|
815
|
+
_createdBy: "shru-design-system library",
|
|
816
|
+
palette: {
|
|
817
|
+
white: "#ffffff",
|
|
818
|
+
black: "#000000",
|
|
819
|
+
transparent: "transparent",
|
|
820
|
+
gray: {
|
|
821
|
+
"50": "#f9fafb",
|
|
822
|
+
"100": "#f3f4f6",
|
|
823
|
+
"200": "#e5e7eb",
|
|
824
|
+
"300": "#d1d5db",
|
|
825
|
+
"400": "#9ca3af",
|
|
826
|
+
"500": "#6b7280",
|
|
827
|
+
"600": "#4b5563",
|
|
828
|
+
"700": "#374151",
|
|
829
|
+
"800": "#1f2937",
|
|
830
|
+
"900": "#111827",
|
|
831
|
+
"950": "#030712"
|
|
832
|
+
},
|
|
833
|
+
blue: {
|
|
834
|
+
"50": "#eff6ff",
|
|
835
|
+
"100": "#dbeafe",
|
|
836
|
+
"200": "#bfdbfe",
|
|
837
|
+
"300": "#93c5fd",
|
|
838
|
+
"400": "#60a5fa",
|
|
839
|
+
"500": "#3b82f6",
|
|
840
|
+
"600": "#2563eb",
|
|
841
|
+
"700": "#1d4ed8",
|
|
842
|
+
"800": "#1e40af",
|
|
843
|
+
"900": "#1e3a8a",
|
|
844
|
+
"950": "#172554"
|
|
845
|
+
},
|
|
846
|
+
red: {
|
|
847
|
+
"50": "#fef2f2",
|
|
848
|
+
"100": "#fee2e2",
|
|
849
|
+
"200": "#fecaca",
|
|
850
|
+
"300": "#fca5a5",
|
|
851
|
+
"400": "#f87171",
|
|
852
|
+
"500": "#ef4444",
|
|
853
|
+
"600": "#dc2626",
|
|
854
|
+
"700": "#b91c1c",
|
|
855
|
+
"800": "#991b1b",
|
|
856
|
+
"900": "#7f1d1d",
|
|
857
|
+
"950": "#450a0a"
|
|
858
|
+
},
|
|
859
|
+
purple: {
|
|
860
|
+
"50": "#faf5ff",
|
|
861
|
+
"100": "#f3e8ff",
|
|
862
|
+
"200": "#e9d5ff",
|
|
863
|
+
"300": "#d8b4fe",
|
|
864
|
+
"400": "#c084fc",
|
|
865
|
+
"500": "#a855f7",
|
|
866
|
+
"600": "#9333ea",
|
|
867
|
+
"700": "#7e22ce",
|
|
868
|
+
"800": "#6b21a8",
|
|
869
|
+
"900": "#581c87",
|
|
870
|
+
"950": "#3b0764"
|
|
871
|
+
},
|
|
872
|
+
pink: {
|
|
873
|
+
"50": "#fdf2f8",
|
|
874
|
+
"100": "#fce7f3",
|
|
875
|
+
"200": "#fbcfe8",
|
|
876
|
+
"300": "#f9a8d4",
|
|
877
|
+
"400": "#f472b6",
|
|
878
|
+
"500": "#ec4899",
|
|
879
|
+
"600": "#db2777",
|
|
880
|
+
"700": "#be185d",
|
|
881
|
+
"800": "#9f1239",
|
|
882
|
+
"900": "#831843",
|
|
883
|
+
"950": "#500724"
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
// src/tokens/themes/color/dark.json
|
|
889
|
+
var dark_default = {
|
|
890
|
+
_createdBy: "shru-design-system library",
|
|
891
|
+
color: {
|
|
892
|
+
primary: "{palette.blue.400}",
|
|
893
|
+
"primary-foreground": "{palette.gray.900}",
|
|
894
|
+
background: "{palette.gray.900}",
|
|
895
|
+
foreground: "{palette.gray.50}",
|
|
896
|
+
card: "{palette.gray.800}",
|
|
897
|
+
"card-foreground": "{palette.gray.50}",
|
|
898
|
+
popover: "{palette.gray.800}",
|
|
899
|
+
"popover-foreground": "{palette.gray.50}",
|
|
900
|
+
secondary: "{palette.gray.800}",
|
|
901
|
+
"secondary-foreground": "{palette.gray.50}",
|
|
902
|
+
muted: "{palette.gray.800}",
|
|
903
|
+
"muted-foreground": "{palette.gray.400}",
|
|
904
|
+
accent: "{palette.gray.800}",
|
|
905
|
+
"accent-foreground": "{palette.gray.50}",
|
|
906
|
+
destructive: "{palette.red.500}",
|
|
907
|
+
"destructive-foreground": "{palette.white}",
|
|
908
|
+
border: "{palette.gray.700}",
|
|
909
|
+
input: "{palette.gray.700}",
|
|
910
|
+
ring: "{palette.gray.600}",
|
|
911
|
+
skeleton: "{palette.gray.700}"
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
// src/tokens/themes/color/white.json
|
|
916
|
+
var white_default = {
|
|
917
|
+
_createdBy: "shru-design-system library",
|
|
918
|
+
color: {
|
|
919
|
+
primary: "{palette.blue.500}",
|
|
920
|
+
"primary-foreground": "{palette.white}",
|
|
921
|
+
background: "{palette.white}",
|
|
922
|
+
foreground: "{palette.gray.900}",
|
|
923
|
+
card: "{palette.white}",
|
|
924
|
+
"card-foreground": "{palette.gray.900}",
|
|
925
|
+
popover: "{palette.white}",
|
|
926
|
+
"popover-foreground": "{palette.gray.900}",
|
|
927
|
+
secondary: "{palette.gray.100}",
|
|
928
|
+
"secondary-foreground": "{palette.gray.900}",
|
|
929
|
+
muted: "{palette.gray.100}",
|
|
930
|
+
"muted-foreground": "{palette.gray.500}",
|
|
931
|
+
accent: "{palette.gray.100}",
|
|
932
|
+
"accent-foreground": "{palette.gray.900}",
|
|
933
|
+
destructive: "{palette.red.500}",
|
|
934
|
+
"destructive-foreground": "{palette.white}",
|
|
935
|
+
border: "{palette.gray.200}",
|
|
936
|
+
input: "{palette.gray.200}",
|
|
937
|
+
ring: "{palette.gray.400}",
|
|
938
|
+
skeleton: "{palette.gray.200}"
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
// src/tokens/themes/typography/sans.json
|
|
943
|
+
var sans_default = {
|
|
944
|
+
_createdBy: "shru-design-system library",
|
|
945
|
+
font: {
|
|
946
|
+
body: "system-ui, -apple-system, sans-serif",
|
|
947
|
+
sans: "system-ui, -apple-system, sans-serif"
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
// src/tokens/themes/typography/serif.json
|
|
952
|
+
var serif_default = {
|
|
953
|
+
_createdBy: "shru-design-system library",
|
|
954
|
+
font: {
|
|
955
|
+
body: "Georgia, serif",
|
|
956
|
+
sans: "Georgia, serif"
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
// src/tokens/themes/shape/smooth.json
|
|
961
|
+
var smooth_default = {
|
|
962
|
+
_createdBy: "shru-design-system library",
|
|
963
|
+
radius: {
|
|
964
|
+
button: "0.5rem",
|
|
965
|
+
card: "0.75rem",
|
|
966
|
+
input: "0.5rem"
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
|
|
970
|
+
// src/tokens/themes/shape/sharp.json
|
|
971
|
+
var sharp_default = {
|
|
972
|
+
_createdBy: "shru-design-system library",
|
|
973
|
+
radius: {
|
|
974
|
+
button: "0",
|
|
975
|
+
card: "0",
|
|
976
|
+
input: "0"
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
|
|
980
|
+
// src/tokens/themes/density/comfortable.json
|
|
981
|
+
var comfortable_default = {
|
|
982
|
+
_createdBy: "shru-design-system library",
|
|
983
|
+
spacing: {
|
|
984
|
+
component: {
|
|
985
|
+
xs: "0.5rem",
|
|
986
|
+
sm: "0.75rem",
|
|
987
|
+
md: "1.25rem",
|
|
988
|
+
lg: "2rem",
|
|
989
|
+
xl: "2.5rem"
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
// src/tokens/themes/density/compact.json
|
|
995
|
+
var compact_default = {
|
|
996
|
+
_createdBy: "shru-design-system library",
|
|
997
|
+
spacing: {
|
|
998
|
+
component: {
|
|
999
|
+
xs: "0.25rem",
|
|
1000
|
+
sm: "0.5rem",
|
|
1001
|
+
md: "0.75rem",
|
|
1002
|
+
lg: "1rem",
|
|
1003
|
+
xl: "1.5rem"
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
|
|
1008
|
+
// src/tokens/themes/animation/gentle.json
|
|
1009
|
+
var gentle_default = {
|
|
1010
|
+
_createdBy: "shru-design-system library",
|
|
1011
|
+
animation: {
|
|
1012
|
+
duration: {
|
|
1013
|
+
fast: "150ms",
|
|
1014
|
+
normal: "300ms",
|
|
1015
|
+
slow: "500ms"
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
|
|
1020
|
+
// src/tokens/themes/animation/brisk.json
|
|
1021
|
+
var brisk_default = {
|
|
1022
|
+
_createdBy: "shru-design-system library",
|
|
1023
|
+
animation: {
|
|
1024
|
+
duration: {
|
|
1025
|
+
fast: "100ms",
|
|
1026
|
+
normal: "200ms",
|
|
1027
|
+
slow: "300ms"
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
|
|
1032
|
+
// src/tokens/themes/custom/brand.json
|
|
1033
|
+
var brand_default = {
|
|
1034
|
+
_createdBy: "shru-design-system library",
|
|
1035
|
+
color: {
|
|
1036
|
+
primary: "{palette.purple.600}",
|
|
1037
|
+
"primary-foreground": "{palette.white}",
|
|
1038
|
+
accent: "{palette.pink.500}",
|
|
1039
|
+
"accent-foreground": "{palette.white}"
|
|
1040
|
+
},
|
|
1041
|
+
radius: {
|
|
1042
|
+
button: "0.5rem",
|
|
1043
|
+
card: "0.75rem"
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
|
|
1047
|
+
// src/tokens/themes/custom/minimal.json
|
|
1048
|
+
var minimal_default = {
|
|
1049
|
+
_createdBy: "shru-design-system library",
|
|
1050
|
+
color: {
|
|
1051
|
+
primary: "{palette.gray.700}",
|
|
1052
|
+
"primary-foreground": "{palette.white}",
|
|
1053
|
+
background: "{palette.white}",
|
|
1054
|
+
foreground: "{palette.gray.900}"
|
|
1055
|
+
},
|
|
1056
|
+
spacing: {
|
|
1057
|
+
component: {
|
|
1058
|
+
xs: "0.375rem",
|
|
1059
|
+
sm: "0.5rem",
|
|
1060
|
+
md: "0.75rem"
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
|
|
1065
|
+
// src/themes/tokenLoader.ts
|
|
1066
|
+
var EMBEDDED_TOKEN_BASE = "__EMBEDDED__";
|
|
1067
|
+
var EMBEDDED_TOKENS = {
|
|
1068
|
+
"base.json": base_default,
|
|
1069
|
+
"palettes.json": palettes_default,
|
|
1070
|
+
"themes/color/dark.json": dark_default,
|
|
1071
|
+
"themes/color/white.json": white_default,
|
|
1072
|
+
"themes/typography/sans.json": sans_default,
|
|
1073
|
+
"themes/typography/serif.json": serif_default,
|
|
1074
|
+
"themes/shape/smooth.json": smooth_default,
|
|
1075
|
+
"themes/shape/sharp.json": sharp_default,
|
|
1076
|
+
"themes/density/comfortable.json": comfortable_default,
|
|
1077
|
+
"themes/density/compact.json": compact_default,
|
|
1078
|
+
"themes/animation/gentle.json": gentle_default,
|
|
1079
|
+
"themes/animation/brisk.json": brisk_default,
|
|
1080
|
+
"themes/custom/brand.json": brand_default,
|
|
1081
|
+
"themes/custom/minimal.json": minimal_default
|
|
1082
|
+
};
|
|
1083
|
+
function normalizeTokenPath(path) {
|
|
1084
|
+
return path.replace(/^\/?tokens\//, "");
|
|
1085
|
+
}
|
|
1086
|
+
function getEmbeddedToken(normalizedPath) {
|
|
1087
|
+
return EMBEDDED_TOKENS[normalizedPath];
|
|
1088
|
+
}
|
|
1089
|
+
|
|
765
1090
|
// src/themes/themeUtils.ts
|
|
766
1091
|
function getThemeName(selectedThemes) {
|
|
767
1092
|
const parts = [];
|
|
@@ -824,31 +1149,57 @@ function deepMerge(target, source) {
|
|
|
824
1149
|
return output;
|
|
825
1150
|
}
|
|
826
1151
|
var tokenCache = /* @__PURE__ */ new Map();
|
|
1152
|
+
function getTokenBaseCandidates() {
|
|
1153
|
+
const bases = [];
|
|
1154
|
+
if (typeof window !== "undefined" && window.__THEME_TOKENS_BASE__) {
|
|
1155
|
+
bases.push(window.__THEME_TOKENS_BASE__);
|
|
1156
|
+
}
|
|
1157
|
+
const env = typeof globalThis !== "undefined" ? globalThis.process?.env : void 0;
|
|
1158
|
+
if (env?.DESIGN_SYSTEM_TOKENS_BASE) {
|
|
1159
|
+
bases.push(env.DESIGN_SYSTEM_TOKENS_BASE);
|
|
1160
|
+
}
|
|
1161
|
+
bases.push(EMBEDDED_TOKEN_BASE);
|
|
1162
|
+
bases.push("/tokens");
|
|
1163
|
+
return Array.from(new Set(bases.filter(Boolean)));
|
|
1164
|
+
}
|
|
827
1165
|
async function loadTokenFile(path) {
|
|
828
|
-
|
|
829
|
-
|
|
1166
|
+
const normalizedPath = normalizeTokenPath(path);
|
|
1167
|
+
if (tokenCache.has(normalizedPath)) {
|
|
1168
|
+
return deepClone(tokenCache.get(normalizedPath));
|
|
830
1169
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
if (
|
|
834
|
-
|
|
835
|
-
|
|
1170
|
+
const bases = getTokenBaseCandidates();
|
|
1171
|
+
for (const base of bases) {
|
|
1172
|
+
if (base === EMBEDDED_TOKEN_BASE) {
|
|
1173
|
+
const embedded = getEmbeddedToken(normalizedPath);
|
|
1174
|
+
if (embedded) {
|
|
1175
|
+
tokenCache.set(normalizedPath, embedded);
|
|
1176
|
+
return deepClone(embedded);
|
|
836
1177
|
}
|
|
837
|
-
|
|
838
|
-
}
|
|
839
|
-
const contentType = response.headers.get("content-type");
|
|
840
|
-
if (!contentType || !contentType.includes("application/json")) {
|
|
841
|
-
return null;
|
|
1178
|
+
continue;
|
|
842
1179
|
}
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
1180
|
+
const url = base.endsWith("/") ? `${base}${normalizedPath}` : `${base}/${normalizedPath}`;
|
|
1181
|
+
try {
|
|
1182
|
+
const response = await fetch(url);
|
|
1183
|
+
if (!response.ok) {
|
|
1184
|
+
if (response.status === 404) {
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
throw new Error(`Failed to load ${url}: ${response.statusText}`);
|
|
1188
|
+
}
|
|
1189
|
+
const contentType = response.headers.get("content-type");
|
|
1190
|
+
if (!contentType || !contentType.includes("application/json")) {
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
const data = await response.json();
|
|
1194
|
+
tokenCache.set(normalizedPath, data);
|
|
1195
|
+
return deepClone(data);
|
|
1196
|
+
} catch (error) {
|
|
1197
|
+
if (typeof window !== "undefined" && window.__DESIGN_SYSTEM_DEBUG__) {
|
|
1198
|
+
console.warn(`Error loading token file ${url}:`, error);
|
|
1199
|
+
}
|
|
849
1200
|
}
|
|
850
|
-
return null;
|
|
851
1201
|
}
|
|
1202
|
+
return null;
|
|
852
1203
|
}
|
|
853
1204
|
function resolveReferences(tokens, palette) {
|
|
854
1205
|
const resolved = JSON.parse(JSON.stringify(tokens));
|
|
@@ -1495,10 +1846,24 @@ function ThemeRingAsync({
|
|
|
1495
1846
|
|
|
1496
1847
|
// src/themes/applyThemeSync.ts
|
|
1497
1848
|
var STORAGE_KEY2 = "design-system-theme";
|
|
1849
|
+
function getTokenBaseCandidatesSync() {
|
|
1850
|
+
const bases = [];
|
|
1851
|
+
if (typeof window !== "undefined" && window.__THEME_TOKENS_BASE__) {
|
|
1852
|
+
bases.push(window.__THEME_TOKENS_BASE__);
|
|
1853
|
+
}
|
|
1854
|
+
const env = typeof globalThis !== "undefined" ? globalThis.process?.env : void 0;
|
|
1855
|
+
if (env?.DESIGN_SYSTEM_TOKENS_BASE) {
|
|
1856
|
+
bases.push(env.DESIGN_SYSTEM_TOKENS_BASE);
|
|
1857
|
+
}
|
|
1858
|
+
bases.push(EMBEDDED_TOKEN_BASE);
|
|
1859
|
+
bases.push("/tokens");
|
|
1860
|
+
return Array.from(new Set(bases.filter(Boolean)));
|
|
1861
|
+
}
|
|
1498
1862
|
function applyThemeSync() {
|
|
1499
1863
|
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
1500
1864
|
return;
|
|
1501
1865
|
}
|
|
1866
|
+
const tokenBases = getTokenBaseCandidatesSync();
|
|
1502
1867
|
let selectedThemes = getDefaultThemes();
|
|
1503
1868
|
try {
|
|
1504
1869
|
const stored = localStorage.getItem(STORAGE_KEY2);
|
|
@@ -1508,17 +1873,17 @@ function applyThemeSync() {
|
|
|
1508
1873
|
} catch {
|
|
1509
1874
|
}
|
|
1510
1875
|
try {
|
|
1511
|
-
const base = loadJSONSync("/tokens/base.json");
|
|
1876
|
+
const base = loadJSONSync("/tokens/base.json", tokenBases);
|
|
1512
1877
|
if (!base) {
|
|
1513
1878
|
return;
|
|
1514
1879
|
}
|
|
1515
|
-
const palettes = loadJSONSync("/tokens/palettes.json");
|
|
1880
|
+
const palettes = loadJSONSync("/tokens/palettes.json", tokenBases);
|
|
1516
1881
|
const palette = palettes?.palette || {};
|
|
1517
1882
|
let merged = deepMergeSync(base, { palette });
|
|
1518
1883
|
for (const category of THEME_CATEGORY_ORDER) {
|
|
1519
1884
|
const themeId = selectedThemes[category];
|
|
1520
1885
|
if (!themeId) continue;
|
|
1521
|
-
const themeData = loadJSONSync(`/tokens/themes/${category}/${themeId}.json
|
|
1886
|
+
const themeData = loadJSONSync(`/tokens/themes/${category}/${themeId}.json`, tokenBases);
|
|
1522
1887
|
if (themeData) {
|
|
1523
1888
|
merged = deepMergeSync(merged, themeData);
|
|
1524
1889
|
}
|
|
@@ -1533,8 +1898,8 @@ ${Object.entries(mappedVars).map(([key, value]) => ` ${key}: ${value};`).join("
|
|
|
1533
1898
|
if (!styleTag) {
|
|
1534
1899
|
styleTag = document.createElement("style");
|
|
1535
1900
|
styleTag.id = "dynamic-theme";
|
|
1536
|
-
document.head.insertBefore(styleTag, document.head.firstChild);
|
|
1537
1901
|
}
|
|
1902
|
+
document.head.appendChild(styleTag);
|
|
1538
1903
|
styleTag.textContent = css;
|
|
1539
1904
|
} catch (error) {
|
|
1540
1905
|
if (typeof window !== "undefined" && window.__DESIGN_SYSTEM_DEBUG__) {
|
|
@@ -1542,22 +1907,33 @@ ${Object.entries(mappedVars).map(([key, value]) => ` ${key}: ${value};`).join("
|
|
|
1542
1907
|
}
|
|
1543
1908
|
}
|
|
1544
1909
|
}
|
|
1545
|
-
function loadJSONSync(path) {
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
}
|
|
1553
|
-
if (xhr.status === 200 || xhr.status === 0) {
|
|
1554
|
-
const contentType = xhr.getResponseHeader("content-type");
|
|
1555
|
-
if (contentType && contentType.includes("application/json")) {
|
|
1556
|
-
return JSON.parse(xhr.responseText);
|
|
1910
|
+
function loadJSONSync(path, bases) {
|
|
1911
|
+
const normalizedPath = normalizeTokenPath(path);
|
|
1912
|
+
for (const base of bases) {
|
|
1913
|
+
if (base === EMBEDDED_TOKEN_BASE) {
|
|
1914
|
+
const embedded = getEmbeddedToken(normalizedPath);
|
|
1915
|
+
if (embedded) {
|
|
1916
|
+
return deepCloneSync(embedded);
|
|
1557
1917
|
}
|
|
1558
|
-
|
|
1918
|
+
continue;
|
|
1919
|
+
}
|
|
1920
|
+
try {
|
|
1921
|
+
const xhr = new XMLHttpRequest();
|
|
1922
|
+
const url = base.endsWith("/") ? `${base}${normalizedPath}` : `${base}/${normalizedPath}`;
|
|
1923
|
+
xhr.open("GET", url, false);
|
|
1924
|
+
xhr.send(null);
|
|
1925
|
+
if (xhr.status === 404) {
|
|
1926
|
+
continue;
|
|
1927
|
+
}
|
|
1928
|
+
if (xhr.status === 200 || xhr.status === 0) {
|
|
1929
|
+
const contentType = xhr.getResponseHeader("content-type");
|
|
1930
|
+
if (contentType && contentType.includes("application/json")) {
|
|
1931
|
+
return JSON.parse(xhr.responseText);
|
|
1932
|
+
}
|
|
1933
|
+
continue;
|
|
1934
|
+
}
|
|
1935
|
+
} catch {
|
|
1559
1936
|
}
|
|
1560
|
-
} catch {
|
|
1561
1937
|
}
|
|
1562
1938
|
return null;
|
|
1563
1939
|
}
|
|
@@ -1578,6 +1954,9 @@ function deepMergeSync(target, source) {
|
|
|
1578
1954
|
}
|
|
1579
1955
|
return output;
|
|
1580
1956
|
}
|
|
1957
|
+
function deepCloneSync(value) {
|
|
1958
|
+
return JSON.parse(JSON.stringify(value));
|
|
1959
|
+
}
|
|
1581
1960
|
function isObjectSync(item) {
|
|
1582
1961
|
return item && typeof item === "object" && !Array.isArray(item);
|
|
1583
1962
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -735,6 +735,331 @@ async function getTheme(category, themeId) {
|
|
|
735
735
|
return categories[category]?.themes[themeId] || null;
|
|
736
736
|
}
|
|
737
737
|
|
|
738
|
+
// src/tokens/base.json
|
|
739
|
+
var base_default = {
|
|
740
|
+
_createdBy: "shru-design-system library",
|
|
741
|
+
color: {
|
|
742
|
+
primary: "{palette.blue.500}",
|
|
743
|
+
"primary-hover": "{palette.blue.600}",
|
|
744
|
+
"primary-foreground": "{palette.white}",
|
|
745
|
+
secondary: "{palette.gray.100}",
|
|
746
|
+
"secondary-foreground": "{palette.gray.900}",
|
|
747
|
+
background: "{palette.white}",
|
|
748
|
+
foreground: "{palette.gray.900}",
|
|
749
|
+
card: "{palette.white}",
|
|
750
|
+
"card-foreground": "{palette.gray.900}",
|
|
751
|
+
popover: "{palette.white}",
|
|
752
|
+
"popover-foreground": "{palette.gray.900}",
|
|
753
|
+
muted: "{palette.gray.100}",
|
|
754
|
+
"muted-foreground": "{palette.gray.500}",
|
|
755
|
+
accent: "{palette.gray.100}",
|
|
756
|
+
"accent-foreground": "{palette.gray.900}",
|
|
757
|
+
destructive: "{palette.red.500}",
|
|
758
|
+
"destructive-foreground": "{palette.white}",
|
|
759
|
+
border: "{palette.gray.200}",
|
|
760
|
+
input: "{palette.gray.200}",
|
|
761
|
+
ring: "{palette.gray.400}",
|
|
762
|
+
skeleton: "{palette.gray.200}"
|
|
763
|
+
},
|
|
764
|
+
spacing: {
|
|
765
|
+
component: {
|
|
766
|
+
xs: "0.25rem",
|
|
767
|
+
sm: "0.5rem",
|
|
768
|
+
md: "1rem",
|
|
769
|
+
lg: "1.5rem",
|
|
770
|
+
xl: "2rem"
|
|
771
|
+
},
|
|
772
|
+
base: "0.25rem"
|
|
773
|
+
},
|
|
774
|
+
font: {
|
|
775
|
+
body: "var(--font-sans)",
|
|
776
|
+
sans: "var(--font-sans)",
|
|
777
|
+
mono: "var(--font-mono)"
|
|
778
|
+
},
|
|
779
|
+
radius: {
|
|
780
|
+
button: "0.375rem",
|
|
781
|
+
card: "0.5rem",
|
|
782
|
+
input: "0.375rem"
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
// src/tokens/palettes.json
|
|
787
|
+
var palettes_default = {
|
|
788
|
+
_createdBy: "shru-design-system library",
|
|
789
|
+
palette: {
|
|
790
|
+
white: "#ffffff",
|
|
791
|
+
black: "#000000",
|
|
792
|
+
transparent: "transparent",
|
|
793
|
+
gray: {
|
|
794
|
+
"50": "#f9fafb",
|
|
795
|
+
"100": "#f3f4f6",
|
|
796
|
+
"200": "#e5e7eb",
|
|
797
|
+
"300": "#d1d5db",
|
|
798
|
+
"400": "#9ca3af",
|
|
799
|
+
"500": "#6b7280",
|
|
800
|
+
"600": "#4b5563",
|
|
801
|
+
"700": "#374151",
|
|
802
|
+
"800": "#1f2937",
|
|
803
|
+
"900": "#111827",
|
|
804
|
+
"950": "#030712"
|
|
805
|
+
},
|
|
806
|
+
blue: {
|
|
807
|
+
"50": "#eff6ff",
|
|
808
|
+
"100": "#dbeafe",
|
|
809
|
+
"200": "#bfdbfe",
|
|
810
|
+
"300": "#93c5fd",
|
|
811
|
+
"400": "#60a5fa",
|
|
812
|
+
"500": "#3b82f6",
|
|
813
|
+
"600": "#2563eb",
|
|
814
|
+
"700": "#1d4ed8",
|
|
815
|
+
"800": "#1e40af",
|
|
816
|
+
"900": "#1e3a8a",
|
|
817
|
+
"950": "#172554"
|
|
818
|
+
},
|
|
819
|
+
red: {
|
|
820
|
+
"50": "#fef2f2",
|
|
821
|
+
"100": "#fee2e2",
|
|
822
|
+
"200": "#fecaca",
|
|
823
|
+
"300": "#fca5a5",
|
|
824
|
+
"400": "#f87171",
|
|
825
|
+
"500": "#ef4444",
|
|
826
|
+
"600": "#dc2626",
|
|
827
|
+
"700": "#b91c1c",
|
|
828
|
+
"800": "#991b1b",
|
|
829
|
+
"900": "#7f1d1d",
|
|
830
|
+
"950": "#450a0a"
|
|
831
|
+
},
|
|
832
|
+
purple: {
|
|
833
|
+
"50": "#faf5ff",
|
|
834
|
+
"100": "#f3e8ff",
|
|
835
|
+
"200": "#e9d5ff",
|
|
836
|
+
"300": "#d8b4fe",
|
|
837
|
+
"400": "#c084fc",
|
|
838
|
+
"500": "#a855f7",
|
|
839
|
+
"600": "#9333ea",
|
|
840
|
+
"700": "#7e22ce",
|
|
841
|
+
"800": "#6b21a8",
|
|
842
|
+
"900": "#581c87",
|
|
843
|
+
"950": "#3b0764"
|
|
844
|
+
},
|
|
845
|
+
pink: {
|
|
846
|
+
"50": "#fdf2f8",
|
|
847
|
+
"100": "#fce7f3",
|
|
848
|
+
"200": "#fbcfe8",
|
|
849
|
+
"300": "#f9a8d4",
|
|
850
|
+
"400": "#f472b6",
|
|
851
|
+
"500": "#ec4899",
|
|
852
|
+
"600": "#db2777",
|
|
853
|
+
"700": "#be185d",
|
|
854
|
+
"800": "#9f1239",
|
|
855
|
+
"900": "#831843",
|
|
856
|
+
"950": "#500724"
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
// src/tokens/themes/color/dark.json
|
|
862
|
+
var dark_default = {
|
|
863
|
+
_createdBy: "shru-design-system library",
|
|
864
|
+
color: {
|
|
865
|
+
primary: "{palette.blue.400}",
|
|
866
|
+
"primary-foreground": "{palette.gray.900}",
|
|
867
|
+
background: "{palette.gray.900}",
|
|
868
|
+
foreground: "{palette.gray.50}",
|
|
869
|
+
card: "{palette.gray.800}",
|
|
870
|
+
"card-foreground": "{palette.gray.50}",
|
|
871
|
+
popover: "{palette.gray.800}",
|
|
872
|
+
"popover-foreground": "{palette.gray.50}",
|
|
873
|
+
secondary: "{palette.gray.800}",
|
|
874
|
+
"secondary-foreground": "{palette.gray.50}",
|
|
875
|
+
muted: "{palette.gray.800}",
|
|
876
|
+
"muted-foreground": "{palette.gray.400}",
|
|
877
|
+
accent: "{palette.gray.800}",
|
|
878
|
+
"accent-foreground": "{palette.gray.50}",
|
|
879
|
+
destructive: "{palette.red.500}",
|
|
880
|
+
"destructive-foreground": "{palette.white}",
|
|
881
|
+
border: "{palette.gray.700}",
|
|
882
|
+
input: "{palette.gray.700}",
|
|
883
|
+
ring: "{palette.gray.600}",
|
|
884
|
+
skeleton: "{palette.gray.700}"
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
// src/tokens/themes/color/white.json
|
|
889
|
+
var white_default = {
|
|
890
|
+
_createdBy: "shru-design-system library",
|
|
891
|
+
color: {
|
|
892
|
+
primary: "{palette.blue.500}",
|
|
893
|
+
"primary-foreground": "{palette.white}",
|
|
894
|
+
background: "{palette.white}",
|
|
895
|
+
foreground: "{palette.gray.900}",
|
|
896
|
+
card: "{palette.white}",
|
|
897
|
+
"card-foreground": "{palette.gray.900}",
|
|
898
|
+
popover: "{palette.white}",
|
|
899
|
+
"popover-foreground": "{palette.gray.900}",
|
|
900
|
+
secondary: "{palette.gray.100}",
|
|
901
|
+
"secondary-foreground": "{palette.gray.900}",
|
|
902
|
+
muted: "{palette.gray.100}",
|
|
903
|
+
"muted-foreground": "{palette.gray.500}",
|
|
904
|
+
accent: "{palette.gray.100}",
|
|
905
|
+
"accent-foreground": "{palette.gray.900}",
|
|
906
|
+
destructive: "{palette.red.500}",
|
|
907
|
+
"destructive-foreground": "{palette.white}",
|
|
908
|
+
border: "{palette.gray.200}",
|
|
909
|
+
input: "{palette.gray.200}",
|
|
910
|
+
ring: "{palette.gray.400}",
|
|
911
|
+
skeleton: "{palette.gray.200}"
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
// src/tokens/themes/typography/sans.json
|
|
916
|
+
var sans_default = {
|
|
917
|
+
_createdBy: "shru-design-system library",
|
|
918
|
+
font: {
|
|
919
|
+
body: "system-ui, -apple-system, sans-serif",
|
|
920
|
+
sans: "system-ui, -apple-system, sans-serif"
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
// src/tokens/themes/typography/serif.json
|
|
925
|
+
var serif_default = {
|
|
926
|
+
_createdBy: "shru-design-system library",
|
|
927
|
+
font: {
|
|
928
|
+
body: "Georgia, serif",
|
|
929
|
+
sans: "Georgia, serif"
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
// src/tokens/themes/shape/smooth.json
|
|
934
|
+
var smooth_default = {
|
|
935
|
+
_createdBy: "shru-design-system library",
|
|
936
|
+
radius: {
|
|
937
|
+
button: "0.5rem",
|
|
938
|
+
card: "0.75rem",
|
|
939
|
+
input: "0.5rem"
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
// src/tokens/themes/shape/sharp.json
|
|
944
|
+
var sharp_default = {
|
|
945
|
+
_createdBy: "shru-design-system library",
|
|
946
|
+
radius: {
|
|
947
|
+
button: "0",
|
|
948
|
+
card: "0",
|
|
949
|
+
input: "0"
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
|
|
953
|
+
// src/tokens/themes/density/comfortable.json
|
|
954
|
+
var comfortable_default = {
|
|
955
|
+
_createdBy: "shru-design-system library",
|
|
956
|
+
spacing: {
|
|
957
|
+
component: {
|
|
958
|
+
xs: "0.5rem",
|
|
959
|
+
sm: "0.75rem",
|
|
960
|
+
md: "1.25rem",
|
|
961
|
+
lg: "2rem",
|
|
962
|
+
xl: "2.5rem"
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
|
|
967
|
+
// src/tokens/themes/density/compact.json
|
|
968
|
+
var compact_default = {
|
|
969
|
+
_createdBy: "shru-design-system library",
|
|
970
|
+
spacing: {
|
|
971
|
+
component: {
|
|
972
|
+
xs: "0.25rem",
|
|
973
|
+
sm: "0.5rem",
|
|
974
|
+
md: "0.75rem",
|
|
975
|
+
lg: "1rem",
|
|
976
|
+
xl: "1.5rem"
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
// src/tokens/themes/animation/gentle.json
|
|
982
|
+
var gentle_default = {
|
|
983
|
+
_createdBy: "shru-design-system library",
|
|
984
|
+
animation: {
|
|
985
|
+
duration: {
|
|
986
|
+
fast: "150ms",
|
|
987
|
+
normal: "300ms",
|
|
988
|
+
slow: "500ms"
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
|
|
993
|
+
// src/tokens/themes/animation/brisk.json
|
|
994
|
+
var brisk_default = {
|
|
995
|
+
_createdBy: "shru-design-system library",
|
|
996
|
+
animation: {
|
|
997
|
+
duration: {
|
|
998
|
+
fast: "100ms",
|
|
999
|
+
normal: "200ms",
|
|
1000
|
+
slow: "300ms"
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
// src/tokens/themes/custom/brand.json
|
|
1006
|
+
var brand_default = {
|
|
1007
|
+
_createdBy: "shru-design-system library",
|
|
1008
|
+
color: {
|
|
1009
|
+
primary: "{palette.purple.600}",
|
|
1010
|
+
"primary-foreground": "{palette.white}",
|
|
1011
|
+
accent: "{palette.pink.500}",
|
|
1012
|
+
"accent-foreground": "{palette.white}"
|
|
1013
|
+
},
|
|
1014
|
+
radius: {
|
|
1015
|
+
button: "0.5rem",
|
|
1016
|
+
card: "0.75rem"
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
|
|
1020
|
+
// src/tokens/themes/custom/minimal.json
|
|
1021
|
+
var minimal_default = {
|
|
1022
|
+
_createdBy: "shru-design-system library",
|
|
1023
|
+
color: {
|
|
1024
|
+
primary: "{palette.gray.700}",
|
|
1025
|
+
"primary-foreground": "{palette.white}",
|
|
1026
|
+
background: "{palette.white}",
|
|
1027
|
+
foreground: "{palette.gray.900}"
|
|
1028
|
+
},
|
|
1029
|
+
spacing: {
|
|
1030
|
+
component: {
|
|
1031
|
+
xs: "0.375rem",
|
|
1032
|
+
sm: "0.5rem",
|
|
1033
|
+
md: "0.75rem"
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
// src/themes/tokenLoader.ts
|
|
1039
|
+
var EMBEDDED_TOKEN_BASE = "__EMBEDDED__";
|
|
1040
|
+
var EMBEDDED_TOKENS = {
|
|
1041
|
+
"base.json": base_default,
|
|
1042
|
+
"palettes.json": palettes_default,
|
|
1043
|
+
"themes/color/dark.json": dark_default,
|
|
1044
|
+
"themes/color/white.json": white_default,
|
|
1045
|
+
"themes/typography/sans.json": sans_default,
|
|
1046
|
+
"themes/typography/serif.json": serif_default,
|
|
1047
|
+
"themes/shape/smooth.json": smooth_default,
|
|
1048
|
+
"themes/shape/sharp.json": sharp_default,
|
|
1049
|
+
"themes/density/comfortable.json": comfortable_default,
|
|
1050
|
+
"themes/density/compact.json": compact_default,
|
|
1051
|
+
"themes/animation/gentle.json": gentle_default,
|
|
1052
|
+
"themes/animation/brisk.json": brisk_default,
|
|
1053
|
+
"themes/custom/brand.json": brand_default,
|
|
1054
|
+
"themes/custom/minimal.json": minimal_default
|
|
1055
|
+
};
|
|
1056
|
+
function normalizeTokenPath(path) {
|
|
1057
|
+
return path.replace(/^\/?tokens\//, "");
|
|
1058
|
+
}
|
|
1059
|
+
function getEmbeddedToken(normalizedPath) {
|
|
1060
|
+
return EMBEDDED_TOKENS[normalizedPath];
|
|
1061
|
+
}
|
|
1062
|
+
|
|
738
1063
|
// src/themes/themeUtils.ts
|
|
739
1064
|
function getThemeName(selectedThemes) {
|
|
740
1065
|
const parts = [];
|
|
@@ -797,31 +1122,57 @@ function deepMerge(target, source) {
|
|
|
797
1122
|
return output;
|
|
798
1123
|
}
|
|
799
1124
|
var tokenCache = /* @__PURE__ */ new Map();
|
|
1125
|
+
function getTokenBaseCandidates() {
|
|
1126
|
+
const bases = [];
|
|
1127
|
+
if (typeof window !== "undefined" && window.__THEME_TOKENS_BASE__) {
|
|
1128
|
+
bases.push(window.__THEME_TOKENS_BASE__);
|
|
1129
|
+
}
|
|
1130
|
+
const env = typeof globalThis !== "undefined" ? globalThis.process?.env : void 0;
|
|
1131
|
+
if (env?.DESIGN_SYSTEM_TOKENS_BASE) {
|
|
1132
|
+
bases.push(env.DESIGN_SYSTEM_TOKENS_BASE);
|
|
1133
|
+
}
|
|
1134
|
+
bases.push(EMBEDDED_TOKEN_BASE);
|
|
1135
|
+
bases.push("/tokens");
|
|
1136
|
+
return Array.from(new Set(bases.filter(Boolean)));
|
|
1137
|
+
}
|
|
800
1138
|
async function loadTokenFile(path) {
|
|
801
|
-
|
|
802
|
-
|
|
1139
|
+
const normalizedPath = normalizeTokenPath(path);
|
|
1140
|
+
if (tokenCache.has(normalizedPath)) {
|
|
1141
|
+
return deepClone(tokenCache.get(normalizedPath));
|
|
803
1142
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
if (
|
|
807
|
-
|
|
808
|
-
|
|
1143
|
+
const bases = getTokenBaseCandidates();
|
|
1144
|
+
for (const base of bases) {
|
|
1145
|
+
if (base === EMBEDDED_TOKEN_BASE) {
|
|
1146
|
+
const embedded = getEmbeddedToken(normalizedPath);
|
|
1147
|
+
if (embedded) {
|
|
1148
|
+
tokenCache.set(normalizedPath, embedded);
|
|
1149
|
+
return deepClone(embedded);
|
|
809
1150
|
}
|
|
810
|
-
|
|
811
|
-
}
|
|
812
|
-
const contentType = response.headers.get("content-type");
|
|
813
|
-
if (!contentType || !contentType.includes("application/json")) {
|
|
814
|
-
return null;
|
|
1151
|
+
continue;
|
|
815
1152
|
}
|
|
816
|
-
const
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1153
|
+
const url = base.endsWith("/") ? `${base}${normalizedPath}` : `${base}/${normalizedPath}`;
|
|
1154
|
+
try {
|
|
1155
|
+
const response = await fetch(url);
|
|
1156
|
+
if (!response.ok) {
|
|
1157
|
+
if (response.status === 404) {
|
|
1158
|
+
continue;
|
|
1159
|
+
}
|
|
1160
|
+
throw new Error(`Failed to load ${url}: ${response.statusText}`);
|
|
1161
|
+
}
|
|
1162
|
+
const contentType = response.headers.get("content-type");
|
|
1163
|
+
if (!contentType || !contentType.includes("application/json")) {
|
|
1164
|
+
continue;
|
|
1165
|
+
}
|
|
1166
|
+
const data = await response.json();
|
|
1167
|
+
tokenCache.set(normalizedPath, data);
|
|
1168
|
+
return deepClone(data);
|
|
1169
|
+
} catch (error) {
|
|
1170
|
+
if (typeof window !== "undefined" && window.__DESIGN_SYSTEM_DEBUG__) {
|
|
1171
|
+
console.warn(`Error loading token file ${url}:`, error);
|
|
1172
|
+
}
|
|
822
1173
|
}
|
|
823
|
-
return null;
|
|
824
1174
|
}
|
|
1175
|
+
return null;
|
|
825
1176
|
}
|
|
826
1177
|
function resolveReferences(tokens, palette) {
|
|
827
1178
|
const resolved = JSON.parse(JSON.stringify(tokens));
|
|
@@ -1468,10 +1819,24 @@ function ThemeRingAsync({
|
|
|
1468
1819
|
|
|
1469
1820
|
// src/themes/applyThemeSync.ts
|
|
1470
1821
|
var STORAGE_KEY2 = "design-system-theme";
|
|
1822
|
+
function getTokenBaseCandidatesSync() {
|
|
1823
|
+
const bases = [];
|
|
1824
|
+
if (typeof window !== "undefined" && window.__THEME_TOKENS_BASE__) {
|
|
1825
|
+
bases.push(window.__THEME_TOKENS_BASE__);
|
|
1826
|
+
}
|
|
1827
|
+
const env = typeof globalThis !== "undefined" ? globalThis.process?.env : void 0;
|
|
1828
|
+
if (env?.DESIGN_SYSTEM_TOKENS_BASE) {
|
|
1829
|
+
bases.push(env.DESIGN_SYSTEM_TOKENS_BASE);
|
|
1830
|
+
}
|
|
1831
|
+
bases.push(EMBEDDED_TOKEN_BASE);
|
|
1832
|
+
bases.push("/tokens");
|
|
1833
|
+
return Array.from(new Set(bases.filter(Boolean)));
|
|
1834
|
+
}
|
|
1471
1835
|
function applyThemeSync() {
|
|
1472
1836
|
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
1473
1837
|
return;
|
|
1474
1838
|
}
|
|
1839
|
+
const tokenBases = getTokenBaseCandidatesSync();
|
|
1475
1840
|
let selectedThemes = getDefaultThemes();
|
|
1476
1841
|
try {
|
|
1477
1842
|
const stored = localStorage.getItem(STORAGE_KEY2);
|
|
@@ -1481,17 +1846,17 @@ function applyThemeSync() {
|
|
|
1481
1846
|
} catch {
|
|
1482
1847
|
}
|
|
1483
1848
|
try {
|
|
1484
|
-
const base = loadJSONSync("/tokens/base.json");
|
|
1849
|
+
const base = loadJSONSync("/tokens/base.json", tokenBases);
|
|
1485
1850
|
if (!base) {
|
|
1486
1851
|
return;
|
|
1487
1852
|
}
|
|
1488
|
-
const palettes = loadJSONSync("/tokens/palettes.json");
|
|
1853
|
+
const palettes = loadJSONSync("/tokens/palettes.json", tokenBases);
|
|
1489
1854
|
const palette = palettes?.palette || {};
|
|
1490
1855
|
let merged = deepMergeSync(base, { palette });
|
|
1491
1856
|
for (const category of THEME_CATEGORY_ORDER) {
|
|
1492
1857
|
const themeId = selectedThemes[category];
|
|
1493
1858
|
if (!themeId) continue;
|
|
1494
|
-
const themeData = loadJSONSync(`/tokens/themes/${category}/${themeId}.json
|
|
1859
|
+
const themeData = loadJSONSync(`/tokens/themes/${category}/${themeId}.json`, tokenBases);
|
|
1495
1860
|
if (themeData) {
|
|
1496
1861
|
merged = deepMergeSync(merged, themeData);
|
|
1497
1862
|
}
|
|
@@ -1506,8 +1871,8 @@ ${Object.entries(mappedVars).map(([key, value]) => ` ${key}: ${value};`).join("
|
|
|
1506
1871
|
if (!styleTag) {
|
|
1507
1872
|
styleTag = document.createElement("style");
|
|
1508
1873
|
styleTag.id = "dynamic-theme";
|
|
1509
|
-
document.head.insertBefore(styleTag, document.head.firstChild);
|
|
1510
1874
|
}
|
|
1875
|
+
document.head.appendChild(styleTag);
|
|
1511
1876
|
styleTag.textContent = css;
|
|
1512
1877
|
} catch (error) {
|
|
1513
1878
|
if (typeof window !== "undefined" && window.__DESIGN_SYSTEM_DEBUG__) {
|
|
@@ -1515,22 +1880,33 @@ ${Object.entries(mappedVars).map(([key, value]) => ` ${key}: ${value};`).join("
|
|
|
1515
1880
|
}
|
|
1516
1881
|
}
|
|
1517
1882
|
}
|
|
1518
|
-
function loadJSONSync(path) {
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
}
|
|
1526
|
-
if (xhr.status === 200 || xhr.status === 0) {
|
|
1527
|
-
const contentType = xhr.getResponseHeader("content-type");
|
|
1528
|
-
if (contentType && contentType.includes("application/json")) {
|
|
1529
|
-
return JSON.parse(xhr.responseText);
|
|
1883
|
+
function loadJSONSync(path, bases) {
|
|
1884
|
+
const normalizedPath = normalizeTokenPath(path);
|
|
1885
|
+
for (const base of bases) {
|
|
1886
|
+
if (base === EMBEDDED_TOKEN_BASE) {
|
|
1887
|
+
const embedded = getEmbeddedToken(normalizedPath);
|
|
1888
|
+
if (embedded) {
|
|
1889
|
+
return deepCloneSync(embedded);
|
|
1530
1890
|
}
|
|
1531
|
-
|
|
1891
|
+
continue;
|
|
1892
|
+
}
|
|
1893
|
+
try {
|
|
1894
|
+
const xhr = new XMLHttpRequest();
|
|
1895
|
+
const url = base.endsWith("/") ? `${base}${normalizedPath}` : `${base}/${normalizedPath}`;
|
|
1896
|
+
xhr.open("GET", url, false);
|
|
1897
|
+
xhr.send(null);
|
|
1898
|
+
if (xhr.status === 404) {
|
|
1899
|
+
continue;
|
|
1900
|
+
}
|
|
1901
|
+
if (xhr.status === 200 || xhr.status === 0) {
|
|
1902
|
+
const contentType = xhr.getResponseHeader("content-type");
|
|
1903
|
+
if (contentType && contentType.includes("application/json")) {
|
|
1904
|
+
return JSON.parse(xhr.responseText);
|
|
1905
|
+
}
|
|
1906
|
+
continue;
|
|
1907
|
+
}
|
|
1908
|
+
} catch {
|
|
1532
1909
|
}
|
|
1533
|
-
} catch {
|
|
1534
1910
|
}
|
|
1535
1911
|
return null;
|
|
1536
1912
|
}
|
|
@@ -1551,6 +1927,9 @@ function deepMergeSync(target, source) {
|
|
|
1551
1927
|
}
|
|
1552
1928
|
return output;
|
|
1553
1929
|
}
|
|
1930
|
+
function deepCloneSync(value) {
|
|
1931
|
+
return JSON.parse(JSON.stringify(value));
|
|
1932
|
+
}
|
|
1554
1933
|
function isObjectSync(item) {
|
|
1555
1934
|
return item && typeof item === "object" && !Array.isArray(item);
|
|
1556
1935
|
}
|
package/package.json
CHANGED
|
@@ -42,6 +42,20 @@
|
|
|
42
42
|
// 2. scripts/themeConfig.js - THEME_CATEGORY_ORDER (JavaScript version)
|
|
43
43
|
const THEME_CATEGORY_ORDER = ['color', 'typography', 'shape', 'density', 'animation', 'custom'];
|
|
44
44
|
|
|
45
|
+
// Token base resolution (standalone script cannot import helpers)
|
|
46
|
+
// Order: consumer override → env override → public fallback.
|
|
47
|
+
function getTokenBaseCandidates() {
|
|
48
|
+
var bases = [];
|
|
49
|
+
if (typeof window !== 'undefined' && window.__THEME_TOKENS_BASE__) {
|
|
50
|
+
bases.push(window.__THEME_TOKENS_BASE__);
|
|
51
|
+
}
|
|
52
|
+
if (typeof process !== 'undefined' && process.env && process.env.DESIGN_SYSTEM_TOKENS_BASE) {
|
|
53
|
+
bases.push(process.env.DESIGN_SYSTEM_TOKENS_BASE);
|
|
54
|
+
}
|
|
55
|
+
bases.push('/tokens');
|
|
56
|
+
return Array.from(new Set(bases.filter(Boolean)));
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
// Get theme from localStorage (optimized - single read)
|
|
46
60
|
var selectedThemes = DEFAULT_THEMES;
|
|
47
61
|
try {
|
|
@@ -71,15 +85,18 @@
|
|
|
71
85
|
return output;
|
|
72
86
|
}
|
|
73
87
|
|
|
74
|
-
function loadJSONSync(
|
|
88
|
+
function loadJSONSync(relativePath, bases) {
|
|
89
|
+
for (var i = 0; i < bases.length; i++) {
|
|
90
|
+
var base = bases[i];
|
|
75
91
|
try {
|
|
92
|
+
var url = base.endsWith('/') ? base + relativePath : base + '/' + relativePath;
|
|
76
93
|
var xhr = new XMLHttpRequest();
|
|
77
|
-
|
|
94
|
+
xhr.open('GET', url, false);
|
|
78
95
|
xhr.send(null);
|
|
79
96
|
|
|
80
|
-
|
|
97
|
+
// 404 means file doesn't exist - try next base
|
|
81
98
|
if (xhr.status === 404) {
|
|
82
|
-
|
|
99
|
+
continue;
|
|
83
100
|
}
|
|
84
101
|
|
|
85
102
|
if (xhr.status === 200 || xhr.status === 0) {
|
|
@@ -88,10 +105,13 @@
|
|
|
88
105
|
if (contentType && contentType.includes('application/json')) {
|
|
89
106
|
return JSON.parse(xhr.responseText);
|
|
90
107
|
}
|
|
91
|
-
|
|
92
|
-
|
|
108
|
+
// If not JSON (likely HTML error page), try next base
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
} catch (e) {
|
|
112
|
+
// Try next base
|
|
93
113
|
}
|
|
94
|
-
}
|
|
114
|
+
}
|
|
95
115
|
return null;
|
|
96
116
|
}
|
|
97
117
|
|
|
@@ -252,10 +272,11 @@
|
|
|
252
272
|
|
|
253
273
|
// Apply theme synchronously (optimized)
|
|
254
274
|
try {
|
|
255
|
-
var
|
|
275
|
+
var tokenBases = getTokenBaseCandidates();
|
|
276
|
+
var base = loadJSONSync('base.json', tokenBases);
|
|
256
277
|
if (!base) return;
|
|
257
278
|
|
|
258
|
-
var palettes = loadJSONSync('
|
|
279
|
+
var palettes = loadJSONSync('palettes.json', tokenBases);
|
|
259
280
|
var palette = (palettes && palettes.palette) || {};
|
|
260
281
|
var merged = deepMerge(base, { palette: palette });
|
|
261
282
|
|
|
@@ -265,7 +286,7 @@
|
|
|
265
286
|
var themeId = selectedThemes[category];
|
|
266
287
|
if (!themeId) continue;
|
|
267
288
|
|
|
268
|
-
var themeData = loadJSONSync('
|
|
289
|
+
var themeData = loadJSONSync('themes/' + category + '/' + themeId + '.json', tokenBases);
|
|
269
290
|
if (themeData) merged = deepMerge(merged, themeData);
|
|
270
291
|
// If themeData is null, file doesn't exist - skip silently (custom themes are optional)
|
|
271
292
|
}
|
|
@@ -287,8 +308,9 @@
|
|
|
287
308
|
if (!styleTag) {
|
|
288
309
|
styleTag = document.createElement('style');
|
|
289
310
|
styleTag.id = 'dynamic-theme';
|
|
290
|
-
document.head.insertBefore(styleTag, document.head.firstChild);
|
|
291
311
|
}
|
|
312
|
+
// Always append (moves it to the end) so vars win over earlier styles
|
|
313
|
+
document.head.appendChild(styleTag);
|
|
292
314
|
styleTag.textContent = css;
|
|
293
315
|
} catch (error) {
|
|
294
316
|
// Silently fail - theme will apply via React hook
|
package/scripts/init.js
CHANGED
|
@@ -639,64 +639,10 @@ function createTokenFiles() {
|
|
|
639
639
|
}
|
|
640
640
|
}
|
|
641
641
|
|
|
642
|
+
// Legacy HTML injection is disabled. Runtime/applyThemeSync now appends the
|
|
643
|
+
// dynamic style to the end of <head>, so we no longer mutate consumer HTML.
|
|
642
644
|
function injectThemeScript() {
|
|
643
|
-
|
|
644
|
-
const scriptContent = fs.readFileSync(scriptPath, 'utf8');
|
|
645
|
-
|
|
646
|
-
// Try to find and update index.html
|
|
647
|
-
const htmlPaths = [
|
|
648
|
-
path.join(process.cwd(), 'index.html'),
|
|
649
|
-
path.join(process.cwd(), 'public', 'index.html'),
|
|
650
|
-
];
|
|
651
|
-
|
|
652
|
-
for (const htmlPath of htmlPaths) {
|
|
653
|
-
if (fs.existsSync(htmlPath)) {
|
|
654
|
-
let htmlContent = fs.readFileSync(htmlPath, 'utf8');
|
|
655
|
-
|
|
656
|
-
// Check if script is already injected
|
|
657
|
-
if (htmlContent.includes('dynamic-theme') || htmlContent.includes('apply-theme-sync')) {
|
|
658
|
-
log('Theme sync script already in index.html', 'green');
|
|
659
|
-
return;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
// Add resource hints for token files (helps browser preload)
|
|
663
|
-
// Preload critical token files that are always needed
|
|
664
|
-
const preloadLinks = ` <!-- Preload token files for faster theme application - Created by ${LIBRARY_NAME} -->
|
|
665
|
-
<link rel="preload" href="/tokens/base.json" as="fetch" crossorigin>
|
|
666
|
-
<link rel="preload" href="/tokens/palettes.json" as="fetch" crossorigin>
|
|
667
|
-
<link rel="preload" href="/tokens/themes/color/white.json" as="fetch" crossorigin>
|
|
668
|
-
<link rel="preload" href="/tokens/themes/color/dark.json" as="fetch" crossorigin>`;
|
|
669
|
-
|
|
670
|
-
// Inject blocking script in <head> (runs before React)
|
|
671
|
-
const scriptTag = ` <!-- Blocking theme script to prevent flash - Created by ${LIBRARY_NAME} -->
|
|
672
|
-
<script>${scriptContent}</script>`;
|
|
673
|
-
|
|
674
|
-
if (htmlContent.includes('</head>')) {
|
|
675
|
-
// Add preload links and script before </head>
|
|
676
|
-
htmlContent = htmlContent.replace('</head>', `${preloadLinks}\n${scriptTag}\n</head>`);
|
|
677
|
-
fs.writeFileSync(htmlPath, htmlContent);
|
|
678
|
-
log('Injected theme sync script and preload links into index.html', 'green');
|
|
679
|
-
return;
|
|
680
|
-
} else if (htmlContent.includes('<head>')) {
|
|
681
|
-
// If no closing </head>, try to add after <head>
|
|
682
|
-
htmlContent = htmlContent.replace('<head>', `<head>\n${preloadLinks}\n${scriptTag}`);
|
|
683
|
-
fs.writeFileSync(htmlPath, htmlContent);
|
|
684
|
-
log('Injected theme sync script and preload links into index.html', 'green');
|
|
685
|
-
return;
|
|
686
|
-
} else if (htmlContent.includes('</body>')) {
|
|
687
|
-
// Last resort: add before </body>
|
|
688
|
-
htmlContent = htmlContent.replace('</body>', `${preloadLinks}\n${scriptTag}\n</body>`);
|
|
689
|
-
fs.writeFileSync(htmlPath, htmlContent);
|
|
690
|
-
log('Injected theme sync script and preload links into index.html (before </body>)', 'yellow');
|
|
691
|
-
return;
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
log('⚠️ Could not find index.html. Add these to <head> manually:', 'yellow');
|
|
697
|
-
log(' <link rel="preload" href="/tokens/base.json" as="fetch" crossorigin>', 'blue');
|
|
698
|
-
log(' <link rel="preload" href="/tokens/palettes.json" as="fetch" crossorigin>', 'blue');
|
|
699
|
-
log(' <script>/* apply-theme-sync.js content */</script>', 'blue');
|
|
645
|
+
log('Skipping HTML injection; runtime theme applies dynamically.', 'yellow');
|
|
700
646
|
}
|
|
701
647
|
|
|
702
648
|
function checkMainFile() {
|