@usetheo/ui 0.6.1-next.0 → 0.6.3-next.0

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.js CHANGED
@@ -188,35 +188,25 @@ function ThemeProvider({
188
188
  useEffect(() => {
189
189
  setThemes(mergedThemes);
190
190
  }, [mergedThemes]);
191
- const [themeName, setThemeName] = useState(() => {
192
- if (typeof window === "undefined" || !storageKey) return defaultTheme;
193
- try {
194
- return window.localStorage.getItem(`${storageKey}:name`) ?? defaultTheme;
195
- } catch (err) {
196
- warnStorageFailure("read theme name", err);
197
- return defaultTheme;
198
- }
199
- });
200
- const [mode, setModeState] = useState(() => {
201
- if (typeof window === "undefined" || !storageKey) return defaultMode;
202
- try {
203
- const stored = window.localStorage.getItem(`${storageKey}:mode`);
204
- return stored === "dark" || stored === "light" ? stored : defaultMode;
205
- } catch (err) {
206
- warnStorageFailure("read theme mode", err);
207
- return defaultMode;
208
- }
209
- });
210
- const [density, setDensityState] = useState(() => {
211
- if (typeof window === "undefined" || !storageKey) return defaultDensity;
191
+ const [themeName, setThemeName] = useState(defaultTheme);
192
+ const [mode, setModeState] = useState(defaultMode);
193
+ const [density, setDensityState] = useState(defaultDensity);
194
+ const skipFirstPersistRef = useRef(true);
195
+ useEffect(() => {
196
+ if (typeof window === "undefined" || !storageKey) return;
212
197
  try {
213
- const stored = window.localStorage.getItem(`${storageKey}:density`);
214
- return stored === "compact" || stored === "comfortable" || stored === "spacious" ? stored : defaultDensity;
198
+ const storedName = window.localStorage.getItem(`${storageKey}:name`);
199
+ const storedMode = window.localStorage.getItem(`${storageKey}:mode`);
200
+ const storedDensity = window.localStorage.getItem(`${storageKey}:density`);
201
+ if (storedName) setThemeName(storedName);
202
+ if (storedMode === "dark" || storedMode === "light") setModeState(storedMode);
203
+ if (storedDensity === "compact" || storedDensity === "comfortable" || storedDensity === "spacious") {
204
+ setDensityState(storedDensity);
205
+ }
215
206
  } catch (err) {
216
- warnStorageFailure("read density", err);
217
- return defaultDensity;
207
+ warnStorageFailure("read theme + mode + density", err);
218
208
  }
219
- });
209
+ }, [storageKey]);
220
210
  useEffect(() => {
221
211
  injectThemeCss(themes);
222
212
  }, [themes]);
@@ -232,6 +222,10 @@ function ThemeProvider({
232
222
  injectDensityCss();
233
223
  }, [themeName, mode, density, themes]);
234
224
  useEffect(() => {
225
+ if (skipFirstPersistRef.current) {
226
+ skipFirstPersistRef.current = false;
227
+ return;
228
+ }
235
229
  if (typeof window === "undefined" || !storageKey) return;
236
230
  try {
237
231
  window.localStorage.setItem(`${storageKey}:name`, themeName);
@@ -285,18 +279,20 @@ function useTheme() {
285
279
  function safe(value) {
286
280
  return JSON.stringify(value).replace(/</g, "\\u003c");
287
281
  }
288
- function buildScript(defaultTheme, defaultMode, storageKey) {
282
+ function buildScript(defaultTheme, defaultMode, defaultDensity, storageKey) {
289
283
  const k = safe(storageKey);
290
284
  const t = safe(defaultTheme);
291
285
  const m = safe(defaultMode);
292
- return `(function(){try{var k=${k};var d=document.documentElement;var t=null;var m=null;if(k){t=localStorage.getItem(k+":name");m=localStorage.getItem(k+":mode");}d.setAttribute("data-theme",t||${t});d.setAttribute("data-mode",m||${m});if((m||${m})==="dark"){d.classList.add("dark");}}catch(e){}})();`;
286
+ const dn = safe(defaultDensity);
287
+ return `(function(){try{var k=${k};var d=document.documentElement;var t=null;var m=null;var dn=null;if(k){t=localStorage.getItem(k+":name");m=localStorage.getItem(k+":mode");dn=localStorage.getItem(k+":density");}d.setAttribute("data-theme",t||${t});d.setAttribute("data-mode",m||${m});d.setAttribute("data-density",dn||${dn});if((m||${m})==="dark"){d.classList.add("dark");}}catch(e){}})();`;
293
288
  }
294
289
  function ThemeScript({
295
290
  defaultTheme = "violet-forge",
296
291
  defaultMode = "dark",
292
+ defaultDensity = "comfortable",
297
293
  storageKey = "theo-ui:theme"
298
294
  }) {
299
- const code = buildScript(defaultTheme, defaultMode, storageKey);
295
+ const code = buildScript(defaultTheme, defaultMode, defaultDensity, storageKey);
300
296
  return /* @__PURE__ */ jsx("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: code } });
301
297
  }
302
298
  function ThemeSwitcher({ className, showModeToggle = true }) {
@@ -1452,6 +1448,15 @@ var buttonVariants = cva(
1452
1448
  "font-medium font-sans tracking-tight",
1453
1449
  "transition-[box-shadow,background-color,color,transform] duration-base ease-out-soft",
1454
1450
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
1451
+ // Tailwind v4 dropped the `button { cursor: pointer }` preflight rule
1452
+ // (https://tailwindcss.com/docs/upgrade-guide#default-button-cursor) so
1453
+ // every <button> now shows the default arrow cursor. Restore the
1454
+ // "clickable hand" explicitly for the Button primitive; the
1455
+ // `disabled:pointer-events-none` rule below short-circuits cursor
1456
+ // application for disabled state (no events → cursor is moot).
1457
+ // `aria-disabled:cursor-default` is a belt-and-suspenders override
1458
+ // for paths where pointer-events still flow.
1459
+ "cursor-pointer disabled:cursor-default aria-disabled:cursor-default",
1455
1460
  "disabled:pointer-events-none disabled:opacity-50",
1456
1461
  "[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0"
1457
1462
  ],