@12nil/theme-registry-package 0.1.0 → 0.1.1

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.cjs CHANGED
@@ -221,6 +221,16 @@ function tokenSetToCssVariables(tokens) {
221
221
  variables[`--theme-${category}-${keyName}`] = value;
222
222
  if (category === "colors") {
223
223
  variables[`--color-theme-${keyName}`] = value;
224
+ variables[`--theme-${keyName}`] = value;
225
+ }
226
+ if (category === "surface") {
227
+ variables[`--surface-${keyName}`] = value;
228
+ }
229
+ if (category === "text") {
230
+ variables[`--text-${keyName}`] = value;
231
+ }
232
+ if (category === "border") {
233
+ variables[`--border-${keyName}`] = value;
224
234
  }
225
235
  });
226
236
  };
@@ -233,6 +243,7 @@ function tokenSetToCssVariables(tokens) {
233
243
  Object.entries(tokens).forEach(([key, value]) => {
234
244
  if (key === "tertiary" && !value) return;
235
245
  variables[`--color-theme-${key}`] = value;
246
+ variables[`--theme-${key}`] = value;
236
247
  });
237
248
  return variables;
238
249
  }
@@ -767,7 +778,10 @@ var index_exports = {};
767
778
  __export(index_exports, {
768
779
  RUNTIME_THEME_REGISTRY_VERSION: () => RUNTIME_THEME_REGISTRY_VERSION,
769
780
  ThemeProvider: () => ThemeProvider,
781
+ ThemeSwitcher: () => ThemeSwitcher,
782
+ ThemeSwitcherStyled: () => ThemeSwitcherStyled,
770
783
  applyThemeToDocument: () => applyThemeToDocument,
784
+ createTailwindThemeColorMap: () => createTailwindThemeColorMap,
771
785
  default: () => themeRegistry,
772
786
  generateAllThemesCSS: () => generateAllThemesCSS,
773
787
  generateThemeCSS: () => generateThemeCSS,
@@ -798,6 +812,16 @@ function semanticTokensToCSSVariables(tokens) {
798
812
  variables[`--theme-${category}-${keyName}`] = value;
799
813
  if (category === "colors") {
800
814
  variables[`--color-theme-${keyName}`] = value;
815
+ variables[`--theme-${keyName}`] = value;
816
+ }
817
+ if (category === "surface") {
818
+ variables[`--surface-${keyName}`] = value;
819
+ }
820
+ if (category === "text") {
821
+ variables[`--text-${keyName}`] = value;
822
+ }
823
+ if (category === "border") {
824
+ variables[`--border-${keyName}`] = value;
801
825
  }
802
826
  });
803
827
  };
@@ -812,7 +836,9 @@ function legacyTokensToCSSVariables(tokens) {
812
836
  Object.entries(tokens).forEach(([key, value]) => {
813
837
  if (key === "tertiary" && !value) return;
814
838
  if (!value) return;
815
- variables[`--color-theme-${String(key)}`] = value;
839
+ const keyName = String(key);
840
+ variables[`--color-theme-${keyName}`] = value;
841
+ variables[`--theme-${keyName}`] = value;
816
842
  });
817
843
  return variables;
818
844
  }
@@ -853,15 +879,84 @@ function applyThemeToDocument(theme, mode) {
853
879
  document.documentElement.style.setProperty(name, value);
854
880
  });
855
881
  }
882
+ function createTailwindThemeColorMap() {
883
+ return {
884
+ primary: "var(--theme-primary)",
885
+ secondary: "var(--theme-secondary)",
886
+ background: "var(--theme-background)",
887
+ text: "var(--theme-text)",
888
+ accent: "var(--theme-accent)",
889
+ muted: "var(--theme-muted)",
890
+ error: "var(--theme-error)",
891
+ warning: "var(--theme-warning)",
892
+ success: "var(--theme-success)",
893
+ info: "var(--theme-info)",
894
+ "surface-page": "var(--surface-page)",
895
+ "surface-card": "var(--surface-card)",
896
+ "surface-sidebar": "var(--surface-sidebar)",
897
+ "surface-modal": "var(--surface-modal)",
898
+ "surface-popover": "var(--surface-popover)",
899
+ "text-primary": "var(--text-primary)",
900
+ "text-secondary": "var(--text-secondary)",
901
+ "text-tertiary": "var(--text-tertiary)",
902
+ "text-disabled": "var(--text-disabled)",
903
+ "border-default": "var(--border-default)",
904
+ "border-subtle": "var(--border-subtle)",
905
+ "border-strong": "var(--border-strong)"
906
+ };
907
+ }
856
908
 
857
909
  // theme-provider.tsx
858
910
  init_app_theme();
859
911
  var import_jsx_runtime = require("react/jsx-runtime");
