@wrksz/themes 0.1.0 → 0.2.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/README.md CHANGED
@@ -1 +1,193 @@
1
- # themes
1
+ <img src=".github/images/banner.png" alt="@wrksz/themes" width="100%" />
2
+
3
+ # @wrksz/themes
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@wrksz/themes)](https://www.npmjs.com/package/@wrksz/themes)
6
+
7
+ Modern theme management for Next.js 16+ and React 19+. Drop-in replacement for `next-themes` - fixes every known bug and adds missing features.
8
+
9
+ ```bash
10
+ bun add @wrksz/themes
11
+ # or
12
+ npm install @wrksz/themes
13
+ ```
14
+
15
+ ## Setup
16
+
17
+ Add the provider to your root layout. Add `suppressHydrationWarning` to `<html>` to prevent hydration warnings caused by the inline theme script running before React hydrates.
18
+
19
+ ```tsx
20
+ // app/layout.tsx
21
+ import { ThemeProvider } from "@wrksz/themes";
22
+
23
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
24
+ return (
25
+ <html lang="en" suppressHydrationWarning>
26
+ <body>
27
+ <ThemeProvider>{children}</ThemeProvider>
28
+ </body>
29
+ </html>
30
+ );
31
+ }
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ```tsx
37
+ "use client";
38
+
39
+ import { useTheme } from "@wrksz/themes";
40
+
41
+ export function ThemeToggle() {
42
+ const { theme, setTheme } = useTheme();
43
+
44
+ return (
45
+ <button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
46
+ Toggle theme
47
+ </button>
48
+ );
49
+ }
50
+ ```
51
+
52
+ ## API
53
+
54
+ ### `ThemeProvider`
55
+
56
+ | Prop | Type | Default | Description |
57
+ |------|------|---------|-------------|
58
+ | `themes` | `string[]` | `["light", "dark"]` | Available themes |
59
+ | `defaultTheme` | `string` | `"system"` | Theme used when no preference is stored |
60
+ | `forcedTheme` | `string` | - | Force a specific theme, ignoring user preference |
61
+ | `enableSystem` | `boolean` | `true` | Detect system preference via `prefers-color-scheme` |
62
+ | `enableColorScheme` | `boolean` | `true` | Set native `color-scheme` CSS property |
63
+ | `attribute` | `string \| string[]` | `"class"` | HTML attribute(s) to set on target element (`"class"`, `"data-theme"`, etc.) |
64
+ | `value` | `Record<string, string>` | - | Map theme names to attribute values |
65
+ | `target` | `string` | `"html"` | Element to apply theme to (`"html"`, `"body"`, or a CSS selector) |
66
+ | `storageKey` | `string` | `"theme"` | Key used for storage |
67
+ | `storage` | `"localStorage" \| "sessionStorage" \| "none"` | `"localStorage"` | Where to persist the theme |
68
+ | `disableTransitionOnChange` | `boolean` | `false` | Disable CSS transitions when switching themes |
69
+ | `themeColor` | `string \| Record<string, string>` | - | Update `<meta name="theme-color">` on theme change |
70
+ | `nonce` | `string` | - | CSP nonce for the inline script |
71
+ | `onThemeChange` | `(theme: string) => void` | - | Called whenever the resolved theme changes |
72
+
73
+ ### `useTheme`
74
+
75
+ ```tsx
76
+ const {
77
+ theme, // Current theme - may be "system"
78
+ resolvedTheme, // Actual theme - never "system"
79
+ systemTheme, // System preference: "light" | "dark" | undefined
80
+ forcedTheme, // Forced theme if set
81
+ themes, // Available themes
82
+ setTheme, // Set theme
83
+ } = useTheme();
84
+ ```
85
+
86
+ Supports generics for full type safety:
87
+
88
+ ```tsx
89
+ type AppTheme = "light" | "dark" | "high-contrast";
90
+
91
+ const { theme, setTheme } = useTheme<AppTheme>();
92
+ // theme: AppTheme | "system" | undefined
93
+ // setTheme: (theme: AppTheme | "system") => void
94
+ ```
95
+
96
+ ## Examples
97
+
98
+ ### Custom themes with Tailwind
99
+
100
+ ```tsx
101
+ <ThemeProvider
102
+ themes={["light", "dark", "high-contrast"]}
103
+ attribute="class"
104
+ >
105
+ {children}
106
+ </ThemeProvider>
107
+ ```
108
+
109
+ ### Data attribute instead of class
110
+
111
+ ```tsx
112
+ <ThemeProvider attribute="data-theme">
113
+ {children}
114
+ </ThemeProvider>
115
+ ```
116
+
117
+ ```css
118
+ [data-theme="dark"] { --bg: #000; }
119
+ [data-theme="light"] { --bg: #fff; }
120
+ ```
121
+
122
+ ### Custom attribute values
123
+
124
+ ```tsx
125
+ <ThemeProvider
126
+ themes={["light", "dark"]}
127
+ attribute="data-mode"
128
+ value={{ light: "light-mode", dark: "dark-mode" }}
129
+ >
130
+ {children}
131
+ </ThemeProvider>
132
+ ```
133
+
134
+ ### Meta theme-color (Safari / PWA)
135
+
136
+ ```tsx
137
+ <ThemeProvider
138
+ themeColor={{ light: "#ffffff", dark: "#0a0a0a" }}
139
+ >
140
+ {children}
141
+ </ThemeProvider>
142
+ ```
143
+
144
+ Works with CSS variables too:
145
+
146
+ ```tsx
147
+ <ThemeProvider themeColor="var(--color-background)">
148
+ {children}
149
+ </ThemeProvider>
150
+ ```
151
+
152
+ ### Disable storage
153
+
154
+ ```tsx
155
+ // No persistence - always uses defaultTheme or system preference
156
+ <ThemeProvider storage="none" defaultTheme="dark">
157
+ {children}
158
+ </ThemeProvider>
159
+ ```
160
+
161
+ ### Forced theme per page
162
+
163
+ ```tsx
164
+ // app/dashboard/layout.tsx
165
+ <ThemeProvider forcedTheme="dark">
166
+ {children}
167
+ </ThemeProvider>
168
+ ```
169
+
170
+ ### Class on body instead of html
171
+
172
+ ```tsx
173
+ <ThemeProvider target="body">
174
+ {children}
175
+ </ThemeProvider>
176
+ ```
177
+
178
+ ## Why not `next-themes`?
179
+
180
+ | Issue | next-themes | @wrksz/themes |
181
+ |-------|-------------|---------------|
182
+ | React 19 script warning | Yes | Fixed (RSC split) |
183
+ | `__name` minification bug | Yes | Fixed |
184
+ | React 19 Activity/cacheComponents stale theme | Yes | Fixed (`useSyncExternalStore`) |
185
+ | `sessionStorage` support | No | Yes |
186
+ | Disable storage | No | Yes (`storage: "none"`) |
187
+ | `meta theme-color` support | No | Yes (`themeColor` prop) |
188
+ | Generic types | No | Yes (`useTheme<AppTheme>()`) |
189
+ | Zero runtime dependencies | Yes | Yes |
190
+
191
+ ## License
192
+
193
+ MIT
package/dist/index.d.ts CHANGED
@@ -1,2 +1,56 @@
1
- declare function Button(props: React.ComponentProps<"button">): React.ReactNode;
2
- export { Button };
1
+ import { ReactNode } from "react";
2
+ type DefaultTheme = "light" | "dark" | "system";
3
+ type Attribute = "class" | `data-${string}`;
4
+ type ValueObject = Record<string, string>;
5
+ type StorageType = "localStorage" | "sessionStorage" | "none";
6
+ /** Per-theme colors for meta theme-color, or a single string for all themes */
7
+ type ThemeColor = string | Partial<Record<string, string>>;
8
+ type ThemeProviderProps<Themes extends string = DefaultTheme> = {
9
+ children: ReactNode;
10
+ /** All available themes */
11
+ themes?: Themes[];
12
+ /** Forced theme, overrides everything */
13
+ forcedTheme?: Themes;
14
+ /** Enable system preference via prefers-color-scheme */
15
+ enableSystem?: boolean;
16
+ /** Default theme when no preference stored */
17
+ defaultTheme?: Themes | "system";
18
+ /** HTML attribute(s) to set on target element */
19
+ attribute?: Attribute | Attribute[];
20
+ /** Map theme name to attribute value */
21
+ value?: ValueObject;
22
+ /** Target element to apply theme to, defaults to <html> */
23
+ target?: "html" | "body" | string;
24
+ /** Disable CSS transitions on theme change */
25
+ disableTransitionOnChange?: boolean;
26
+ /** Where to persist theme */
27
+ storage?: StorageType;
28
+ /** Storage key */
29
+ storageKey?: string;
30
+ /** Set native color-scheme CSS property */
31
+ enableColorScheme?: boolean;
32
+ /** Nonce for CSP */
33
+ nonce?: string;
34
+ /** Called when theme changes */
35
+ onThemeChange?: (theme: Themes) => void;
36
+ /** Colors for meta theme-color tag, per theme or a single value */
37
+ themeColor?: ThemeColor;
38
+ };
39
+ type ThemeContextValue<Themes extends string = DefaultTheme> = {
40
+ /** Current theme (may be "system") */
41
+ theme: Themes | "system" | undefined;
42
+ /** Resolved theme - never "system" */
43
+ resolvedTheme: Themes | undefined;
44
+ /** System preference */
45
+ systemTheme: "light" | "dark" | undefined;
46
+ /** Forced theme if set */
47
+ forcedTheme: Themes | undefined;
48
+ /** All available themes */
49
+ themes: Themes[];
50
+ /** Set theme */
51
+ setTheme: (theme: Themes | "system" | ((current: Themes | "system" | undefined) => Themes | "system")) => void;
52
+ };
53
+ declare function useTheme<Themes extends string = DefaultTheme>(): ThemeContextValue<Themes>;
54
+ import { ReactElement } from "react";
55
+ declare function ThemeProvider<Themes extends string = DefaultTheme>({ children, themes, forcedTheme, enableSystem, defaultTheme, attribute, value: valueMap, target, disableTransitionOnChange, storage, storageKey, enableColorScheme, nonce, onThemeChange, themeColor }: ThemeProviderProps<Themes>): ReactElement;
56
+ export { useTheme, ValueObject, ThemeProviderProps, ThemeProvider, ThemeContextValue, ThemeColor, StorageType, DefaultTheme, Attribute };
package/dist/index.js CHANGED
@@ -1,12 +1,343 @@
1
- // src/components/button.tsx
1
+ "use client";
2
+ // src/context.ts
3
+ import { createContext, useContext } from "react";
4
+ var ThemeContext = createContext(undefined);
5
+ function useTheme() {
6
+ const ctx = useContext(ThemeContext);
7
+ if (!ctx) {
8
+ throw new Error("useTheme must be used within a ThemeProvider");
9
+ }
10
+ return ctx;
11
+ }
12
+ // src/client-provider.tsx
13
+ import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
14
+
15
+ // src/store.ts
16
+ var state = { theme: undefined, systemTheme: undefined };
17
+ var listeners = new Set;
18
+ function emit() {
19
+ for (const listener of listeners)
20
+ listener();
21
+ }
22
+ var SERVER_SNAPSHOT = { theme: undefined, systemTheme: undefined };
23
+ var themeStore = {
24
+ subscribe(listener) {
25
+ listeners.add(listener);
26
+ return () => {
27
+ listeners.delete(listener);
28
+ };
29
+ },
30
+ getSnapshot() {
31
+ return state;
32
+ },
33
+ getServerSnapshot() {
34
+ return SERVER_SNAPSHOT;
35
+ },
36
+ setTheme(theme) {
37
+ if (state.theme === theme)
38
+ return;
39
+ state = { ...state, theme };
40
+ emit();
41
+ },
42
+ setSystemTheme(systemTheme) {
43
+ if (state.systemTheme === systemTheme)
44
+ return;
45
+ state = { ...state, systemTheme };
46
+ emit();
47
+ }
48
+ };
49
+
50
+ // src/client-provider.tsx
2
51
  import { jsxDEV } from "react/jsx-dev-runtime";
3
- function Button(props) {
4
- return /* @__PURE__ */ jsxDEV("button", {
5
- type: "button",
6
- "data-slot": "button",
7
- ...props
52
+
53
+ var DEFAULT_THEMES = ["light", "dark"];
54
+ function resolveThemeColor(themeColor, resolved) {
55
+ if (typeof themeColor === "string")
56
+ return themeColor;
57
+ return themeColor[resolved];
58
+ }
59
+ function updateMetaThemeColor(color) {
60
+ if (!color)
61
+ return;
62
+ let meta = document.querySelector('meta[name="theme-color"]');
63
+ if (!meta) {
64
+ meta = document.createElement("meta");
65
+ meta.name = "theme-color";
66
+ document.head.appendChild(meta);
67
+ }
68
+ meta.content = color;
69
+ }
70
+ function ClientThemeProvider({
71
+ children,
72
+ themes = DEFAULT_THEMES,
73
+ forcedTheme,
74
+ enableSystem = true,
75
+ defaultTheme,
76
+ attribute = "class",
77
+ value: valueMap,
78
+ target = "html",
79
+ disableTransitionOnChange = false,
80
+ storage = "localStorage",
81
+ storageKey = "theme",
82
+ enableColorScheme = true,
83
+ themeColor,
84
+ onThemeChange
85
+ }) {
86
+ const resolvedDefault = defaultTheme ?? (enableSystem ? "system" : "light");
87
+ const { theme, systemTheme } = useSyncExternalStore(themeStore.subscribe, themeStore.getSnapshot, themeStore.getServerSnapshot);
88
+ const resolvedTheme = forcedTheme ?? (theme === "system" || theme === undefined ? systemTheme : theme);
89
+ const onThemeChangeRef = useRef(onThemeChange);
90
+ useEffect(() => {
91
+ onThemeChangeRef.current = onThemeChange;
92
+ });
93
+ const getTargetEl = useCallback(() => {
94
+ if (target === "html")
95
+ return document.documentElement;
96
+ if (target === "body")
97
+ return document.body;
98
+ return document.querySelector(target);
99
+ }, [target]);
100
+ const applyToDom = useCallback((resolved) => {
101
+ const el = getTargetEl();
102
+ if (!el)
103
+ return;
104
+ const attrValue = valueMap?.[resolved] ?? resolved;
105
+ const attrs = Array.isArray(attribute) ? attribute : [attribute];
106
+ if (disableTransitionOnChange) {
107
+ const style = document.createElement("style");
108
+ style.textContent = "*,*::before,*::after{transition:none!important}";
109
+ document.head.appendChild(style);
110
+ requestAnimationFrame(() => requestAnimationFrame(() => document.head.removeChild(style)));
111
+ }
112
+ for (const attr of attrs) {
113
+ if (attr === "class") {
114
+ const toRemove = themes.map((t) => valueMap?.[t] ?? t);
115
+ el.classList.remove(...toRemove);
116
+ el.classList.add(attrValue);
117
+ } else {
118
+ el.setAttribute(attr, attrValue);
119
+ }
120
+ }
121
+ if (enableColorScheme && (resolved === "light" || resolved === "dark")) {
122
+ el.style.colorScheme = resolved;
123
+ }
124
+ if (themeColor) {
125
+ updateMetaThemeColor(resolveThemeColor(themeColor, resolved));
126
+ }
127
+ }, [
128
+ attribute,
129
+ disableTransitionOnChange,
130
+ enableColorScheme,
131
+ getTargetEl,
132
+ themes,
133
+ valueMap,
134
+ themeColor
135
+ ]);
136
+ useEffect(() => {
137
+ if (forcedTheme) {
138
+ themeStore.setTheme(forcedTheme);
139
+ return;
140
+ }
141
+ let stored = null;
142
+ try {
143
+ if (storage !== "none") {
144
+ const store = storage === "localStorage" ? localStorage : sessionStorage;
145
+ stored = store.getItem(storageKey);
146
+ }
147
+ } catch {}
148
+ const initial = stored && themes.includes(stored) ? stored : resolvedDefault;
149
+ themeStore.setTheme(initial);
150
+ }, [forcedTheme, resolvedDefault, storage, storageKey, themes]);
151
+ useEffect(() => {
152
+ if (!enableSystem)
153
+ return;
154
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
155
+ themeStore.setSystemTheme(mq.matches ? "dark" : "light");
156
+ const handler = (e) => {
157
+ const next = e.matches ? "dark" : "light";
158
+ themeStore.setSystemTheme(next);
159
+ const current = themeStore.getSnapshot().theme;
160
+ if (current === "system" || current === undefined) {
161
+ applyToDom(next);
162
+ onThemeChangeRef.current?.(next);
163
+ }
164
+ };
165
+ mq.addEventListener("change", handler);
166
+ return () => mq.removeEventListener("change", handler);
167
+ }, [enableSystem, applyToDom]);
168
+ useEffect(() => {
169
+ if (storage === "none")
170
+ return;
171
+ const handler = (e) => {
172
+ if (e.key !== storageKey || !e.newValue)
173
+ return;
174
+ if (themes.includes(e.newValue)) {
175
+ const newTheme = e.newValue;
176
+ const resolved = newTheme === "system" ? themeStore.getSnapshot().systemTheme ?? "light" : newTheme;
177
+ themeStore.setTheme(newTheme);
178
+ applyToDom(resolved);
179
+ }
180
+ };
181
+ window.addEventListener("storage", handler);
182
+ return () => window.removeEventListener("storage", handler);
183
+ }, [storage, storageKey, themes, applyToDom]);
184
+ const setTheme = useCallback((next) => {
185
+ if (forcedTheme)
186
+ return;
187
+ const current = themeStore.getSnapshot().theme;
188
+ const newTheme = typeof next === "function" ? next(current) : next;
189
+ const resolved = newTheme === "system" ? themeStore.getSnapshot().systemTheme ?? "light" : newTheme;
190
+ themeStore.setTheme(newTheme);
191
+ applyToDom(resolved);
192
+ onThemeChangeRef.current?.(resolved);
193
+ try {
194
+ if (storage !== "none") {
195
+ const store = storage === "localStorage" ? localStorage : sessionStorage;
196
+ store.setItem(storageKey, newTheme);
197
+ }
198
+ } catch {}
199
+ }, [applyToDom, forcedTheme, storage, storageKey]);
200
+ const contextValue = {
201
+ theme: forcedTheme ?? theme,
202
+ resolvedTheme,
203
+ systemTheme,
204
+ forcedTheme,
205
+ themes,
206
+ setTheme
207
+ };
208
+ return /* @__PURE__ */ jsxDEV(ThemeContext.Provider, {
209
+ value: contextValue,
210
+ children
8
211
  }, undefined, false, undefined, this);
9
212
  }
213
+
214
+ // src/script.ts
215
+ function themeScript(storageKey, attribute, defaultTheme, enableSystem, enableColorScheme, forcedTheme, themes, value, target, storage, themeColors) {
216
+ let theme;
217
+ if (forcedTheme) {
218
+ theme = forcedTheme;
219
+ } else {
220
+ let stored = null;
221
+ try {
222
+ if (storage !== "none") {
223
+ const store = storage === "localStorage" ? localStorage : sessionStorage;
224
+ stored = store.getItem(storageKey);
225
+ }
226
+ } catch {}
227
+ theme = stored && themes.includes(stored) ? stored : defaultTheme;
228
+ }
229
+ if (theme === "system") {
230
+ theme = enableSystem ? matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : defaultTheme;
231
+ }
232
+ const attrValue = value?.[theme] || theme;
233
+ const el = target === "html" ? document.documentElement : target === "body" ? document.body : document.querySelector(target);
234
+ if (!el)
235
+ return;
236
+ const attrs = Array.isArray(attribute) ? attribute : [attribute];
237
+ for (const attr of attrs) {
238
+ if (attr === "class") {
239
+ const toRemove = themes.map((t) => value?.[t] || t);
240
+ el.classList.remove(...toRemove);
241
+ el.classList.add(attrValue);
242
+ } else {
243
+ el.setAttribute(attr, attrValue);
244
+ }
245
+ }
246
+ if (enableColorScheme && (theme === "light" || theme === "dark")) {
247
+ el.style.colorScheme = theme;
248
+ }
249
+ if (themeColors) {
250
+ const color = typeof themeColors === "string" ? themeColors : themeColors[theme];
251
+ if (color) {
252
+ let meta = document.querySelector('meta[name="theme-color"]');
253
+ if (!meta) {
254
+ meta = document.createElement("meta");
255
+ meta.setAttribute("name", "theme-color");
256
+ document.head.appendChild(meta);
257
+ }
258
+ meta.setAttribute("content", color);
259
+ }
260
+ }
261
+ }
262
+ function getScript(config) {
263
+ const fn = themeScript.toString().replace(/\s*__name\s*\([^)]*\)\s*;?\s*/g, "");
264
+ const args = [
265
+ JSON.stringify(config.storageKey),
266
+ JSON.stringify(config.attribute),
267
+ JSON.stringify(config.defaultTheme),
268
+ String(config.enableSystem),
269
+ String(config.enableColorScheme),
270
+ JSON.stringify(config.forcedTheme ?? null),
271
+ JSON.stringify(config.themes),
272
+ JSON.stringify(config.value ?? null),
273
+ JSON.stringify(config.target),
274
+ JSON.stringify(config.storage),
275
+ JSON.stringify(config.themeColors ?? null)
276
+ ].join(",");
277
+ return `(${fn})(${args})`;
278
+ }
279
+
280
+ // src/provider.tsx
281
+ import { jsxDEV as jsxDEV2, Fragment } from "react/jsx-dev-runtime";
282
+ var DEFAULT_THEMES2 = ["light", "dark"];
283
+ function ThemeProvider({
284
+ children,
285
+ themes = DEFAULT_THEMES2,
286
+ forcedTheme,
287
+ enableSystem = true,
288
+ defaultTheme,
289
+ attribute = "class",
290
+ value: valueMap,
291
+ target = "html",
292
+ disableTransitionOnChange = false,
293
+ storage = "localStorage",
294
+ storageKey = "theme",
295
+ enableColorScheme = true,
296
+ nonce,
297
+ onThemeChange,
298
+ themeColor
299
+ }) {
300
+ const resolvedDefault = defaultTheme ?? (enableSystem ? "system" : "light");
301
+ return /* @__PURE__ */ jsxDEV2(Fragment, {
302
+ children: [
303
+ /* @__PURE__ */ jsxDEV2("script", {
304
+ dangerouslySetInnerHTML: {
305
+ __html: getScript({
306
+ storageKey,
307
+ attribute,
308
+ defaultTheme: resolvedDefault,
309
+ enableSystem,
310
+ enableColorScheme,
311
+ forcedTheme,
312
+ themes,
313
+ value: valueMap,
314
+ target,
315
+ storage,
316
+ themeColors: themeColor
317
+ })
318
+ },
319
+ nonce
320
+ }, undefined, false, undefined, this),
321
+ /* @__PURE__ */ jsxDEV2(ClientThemeProvider, {
322
+ themes,
323
+ forcedTheme,
324
+ enableSystem,
325
+ defaultTheme,
326
+ attribute,
327
+ value: valueMap,
328
+ target,
329
+ disableTransitionOnChange,
330
+ storage,
331
+ storageKey,
332
+ enableColorScheme,
333
+ themeColor,
334
+ onThemeChange,
335
+ children
336
+ }, undefined, false, undefined, this)
337
+ ]
338
+ }, undefined, true, undefined, this);
339
+ }
10
340
  export {
11
- Button
341
+ useTheme,
342
+ ThemeProvider
12
343
  };
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@wrksz/themes",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "A modern, fully-featured theme management library for Next.js",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/jakubwarkusz/themes"
8
+ },
5
9
  "license": "MIT",
6
10
  "files": [
7
11
  "dist"
@@ -13,7 +17,7 @@
13
17
  "lint": "biome check src",
14
18
  "lint:fix": "biome check --write src",
15
19
  "format": "biome format --write src",
16
- "prepare": ""
20
+ "prepare": "[ \"$CI\" = \"true\" ] || lefthook install"
17
21
  },
18
22
  "devDependencies": {
19
23
  "@biomejs/biome": "^2.4.8",
package/dist/index.css DELETED
@@ -1,29 +0,0 @@
1
- /* src/styles.css */
2
- :root {
3
- --button-bg: #007bff;
4
- --button-bg-hover: #006fe6;
5
- --button-text: #fff;
6
- }
7
-
8
- @media (prefers-color-scheme: dark) {
9
- :root {
10
- --button-bg: #3089e8;
11
- --button-bg-hover: #4796eb;
12
- --button-text: #fff;
13
- }
14
- }
15
-
16
- [data-slot="button"] {
17
- background: var(--button-bg);
18
- color: var(--button-text);
19
- cursor: pointer;
20
- border: none;
21
- border-radius: .5rem;
22
- padding: .6rem 1.2rem;
23
- font-size: .875rem;
24
- font-weight: 500;
25
- }
26
-
27
- [data-slot="button"]:hover {
28
- background: var(--button-bg-hover);
29
- }