Skip to content

Commit a175fef

Browse files
committed
feat: add multi class themes
1 parent 350f653 commit a175fef

File tree

8 files changed

+104
-33
lines changed

8 files changed

+104
-33
lines changed

CHANGELOG.md

Whitespace-only changes.

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,33 @@ If your Qwik app uses a class to style the page based on the theme, change the a
159159

160160
Now, setting the theme to "dark" will set `class="dark"` on the `html` element.
161161

162+
### Multi Class Themes
163+
164+
You can also use multi Class Themes like `[dark, skeumorphic]`
165+
166+
167+
```tsx
168+
<ThemeProvider
169+
themes={[
170+
["simple", "light-yellow"],
171+
["simple", "dark-yellow"],
172+
["brutalist", "light-yellow"],
173+
["brutalist", "dark-yellow"],
174+
["hand-drawn", "light"],
175+
["hand-drawn", "dark"],
176+
]
177+
}>
178+
```
179+
180+
and then you can simply change the theme as before, just with your arrays instead!
181+
182+
```tsx
183+
const { setTheme } = useTheme()
184+
185+
setTheme(["simple", "light-yellow"])
186+
```
187+
188+
162189
### Force page to a theme
163190
TODO
164191

bun.lockb

24.7 KB
Binary file not shown.

package.json

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
"types": "./lib-types/index.d.ts"
1313
}
1414
},
15-
"files": ["lib", "lib-types"],
15+
"files": [
16+
"lib",
17+
"lib-types"
18+
],
1619
"engines": {
1720
"node": ">=15.0.0"
1821
},
@@ -33,20 +36,20 @@
3336
"qwik": "qwik"
3437
},
3538
"devDependencies": {
36-
"@builder.io/qwik": "1.2.10",
37-
"@types/eslint": "8.44.1",
38-
"@types/node": "^20.4.5",
39-
"@typescript-eslint/eslint-plugin": "6.2.0",
40-
"@typescript-eslint/parser": "6.2.0",
41-
"eslint": "8.45.0",
39+
"@builder.io/qwik": "1.4.0",
40+
"@types/eslint": "8.56.2",
41+
"@types/node": "^20.11.5",
42+
"@typescript-eslint/eslint-plugin": "6.19.1",
43+
"@typescript-eslint/parser": "6.19.1",
44+
"eslint": "8.56.0",
4245
"eslint-plugin-qwik": "latest",
43-
"np": "7.6.1",
44-
"prettier": "3.0.0",
45-
"typescript": "5.1.6",
46-
"undici": "5.22.1",
47-
"vite": "4.4.7",
48-
"vite-tsconfig-paths": "4.2.0",
49-
"@biomejs/biome": "^1.1.2"
46+
"np": "9.2.0",
47+
"prettier": "3.2.4",
48+
"typescript": "5.3.3",
49+
"undici": "6.4.0",
50+
"vite": "5.0.12",
51+
"vite-tsconfig-paths": "4.3.1",
52+
"@biomejs/biome": "^1.5.3"
5053
},
5154
"dependencies": {}
52-
}
55+
}