912
+ var THEME_UTILITY_STYLE_ID = "runtime-theme-registry-utility-classes";
913
+ var THEME_UTILITY_CSS = `
914
+ .bg-primary{background-color:var(--theme-primary)!important}
915
+ .bg-secondary{background-color:var(--theme-secondary)!important}
916
+ .bg-background{background-color:var(--theme-background)!important}
917
+ .bg-accent{background-color:var(--theme-accent)!important}
918
+ .bg-muted{background-color:var(--theme-muted)!important}
919
+ .bg-error{background-color:var(--theme-error)!important}
920
+ .bg-warning{background-color:var(--theme-warning)!important}
921
+ .bg-success{background-color:var(--theme-success)!important}
922
+ .bg-info{background-color:var(--theme-info)!important}
923
+ .bg-surface-card{background-color:var(--surface-card)!important}
924
+ .bg-surface-page{background-color:var(--surface-page)!important}
925
+
926
+ .text-primary{color:var(--theme-primary)!important}
927
+ .text-secondary{color:var(--theme-secondary)!important}
928
+ .text-background{color:var(--theme-background)!important}
929
+ .text-text{color:var(--theme-text)!important}
930
+ .text-accent{color:var(--theme-accent)!important}
931
+ .text-muted{color:var(--theme-muted)!important}
932
+ .text-error{color:var(--theme-error)!important}
933
+ .text-warning{color:var(--theme-warning)!important}
934
+ .text-success{color:var(--theme-success)!important}
935
+ .text-info{color:var(--theme-info)!important}
936
+ .text-text-primary{color:var(--text-primary)!important}
937
+ .text-text-secondary{color:var(--text-secondary)!important}
938
+ .text-text-tertiary{color:var(--text-tertiary)!important}
939
+ .text-text-disabled{color:var(--text-disabled)!important}
940
+
941
+ .border-border-default{border-color:var(--border-default)!important}
942
+ .border-border-subtle{border-color:var(--border-subtle)!important}
943
+ .border-border-strong{border-color:var(--border-strong)!important}
944
+ `;
860
945
  var ThemeContext = (0, import_react.createContext)(void 0);
