Skip to content

Commit 0117864

Browse files
committed
accent colors for light & dark mode
1 parent 8583ba8 commit 0117864

10 files changed

Lines changed: 189 additions & 132 deletions

File tree

app/assets/main.css

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ a:hover {
129129
}
130130

131131
a:focus-visible {
132-
outline: 2px solid var(--border);
132+
outline: 2px solid var(--border-hover);
133133
outline-offset: 2px;
134-
border-radius: 2px;
134+
border-radius: 4px;
135135
}
136136

137137
/* Reset dd margin (browser default is margin-left: 40px) */
@@ -148,6 +148,12 @@ button {
148148
padding: 0;
149149
}
150150

151+
button:focus-visible {
152+
outline: 2px solid var(--border-hover);
153+
outline-offset: 2px;
154+
border-radius: 4px;
155+
}
156+
151157
/* Selection */
152158
::selection {
153159
background-color: var(--fg-muted);

app/components/AppHeader.vue

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,7 @@ onKeyStroke(
101101
:aria-label="$t('nav.tap_to_search')"
102102
@click="expandMobileSearch"
103103
>
104-
<img
105-
aria-hidden="true"
106-
:alt="$t('alt_logo')"
107-
src="/logo.svg"
108-
width="96"
109-
height="96"
110-
class="w-8 h-8 rounded-lg"
111-
/>
104+
<AppLogo class="w-8 h-8 rounded-lg" />
112105
<span class="i-carbon:search w-4 h-4 text-fg-subtle" aria-hidden="true" />
113106
</button>
114107

@@ -118,16 +111,9 @@ onKeyStroke(
118111
to="/"
119112
:aria-label="$t('header.home')"
120113
dir="ltr"
121-
class="inline-flex items-center gap-2 header-logo font-mono text-lg font-medium text-fg hover:text-fg transition-colors duration-200 focus-ring rounded"
114+
class="inline-flex items-center gap-2 header-logo font-mono text-lg font-medium text-fg hover:text-fg focus-visible:outline-accent/50 transition-colors duration-200 rounded"
122115
>
123-
<img
124-
aria-hidden="true"
125-
:alt="$t('alt_logo')"
126-
src="/logo.svg"
127-
width="96"
128-
height="96"
129-
class="w-8 h-8 rounded-lg"
130-
/>
116+
<AppLogo class="w-8 h-8 rounded-lg" />
131117
<span>npmx</span>
132118
</NuxtLink>
133119
</div>
@@ -165,11 +151,11 @@ onKeyStroke(
165151
</div>
166152

167153
<!-- End: Desktop nav items + Mobile menu button -->
168-
<div class="flex-shrink-0 flex items-center gap-4 sm:gap-6">
154+
<div class="flex-shrink-0 flex items-center lg:gap-4">
169155
<!-- Desktop: Compare link -->
170156
<NuxtLink
171157
to="/compare"
172-
class="hidden sm:inline-flex link-subtle font-mono text-sm items-center gap-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50 rounded"
158+
class="hidden sm:inline-flex link-subtle px-2 py-1.5 hover:bg-bg-subtle font-mono text-sm items-center gap-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50 rounded"
173159
aria-keyshortcuts="c"
174160
>
175161
{{ $t('nav.compare') }}
@@ -184,7 +170,7 @@ onKeyStroke(
184170
<!-- Desktop: Settings link -->
185171
<NuxtLink
186172
to="/settings"
187-
class="hidden sm:inline-flex link-subtle font-mono text-sm items-center gap-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50 rounded"
173+
class="hidden sm:inline-flex link-subtle px-2 py-1.5 hover:bg-bg-subtle font-mono text-sm items-center gap-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50 rounded"
188174
aria-keyshortcuts=","
189175
>
190176
{{ $t('nav.settings') }}

app/components/AppLogo.vue

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<script setup lang="ts">
2+
defineProps<{
3+
class?: string
4+
}>()
5+
</script>
6+
7+
<template>
8+
<svg
9+
aria-hidden="true"
10+
xmlns="http://www.w3.org/2000/svg"
11+
viewBox="0 0 512 512"
12+
width="96"
13+
height="96"
14+
:class="class"
15+
>
16+
<title>{{ $t('alt_logo') }}</title>
17+
<rect fill="var(--bg)" width="512" height="512" rx="64" />
18+
<rect fill="var(--fg)" x="110" y="310" width="60" height="60" />
19+
<text
20+
fill="var(--accent)"
21+
x="320"
22+
y="370"
23+
font-family="ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace"
24+
font-size="420"
25+
font-weight="500"
26+
text-anchor="middle"
27+
>
28+
<tspan>/</tspan>
29+
</text>
30+
</svg>
31+
</template>

app/components/HeaderAccountMenu.client.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ function openAuthModal() {
5858
<div ref="accountMenuRef" class="relative">
5959
<button
6060
type="button"
61-
class="relative flex items-center justify-end gap-2 px-2 py-1.5 min-w-24 rounded-md transition-colors duration-200 hover:bg-bg-subtle focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
61+
class="relative flex items-center justify-end gap-2 px-2 py-1.5 min-w-24 rounded-md transition-colors duration-200 hover:bg-bg-subtle hover:text-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50"
6262
:aria-expanded="isOpen"
6363
aria-haspopup="true"
6464
@click="isOpen = !isOpen"

app/composables/useSettings.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useLocalStorage } from '@vueuse/core'
33
import { ACCENT_COLORS } from '#shared/utils/constants'
44
import type { LocaleObject } from '@nuxtjs/i18n'
55

6-
type AccentColorId = keyof typeof ACCENT_COLORS
6+
type AccentColorId = keyof typeof ACCENT_COLORS.light
77

88
/**
99
* Application settings stored in localStorage
@@ -70,20 +70,34 @@ export function useRelativeDates() {
7070
*/
7171
export function useAccentColor() {
7272
const { settings } = useSettings()
73+
const colorMode = useColorMode()
7374

74-
const accentColors = Object.entries(ACCENT_COLORS).map(([id, value]) => ({
75-
id: id as AccentColorId,
76-
name: id,
77-
value,
78-
}))
75+
const accentColors = computed(() =>
76+
Object.entries(ACCENT_COLORS[colorMode.value as 'light' | 'dark']).map(([id, value]) => ({
77+
id: id as AccentColorId,
78+
name: id,
79+
value,
80+
})),
81+
)
82+
83+
const currentAccentColor = computed(() => {
84+
const id = settings.value.accentColorId
85+
const theme = colorMode.value as 'light' | 'dark'
86+
return id ? ACCENT_COLORS[theme][id] : null
87+
})
88+
89+
// Simple client-side check
90+
if (process.client) {
91+
watchEffect(() => {
92+
if (currentAccentColor.value) {
93+
document.documentElement.style.setProperty('--accent-color', currentAccentColor.value)
94+
} else {
95+
document.documentElement.style.removeProperty('--accent-color')
96+
}
97+
})
98+
}
7999

80100
function setAccentColor(id: AccentColorId | null) {
81-
const color = id ? ACCENT_COLORS[id] : null
82-
if (color) {
83-
document.documentElement.style.setProperty('--accent-color', color)
84-
} else {
85-
document.documentElement.style.removeProperty('--accent-color')
86-
}
87101
settings.value.accentColorId = id
88102
}
89103

app/pages/[...package].vue

Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -467,66 +467,10 @@ function handleClick(event: MouseEvent) {
467467
</ul>
468468
</template>
469469
</ClientOnly>
470-
471-
<!-- Internal navigation: Docs + Code + Compare (hidden on mobile, shown in external links instead) -->
472-
<nav
473-
v-if="displayVersion"
474-
:aria-label="$t('package.navigation')"
475-
class="hidden sm:flex items-center gap-0.5 p-0.5 bg-bg-subtle border border-border-subtle rounded-md shrink-0 ms-auto self-center"
476-
>
477-
<NuxtLink
478-
v-if="docsLink"
479-
:to="docsLink"
480-
class="px-2 py-1.5 font-mono text-xs rounded transition-colors duration-150 border border-transparent text-fg-subtle hover:text-fg hover:bg-bg hover:shadow hover:border-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 inline-flex items-center gap-1.5"
481-
aria-keyshortcuts="d"
482-
>
483-
<span class="i-carbon:document w-3 h-3" aria-hidden="true" />
484-
{{ $t('package.links.docs') }}
485-
<kbd
486-
class="inline-flex items-center justify-center w-4 h-4 text-xs bg-bg-muted border border-border rounded"
487-
aria-hidden="true"
488-
>
489-
d
490-
</kbd>
491-
</NuxtLink>
492-
<NuxtLink
493-
:to="{
494-
name: 'code',
495-
params: {
496-
path: [...pkg.name.split('/'), 'v', displayVersion.version],
497-
},
498-
}"
499-
class="px-2 py-1.5 font-mono text-xs rounded transition-colors duration-150 border border-transparent text-fg-subtle hover:text-fg hover:bg-bg hover:shadow hover:border-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 inline-flex items-center gap-1.5"
500-
aria-keyshortcuts="."
501-
>
502-
<span class="i-carbon:code w-3 h-3" aria-hidden="true" />
503-
{{ $t('package.links.code') }}
504-
<kbd
505-
class="inline-flex items-center justify-center w-4 h-4 text-xs bg-bg-muted border border-border rounded"
506-
aria-hidden="true"
507-
>
508-
.
509-
</kbd>
510-
</NuxtLink>
511-
<NuxtLink
512-
:to="{ path: '/compare', query: { packages: pkg.name } }"
513-
class="px-2 py-1.5 font-mono text-xs rounded transition-colors duration-150 border border-transparent text-fg-subtle hover:text-fg hover:bg-bg hover:shadow hover:border-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 inline-flex items-center gap-1.5"
514-
aria-keyshortcuts="c"
515-
>
516-
<span class="i-carbon:compare w-3 h-3" aria-hidden="true" />
517-
{{ $t('package.links.compare') }}
518-
<kbd
519-
class="inline-flex items-center justify-center w-4 h-4 text-xs bg-bg-muted border border-border rounded"
520-
aria-hidden="true"
521-
>
522-
c
523-
</kbd>
524-
</NuxtLink>
525-
</nav>
526470
</div>
527471

528472
<!-- Description container with min-height to prevent CLS -->
529-
<div class="max-w-2xl min-h-[4.5rem]">
473+
<div class="max-w-2xl ms-1 min-h-[4.5rem]">
530474
<p v-if="pkg.description" class="text-fg-muted text-base m-0">
531475
<MarkdownText :text="pkg.description" :package-name="pkg.name" />
532476
</p>
@@ -535,8 +479,64 @@ function handleClick(event: MouseEvent) {
535479
</p>
536480
</div>
537481

482+
<!-- Internal navigation: Docs + Code + Compare (hidden on mobile, shown in external links instead) -->
483+
<nav
484+
v-if="displayVersion"
485+
:aria-label="$t('package.navigation')"
486+
class="hidden sm:inline-flex items-center gap-0.5 p-0.5 bg-bg-subtle border border-border-subtle rounded-md shrink-0 self-center mb-5"
487+
>
488+
<NuxtLink
489+
v-if="docsLink"
490+
:to="docsLink"
491+
class="px-2 py-1.5 font-mono text-xs rounded transition-colors duration-150 border border-transparent text-fg-subtle hover:text-fg hover:bg-bg hover:shadow hover:border-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 inline-flex items-center gap-1.5"
492+
aria-keyshortcuts="d"
493+
>
494+
<span class="i-carbon:document w-3 h-3" aria-hidden="true" />
495+
{{ $t('package.links.docs') }}
496+
<kbd
497+
class="inline-flex items-center justify-center w-4 h-4 text-xs bg-bg-muted border border-border rounded"
498+
aria-hidden="true"
499+
>
500+
d
501+
</kbd>
502+
</NuxtLink>
503+
<NuxtLink
504+
:to="{
505+
name: 'code',
506+
params: {
507+
path: [...pkg.name.split('/'), 'v', displayVersion.version],
508+
},
509+
}"
510+
class="px-2 py-1.5 font-mono text-xs rounded transition-colors duration-150 border border-transparent text-fg-subtle hover:text-fg hover:bg-bg hover:shadow hover:border-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 inline-flex items-center gap-1.5"
511+
aria-keyshortcuts="."
512+
>
513+
<span class="i-carbon:code w-3 h-3" aria-hidden="true" />
514+
{{ $t('package.links.code') }}
515+
<kbd
516+
class="inline-flex items-center justify-center w-4 h-4 text-xs bg-bg-muted border border-border rounded"
517+
aria-hidden="true"
518+
>
519+
.
520+
</kbd>
521+
</NuxtLink>
522+
<NuxtLink
523+
:to="{ path: '/compare', query: { packages: pkg.name } }"
524+
class="px-2 py-1.5 font-mono text-xs rounded transition-colors duration-150 border border-transparent text-fg-subtle hover:text-fg hover:bg-bg hover:shadow hover:border-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 inline-flex items-center gap-1.5"
525+
aria-keyshortcuts="c"
526+
>
527+
<span class="i-carbon:compare w-3 h-3" aria-hidden="true" />
528+
{{ $t('package.links.compare') }}
529+
<kbd
530+
class="inline-flex items-center justify-center w-4 h-4 text-xs bg-bg-muted border border-border rounded"
531+
aria-hidden="true"
532+
>
533+
c
534+
</kbd>
535+
</NuxtLink>
536+
</nav>
537+
538538
<!-- External links -->
539-
<ul class="flex flex-wrap items-center gap-x-3 gap-y-1.5 sm:gap-4 list-none m-0 p-0 mt-3">
539+
<ul class="flex flex-wrap items-center gap-x-3 gap-y-1.5 sm:gap-4 list-none m-0 p-0">
540540
<li v-if="repositoryUrl">
541541
<a
542542
:href="repositoryUrl"

app/pages/index.vue

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,7 @@ defineOgImageComponent('Default', {
4242
dir="ltr"
4343
class="flex items-center justify-center gap-2 header-logo font-mono text-5xl sm:text-7xl md:text-8xl font-medium tracking-tight mb-4 motion-safe:animate-fade-in motion-safe:animate-fill-both"
4444
>
45-
<img
46-
aria-hidden="true"
47-
:alt="$t('alt_logo')"
48-
src="/logo.svg"
49-
width="48"
50-
height="48"
51-
class="w-12 h-12 sm:w-20 sm:h-20 md:w-24 md:h-24 rounded-2xl sm:rounded-3xl"
52-
/>
45+
<AppLogo class="w-12 h-12 sm:w-20 sm:h-20 md:w-24 md:h-24 rounded-2xl sm:rounded-3xl" />
5346
<span class="pb-4">npmx</span>
5447
</h1>
5548

app/utils/prehydrate.ts

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { ACCENT_COLORS } from '#shared/utils/constants'
2-
3-
type AccentColorId = keyof typeof ACCENT_COLORS
2+
type AccentColorId = keyof typeof ACCENT_COLORS.light // for both themes color names are same
43

54
/**
65
* Initialize user preferences before hydration to prevent flash/layout shift.
@@ -14,13 +13,23 @@ export function initPreferencesOnPrehydrate() {
1413
// All constants must be hardcoded inside the callback.
1514
onPrehydrate(() => {
1615
// Accent colors - hardcoded since ACCENT_COLORS can't be referenced
17-
const colors: Record<AccentColorId, string> = {
18-
rose: 'oklch(0.797 0.084 11.056)',
19-
amber: 'oklch(0.828 0.165 84.429)',
20-
emerald: 'oklch(0.792 0.153 166.95)',
21-
sky: 'oklch(0.787 0.128 230.318)',
22-
violet: 'oklch(0.714 0.148 286.067)',
23-
coral: 'oklch(0.704 0.177 14.75)',
16+
const colors = {
17+
light: {
18+
rose: 'oklch(0.70 0.15 11.056)',
19+
amber: 'oklch(0.73 0.18 84.429)',
20+
emerald: 'oklch(0.70 0.17 166.95)',
21+
sky: 'oklch(0.70 0.15 230.318)',
22+
violet: 'oklch(0.70 0.17 286.067)',
23+
coral: 'oklch(0.70 0.19 14.75)',
24+
},
25+
dark: {
26+
rose: 'oklch(0.797 0.084 11.056)',
27+
amber: 'oklch(0.828 0.165 84.429)',
28+
emerald: 'oklch(0.792 0.153 166.95)',
29+
sky: 'oklch(0.787 0.128 230.318)',
30+
violet: 'oklch(0.714 0.148 286.067)',
31+
coral: 'oklch(0.704 0.177 14.75)',
32+
},
2433
}
2534

2635
// Valid package manager IDs
@@ -29,10 +38,13 @@ export function initPreferencesOnPrehydrate() {
2938
// Read settings from localStorage
3039
const settings = JSON.parse(localStorage.getItem('npmx-settings') || '{}')
3140

32-
// Apply accent color
33-
const color = settings.accentColorId ? colors[settings.accentColorId as AccentColorId] : null
34-
if (color) {
35-
document.documentElement.style.setProperty('--accent-color', color)
41+
// Determine theme (default to 'dark')
42+
const theme = document.documentElement.dataset.theme === 'light' ? 'light' : 'dark'
43+
44+
// Apply accent color based on theme
45+
const accentColorId = settings.accentColorId as AccentColorId | undefined
46+
if (accentColorId && colors[theme][accentColorId]) {
47+
document.documentElement.style.setProperty('--accent-color', colors[theme][accentColorId])
3648
}
3749

3850
// Read and apply package manager preference
@@ -55,7 +67,6 @@ export function initPreferencesOnPrehydrate() {
5567

5668
// Set data attribute for CSS-based visibility
5769
document.documentElement.dataset.pm = pm
58-
5970
document.documentElement.dataset.collapsed = settings.sidebar?.collapsed?.join(' ') ?? ''
6071
})
6172
}

0 commit comments

Comments
 (0)