src/lib/provider.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const ThemeProvider = component$<ThemeProviderProps>(
3333
value,
3434
nonce,
3535
}) => {
36-
const attrs = !value ? themes : Object.values(value)
36+
const attrs = !value ? themes.flat() : Object.values(value)
3737

3838
const applyTheme = $((theme: Theme) => {
3939
let resolved = theme
@@ -44,14 +44,20 @@ export const ThemeProvider = component$<ThemeProviderProps>(
4444
resolved = getSystemTheme()
4545
}
4646

47-
const name = value ? value[resolved] : resolved
47+
// Join the array of attr if the theme is an array
48+
const computedResolved = Array.isArray(resolved)
49+
? resolved.join(attribute === "class" ? " " : "-")
50+
: resolved
51+
52+
const name = value ? value[computedResolved] : computedResolved
53+
4854
disableTransitionOnChange ? disableAnimation() : null
4955
const d = document.documentElement
5056

5157
if (attribute === "class") {
5258
d.classList.remove(...attrs)
5359

54-
if (name) d.classList.add(name)
60+
if (name) d.classList.add(...name.split(" "))
5561
} else {
5662
if (name) {
5763
d.setAttribute(attribute, name)
@@ -74,13 +80,17 @@ export const ThemeProvider = component$<ThemeProviderProps>(
7480
this.theme = theme
7581

7682
try {
77-
localStorage.setItem(storageKey, theme as string)
83+
localStorage.setItem(storageKey, Array.isArray(theme) ? theme.join(" ") : (theme as string))
7884
} catch (e) {
7985
// Unsupported
8086
}
8187
}),
8288
forcedTheme,
83-
themes: enableSystem ? [...themes, "system"] : themes,
89+
themes: enableSystem
90+
? Array.isArray(themes[0])
91+
? [...(themes as string[][]), ["system"]]
92+
: [...(themes as string[]), "system"]
93+
: themes,
8494
systemTheme: (enableSystem ? resolvedThemeStore.value : undefined) as "light" | "dark" | undefined,
8595
})
8696

src/lib/types.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ interface ValueObject {
66

77
export type SystemTheme = "dark" | "light"
88

9-
export type Theme = "dark" | "light" | (string & {}) | undefined
9+
export type Theme = "dark" | "light" | (string & {}) | string[] | undefined
1010

1111
export interface UseThemeProps {
1212
/** List of all available theme names */
13-
themes: string[]
13+
themes: string[] | string[][]
1414
/** Forced theme name for the current page */
1515
forcedTheme?: string | undefined
1616
/** Update the theme */
@@ -26,7 +26,7 @@ export interface UseThemeProps {
2626

2727
export interface ThemeProviderProps {
2828
/** List of all available theme names */
29-
themes?: string[] | undefined
29+
themes?: string[] | string[][] | undefined
3030
/** Forced theme name for the current page */
3131
forcedTheme?: string | undefined
3232
/** Whether to switch between dark and light themes based on prefers-color-scheme */
@@ -40,7 +40,7 @@ export interface ThemeProviderProps {
4040
/** Default theme name (for v0.0.12 and lower the default was light). If `enableSystem` is false, the default theme is light */
4141
defaultTheme?: string | undefined
4242
/** HTML attribute modified based on the active theme. Accepts `class` and `data-*` (meaning any data attribute, `data-mode`, `data-color`, etc.) */
43-
attribute?: string | "class" | undefined
43+
attribute?: "class" | (string & {}) | undefined
4444
/** Mapping of theme name to HTML attribute value. Object where key is the theme name and value is the attribute value */
4545
value?: ValueObject | undefined
4646
/** Nonce string to pass to the inline script for CSP headers */

src/root.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,21 @@ export default () => {
88
<meta charSet="utf-8" />
99
<title>Qwik Blank App</title>
1010
</head>
11-
<body class="dark">
12-
<ThemeProvider>
13-
<div>Cool</div>
11+
<body>
12+
<ThemeProvider
13+
themes={[
14+
["simple", "light-yellow"],
15+
["simple", "dark-yellow"],
16+
["brutalist", "light-yellow"],
17+
["brutalist", "dark-yellow"],
18+
["hand-drawn", "light"],
19+
["hand-drawn", "dark"],
20+
]}
21+
// themes={["dark", "light"]}
22+
attribute="class"
23+
// defaultTheme="dark"
24+
>
25+
<div>Nested Theme test</div>
1426
<Test />
1527
</ThemeProvider>
1628
</body>

src/test.tsx

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,39 @@ import { component$ } from "@builder.io/qwik"
22
import { useTheme } from "./lib/provider"
33

44
export const Test = component$(() => {
5-
const { theme, setTheme, resolvedTheme } = useTheme()
6-
console.log(theme, resolvedTheme)
5+
const { theme, setTheme, themes, resolvedTheme } = useTheme()
6+
console.log(theme, themes, resolvedTheme)
77
return (
8-
<div>
9-
{theme}
10-
<button
8+
<div
9+
style={{
10+
display: "flex",
11+
flexDirection: "column",
12+
gap: 4,
13+
}}
14+
>
15+
{theme?.toString()}
16+
{themes?.map((theme) => (
17+
<button
18+
key={theme.toString()}
19+
type="button"
20+
onClick$={() => {
21+
console.log(theme)
22+
setTheme(theme)
23+
}}
24+
>
25+
{theme.toString()}
26+
</button>
27+
))}
28+
29+
{/* <button
1130
type="button"
1231
onClick$={() => {
1332
setTheme(theme === "dark" ? "light" : "dark")
1433
// themeSig.value = themeSig.value === "dark" ? "light" : "dark"
1534
}}
1635
>
1736
Toggle
18-
</button>
37+
</button> */}
1938
</div>
2039
)
2140
})

0 commit comments

Comments
 (0)