Skip to content

Commit 627c819

Browse files
committed
feat: add background theming
1 parent 4e08976 commit 627c819

7 files changed

Lines changed: 150 additions & 9 deletions

File tree

app/assets/main.css

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77

88
:root[data-theme='dark'] {
99
/* background colors */
10-
--bg: oklch(0.145 0 0);
11-
--bg-subtle: oklch(0.178 0 0);
12-
--bg-muted: oklch(0.218 0 0);
13-
--bg-elevated: oklch(0.252 0 0);
10+
--bg: var(--bg-color, oklch(0.145 0 0));
11+
--bg-subtle: var(--bg-subtle-color, oklch(0.178 0 0));
12+
--bg-muted: var(--bg-muted-color, oklch(0.218 0 0));
13+
--bg-elevated: var(--bg-elevated-color, oklch(0.252 0 0));
1414

1515
/* text colors */
1616
--fg: oklch(0.985 0 0);
@@ -43,11 +43,32 @@
4343
--badge-pink: oklch(0.584 0.189 343);
4444
}
4545

46+
:root[data-theme='dark'][data-bg-theme='slate'] {
47+
--bg: oklch(0.129 0.012 264.695);
48+
--bg-subtle: oklch(0.159 0.022 262.421);
49+
--bg-muted: oklch(0.204 0.033 261.234);
50+
--bg-elevated: oklch(0.259 0.041 260.031);
51+
}
52+
53+
:root[data-theme='dark'][data-bg-theme='zinc'] {
54+
--bg: oklch(0.141 0.005 285.823);
55+
--bg-subtle: oklch(0.168 0.005 285.894);
56+
--bg-muted: oklch(0.209 0.005 285.929);
57+
--bg-elevated: oklch(0.256 0.006 286.033);
58+
}
59+
60+
:root[data-theme='dark'][data-bg-theme='stone'] {
61+
--bg: oklch(0.147 0.004 49.25);
62+
--bg-subtle: oklch(0.178 0.004 49.321);
63+
--bg-muted: oklch(0.218 0.004 49.386);
64+
--bg-elevated: oklch(0.252 0.007 34.298);
65+
}
66+
4667
:root[data-theme='light'] {
47-
--bg: oklch(1 0 0);
48-
--bg-subtle: oklch(0.979 0.001 286.375);
49-
--bg-muted: oklch(0.955 0 0);
50-
--bg-elevated: oklch(0.94 0 0);
68+
--bg: var(--bg-color, oklch(1 0 0));
69+
--bg-subtle: var(--bg-subtle-color, oklch(0.979 0.001 286.375));
70+
--bg-muted: var(--bg-muted-color, oklch(0.955 0 0));
71+
--bg-elevated: var(--bg-elevated-color, oklch(0.94 0 0));
5172

5273
--fg: oklch(0.145 0 0);
5374
--fg-muted: oklch(0.439 0 0);
@@ -75,6 +96,27 @@
7596
--badge-cyan: oklch(0.571 0.181 210);
7697
}
7798