861
- function ThemeProvider({ children }) {
946
+ function ThemeProvider({ children, injectUtilityClasses = true }) {
862
947
  const [currentTheme, setCurrentTheme] = (0, import_react.useState)(null);
863
948
  const [currentMode, setCurrentMode] = (0, import_react.useState)(null);
864
949
  const [themes, setThemes] = (0, import_react.useState)([]);
950
+ (0, import_react.useEffect)(() => {
951
+ if (!injectUtilityClasses) return;
952
+ if (typeof document === "undefined") return;
953
+ const existing = document.getElementById(THEME_UTILITY_STYLE_ID);
954
+ if (existing) return;
955
+ const style = document.createElement("style");
956
+ style.id = THEME_UTILITY_STYLE_ID;
957
+ style.textContent = THEME_UTILITY_CSS;
958
+ document.head.appendChild(style);
959
+ }, [injectUtilityClasses]);
865
960
  (0, import_react.useEffect)(() => {
866
961
  const registeredThemes = themeRegistry.getAll();
867
962
  if (registeredThemes.length === 0) {
@@ -934,6 +1029,146 @@ function useTheme() {
934
1029
  return context;
935
1030
  }
936
1031
 
1032
+ // theme-switcher.tsx
1033
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1034
+ function getDefaultModeForTheme(themes, themeName, fallbackMode) {
1035
+ const nextTheme = themes.find((theme) => theme.name === themeName);
1036
+ if (!nextTheme) return null;
1037
+ if (fallbackMode && nextTheme.modes[fallbackMode]) {
1038
+ return fallbackMode;
1039
+ }
1040
+ const firstMode = Object.keys(nextTheme.modes)[0];
1041
+ return firstMode ?? null;
1042
+ }
1043
+ function ThemeSwitcher({
1044
+ className,
1045
+ themeLabel = "Theme",
1046
+ modeLabel = "Mode",
1047
+ showModeSelector = true,
1048
+ idPrefix = "runtime-theme-switcher",
1049
+ onThemeChanged,
1050
+ onModeChanged
1051
+ }) {
1052
+ const { themes, currentTheme, currentMode, availableModes, setTheme, setMode } = useTheme();
1053
+ if (themes.length === 0) {
1054
+ return null;
1055
+ }
1056
+ const handleThemeChange = (event) => {
1057
+ const nextThemeName = event.target.value;
1058
+ const nextMode = getDefaultModeForTheme(themes, nextThemeName, currentMode);
1059
+ if (!nextMode) return;
1060
+ setTheme(nextThemeName, nextMode);
1061
+ onThemeChanged?.(nextThemeName, nextMode);
1062
+ };
1063
+ const handleModeChange = (event) => {
1064
+ const nextMode = event.target.value;
1065
+ setMode(nextMode);
1066
+ onModeChanged?.(nextMode);
1067
+ };
1068
+ const themeSelectId = `${idPrefix}-theme`;
1069
+ const modeSelectId = `${idPrefix}-mode`;
1070
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className, children: [
1071
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: themeSelectId, children: themeLabel }),
1072
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("select", { id: themeSelectId, value: currentTheme ?? "", onChange: handleThemeChange, children: themes.map((theme) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: theme.name, children: theme.name }, theme.name)) }),
1073
+ showModeSelector ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1074
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: modeSelectId, children: modeLabel }),
1075
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("select", { id: modeSelectId, value: currentMode ?? "", onChange: handleModeChange, children: availableModes.map((mode) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: mode, children: mode }, mode)) })
1076
+ ] }) : null
1077
+ ] });
1078
+ }
1079
+ function ThemeSwitcherStyled({
1080
+ className,
1081
+ title = "Theme",
1082
+ subtitle = "Switch appearance and mode",
1083
+ themeLabel = "Theme",
1084
+ modeLabel = "Mode",
1085
+ showModeSelector = true,
1086
+ idPrefix = "runtime-theme-switcher-styled",
1087
+ onThemeChanged,
1088
+ onModeChanged
1089
+ }) {
1090
+ const { themes, currentTheme, currentMode, availableModes, setTheme, setMode } = useTheme();
1091
+ if (themes.length === 0) {
1092
+ return null;
1093
+ }
1094
+ const handleThemeChange = (event) => {
1095
+ const nextThemeName = event.target.value;
1096
+ const nextMode = getDefaultModeForTheme(themes, nextThemeName, currentMode);
1097
+ if (!nextMode) return;
1098
+ setTheme(nextThemeName, nextMode);
1099
+ onThemeChanged?.(nextThemeName, nextMode);
1100
+ };
1101
+ const handleModeClick = (mode) => {
1102
+ setMode(mode);
1103
+ onModeChanged?.(mode);
1104
+ };
1105
+ const themeSelectId = `${idPrefix}-theme`;
1106
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1107
+ "section",
1108
+ {
1109
+ className,
1110
+ style: {
1111
+ border: "1px solid var(--theme-border-subtle, #d1d5db)",
1112
+ borderRadius: 12,
1113
+ padding: 12,
1114
+ background: "var(--theme-surface-card, var(--color-theme-background, #ffffff))",
1115
+ color: "var(--theme-text-primary, var(--color-theme-text, #111827))",
1116
+ display: "grid",
1117
+ gap: 10,
1118
+ maxWidth: 360
1119
+ },
1120
+ children: [
1121
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { style: { display: "grid", gap: 2 }, children: [
1122
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: title }),
1123
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { style: { opacity: 0.8 }, children: subtitle })
1124
+ ] }),
1125
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "grid", gap: 6 }, children: [
1126
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: themeSelectId, children: themeLabel }),
1127
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1128
+ "select",
1129
+ {
1130
+ id: themeSelectId,
1131
+ value: currentTheme ?? "",
1132
+ onChange: handleThemeChange,
1133
+ style: {
1134
+ border: "1px solid var(--theme-border-default, #9ca3af)",
1135
+ borderRadius: 8,
1136
+ padding: "8px 10px",
1137
+ background: "var(--theme-surface-page, var(--color-theme-background, #ffffff))",
1138
+ color: "inherit"
1139
+ },
1140
+ children: themes.map((theme) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: theme.name, children: theme.name }, theme.name))
1141
+ }
1142
+ )
1143
+ ] }),
1144
+ showModeSelector ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "grid", gap: 6 }, children: [
1145
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: modeLabel }),
1146
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: availableModes.map((mode) => {
1147
+ const isActive = mode === currentMode;
1148
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1149
+ "button",
1150
+ {
1151
+ type: "button",
1152
+ onClick: () => handleModeClick(mode),
1153
+ style: {
1154
+ border: "1px solid var(--theme-border-default, #9ca3af)",
1155
+ borderRadius: 999,
1156
+ padding: "6px 12px",
1157
+ cursor: "pointer",
1158
+ background: isActive ? "var(--theme-colors-primary, var(--color-theme-primary, #2563eb))" : "var(--theme-surface-page, var(--color-theme-background, #ffffff))",
1159
+ color: isActive ? "var(--theme-surface-page, var(--color-theme-background, #ffffff))" : "inherit"
1160
+ },
1161
+ children: mode
1162
+ },
1163
+ mode
1164
+ );
1165
+ }) })
1166
+ ] }) : null
1167
+ ]
1168
+ }
1169
+ );
1170
+ }
1171
+
937
1172
  // index.ts
938
1173
  init_theme_registry();
939
1174
  init_theme_registry();
@@ -941,7 +1176,10 @@ init_theme_registry();
941
1176
  0 && (module.exports = {
942
1177
  RUNTIME_THEME_REGISTRY_VERSION,
943
1178
  ThemeProvider,
1179
+ ThemeSwitcher,
1180
+ ThemeSwitcherStyled,
944
1181
  applyThemeToDocument,
1182
+ createTailwindThemeColorMap,
945
1183
  generateAllThemesCSS,
946
1184
  generateThemeCSS,
947
1185
  isThemeTokensV2,