99+
:root[data-theme='light'][data-bg-theme='slate'] {
100+
--bg: oklch(1 0 0);
101+
--bg-subtle: oklch(0.982 0.006 264.62);
102+
--bg-muted: oklch(0.96 0.041 261.234);
103+
--bg-elevated: oklch(0.943 0.013 255.52);
104+
}
105+
106+
:root[data-theme='light'][data-bg-theme='zinc'] {
107+
--bg: oklch(1 0 0);
108+
--bg-subtle: oklch(0.979 0.004 286.53);
109+
--bg-muted: oklch(0.958 0.004 286.39);
110+
--bg-elevated: oklch(0.939 0.004 286.32);
111+
}
112+
113+
:root[data-theme='light'][data-bg-theme='stone'] {
114+
--bg: oklch(1 0 0);
115+
--bg-subtle: oklch(0.979 0.005 48.762);
116+
--bg-muted: oklch(0.958 0.005 48.743);
117+
--bg-elevated: oklch(0.943 0.005 48.731);
118+
}
119+
78120
html {
79121
-webkit-font-smoothing: antialiased;
80122
-moz-osx-font-smoothing: grayscale;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<script setup lang="ts">
2+
import { useBackgroundTheme } from '~/composables/useBackgroundTheme'
3+
4+
const { backgroundThemes, selectedBackgroundTheme, setBackgroundTheme } = useBackgroundTheme()
5+
6+
onPrehydrate(el => {
7+
const id = localStorage.getItem('npmx-background-theme')
8+
if (id) {
9+
const input = el.querySelector<HTMLInputElement>(`input[value="${id || 'neutral'}"]`)
10+
if (input) {
11+
input.checked = true
12+
}
13+
}
14+
})
15+
</script>
16+
17+
<template>
18+
<fieldset class="flex items-center gap-4">
19+
<legend class="sr-only">{{ $t('settings.background_themes') }}</legend>
20+
<label
21+
v-for="theme in backgroundThemes"
22+
:key="theme.id"
23+
class="size-6 rounded-full transition-transform duration-150 motion-safe:hover:scale-110 cursor-pointer has-[:checked]:(ring-2 ring-fg ring-offset-2 ring-offset-bg-subtle) has-[:focus-visible]:(ring-2 ring-fg ring-offset-2 ring-offset-bg-subtle)"
24+
:style="{ backgroundColor: theme.value }"
25+
>
26+
<input
27+
type="radio"
28+
name="background-theme"
29+
class="sr-only"
30+
:value="theme.id"
31+
:checked="selectedBackgroundTheme === theme.id"
32+
:aria-label="theme.name"
33+
@change="setBackgroundTheme(theme.id)"
34+
/>
35+
</label>
36+
</fieldset>
37+
</template>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { RemovableRef } from '@vueuse/core'
2+
import { useLocalStorage } from '@vueuse/core'
3+
import { BACKGROUND_THEMES } from '#shared/utils/constants'
4+
5+
type BackgroundThemeId = keyof typeof BACKGROUND_THEMES
6+
7+
export const BACKGROUND_THEME_STORAGE_KEY = 'npmx-background-theme'
8+
9+
const backgroundThemeRef: RemovableRef<BackgroundThemeId | null> = useLocalStorage(
10+
BACKGROUND_THEME_STORAGE_KEY,
11+
null,
12+
)
13+
14+
export function useBackgroundTheme() {
15+
const backgroundThemes = Object.entries(BACKGROUND_THEMES).map(([id, value]) => ({
16+
id: id as BackgroundThemeId,
17+
name: id,
18+
value,
19+
}))
20+
21+
function setBackgroundTheme(id: BackgroundThemeId | null) {
22+
if (id) {
23+
document.documentElement.dataset.bgTheme = id
24+
} else {
25+
document.documentElement.removeAttribute('data-bg-theme')
26+
}
27+
backgroundThemeRef.value = id
28+
}
29+
30+
return {
31+
backgroundThemes,
32+
selectedBackgroundTheme: computed(() => backgroundThemeRef.value),
33+
setBackgroundTheme,
34+
}
35+
}

app/composables/useColors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export function useCssVariables(
8181
if (options.watchHtmlAttributes && isClientSupported.value) {
8282
useMutationObserver(document.documentElement, () => void colors.value, {
8383
attributes: true,
84-
attributeFilter: ['class', 'style', 'data-theme'],
84+
attributeFilter: ['class', 'style', 'data-theme', 'data-bg-theme'],
8585
})
8686
}
8787

app/pages/settings.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ const setLocale: typeof setNuxti18nLocale = locale => {
9797
</span>
9898
<SettingsAccentColorPicker />
9999
</div>
100+
101+
<!-- Background themes -->
102+
<div class="space-y-3">
103+
<span class="block text-sm text-fg font-medium">
104+
{{ $t('settings.background_themes') }}
105+
</span>
106+
<SettingsBgThemePicker />
107+
</div>
100108
</div>
101109
</section>
102110

nuxt.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ export default defineNuxtConfig({
7373
href: '/opensearch.xml',
7474
},
7575
],
76+
script: [
77+
{
78+
innerHTML: `
79+
(function () {
80+
const preferredBackgroundTheme = localStorage.getItem('npmx-background-theme')
81+
if (preferredBackgroundTheme) {
82+
document.documentElement.dataset.bgTheme = preferredBackgroundTheme
83+
}
84+
})()
85+
`,
86+
},
87+
],
7688
},
7789
},
7890

shared/utils/constants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,10 @@ export const ACCENT_COLORS = {
3434
violet: 'oklch(0.714 0.148 286.067)',
3535
coral: 'oklch(0.704 0.177 14.75)',
3636
} as const
37+
38+
export const BACKGROUND_THEMES = {
39+
neutral: 'oklch(0.555 0 0)',
40+
stone: 'oklch(0.555 0.013 58.123)',
41+
zinc: 'oklch(0.555 0.016 285.931)',
42+
slate: 'oklch(0.555 0.046 257.407)',
43+
} as const

0 commit comments

Comments
 (0)