diff --git a/app/app.vue b/app/app.vue
index 2f5ff3bb5a..7f4f1244d5 100644
--- a/app/app.vue
+++ b/app/app.vue
@@ -4,6 +4,9 @@ import { useEventListener } from '@vueuse/core'
const route = useRoute()
const router = useRouter()
+// Initialize accent color before hydration to prevent flash
+initAccentOnPrehydrate()
+
const isHomepage = computed(() => route.path === '/')
useHead({
diff --git a/app/components/AccentColorPicker.vue b/app/components/AccentColorPicker.vue
new file mode 100644
index 0000000000..8a209732a3
--- /dev/null
+++ b/app/components/AccentColorPicker.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/app/components/AppHeader.vue b/app/components/AppHeader.vue
index 7c1468b631..e3da8fe7bb 100644
--- a/app/components/AppHeader.vue
+++ b/app/components/AppHeader.vue
@@ -24,7 +24,7 @@ const { isConnected, npmUser } = useConnector()
:aria-label="$t('header.home')"
class="header-logo font-mono text-lg font-medium text-fg hover:text-fg transition-colors duration-200 focus-ring rounded"
>
- ./npmx
+ ./npmx
diff --git a/app/components/SettingsMenu.vue b/app/components/SettingsMenu.vue
index 9a857e81bb..3936992678 100644
--- a/app/components/SettingsMenu.vue
+++ b/app/components/SettingsMenu.vue
@@ -163,6 +163,10 @@ onKeyStroke(',', e => {
+
+
= {
+ rose: '#e9aeba',
+ amber: '#fbbf24',
+ emerald: '#34d399',
+ sky: '#38bdf8',
+ violet: '#a78bfa',
+ coral: '#fb7185',
+ }
+ const settings = JSON.parse(localStorage.getItem('npmx-settings') || '{}')
+ const color = settings.accentColorId ? colors[settings.accentColorId as AccentColorId] : null
+ if (color) {
+ document.documentElement.style.setProperty('--accent-color', color)
+ }
+ })
+}
diff --git a/app/pages/index.vue b/app/pages/index.vue
index bbc7a93a76..3dba648d7a 100644
--- a/app/pages/index.vue
+++ b/app/pages/index.vue
@@ -27,7 +27,7 @@ defineOgImageComponent('Default')
- ./npmx
+ ./npmx
/
@@ -69,7 +69,7 @@ defineOgImageComponent('Default')
:placeholder="$t('search.placeholder')"
autocomplete="off"
autofocus
- class="w-full bg-bg-subtle border border-border rounded-lg pl-8 pr-24 py-4 font-mono text-base text-fg placeholder:text-fg-subtle transition-all duration-300 focus:(border-border-hover outline-none)"
+ class="w-full bg-bg-subtle border border-border rounded-lg pl-8 pr-24 py-4 font-mono text-base text-fg placeholder:text-fg-subtle transition-all duration-300 focus:(border-accent outline-none)"
@input="handleSearch"
@focus="isSearchFocused = true"
@blur="isSearchFocused = false"
@@ -103,7 +103,7 @@ defineOgImageComponent('Default')
class="link-subtle font-mono text-sm inline-flex items-center gap-2 group"
>
{{ pkg }}
diff --git a/app/pages/search.vue b/app/pages/search.vue
index ec7cf952cb..ce4cfd0ec2 100644
--- a/app/pages/search.vue
+++ b/app/pages/search.vue
@@ -356,7 +356,7 @@ defineOgImageComponent('Default', {
/
@@ -372,7 +372,7 @@ defineOgImageComponent('Default', {
autocomplete="off"
autocorrect="off"
spellcheck="false"
- class="w-full max-w-full bg-bg-subtle border border-border rounded-lg pl-8 pr-10 py-3 font-mono text-base text-fg placeholder:text-fg-subtle transition-colors duration-300 focus:border-border-hover focus-visible:outline-none appearance-none"
+ class="w-full max-w-full bg-bg-subtle border border-border rounded-lg pl-8 pr-10 py-3 font-mono text-base text-fg placeholder:text-fg-subtle transition-colors duration-300 focus:border-accent focus-visible:outline-none appearance-none"
@focus="isSearchFocused = true"
@blur="isSearchFocused = false"
@keydown="handleResultsKeydown"
diff --git a/shared/utils/constants.ts b/shared/utils/constants.ts
index e4efd422f6..f4ec66d1f1 100644
--- a/shared/utils/constants.ts
+++ b/shared/utils/constants.ts
@@ -16,3 +16,13 @@ export const NPM_MISSING_README_SENTINEL = 'ERROR: No README data found!'
export const ERROR_JSR_FETCH_FAILED = 'Failed to fetch package from JSR registry.'
export const ERROR_NPM_FETCH_FAILED = 'Failed to fetch package from npm registry.'
export const ERROR_SUGGESTIONS_FETCH_FAILED = 'Failed to fetch suggestions.'
+
+// Theming
+export const ACCENT_COLORS = {
+ rose: '#e9aeba',
+ amber: '#fbbf24',
+ emerald: '#34d399',
+ sky: '#38bdf8',
+ violet: '#a78bfa',
+ coral: '#fb7185',
+} as const
diff --git a/test/nuxt/components/DateTime.spec.ts b/test/nuxt/components/DateTime.spec.ts
index 5c5de7e692..a090d0bf30 100644
--- a/test/nuxt/components/DateTime.spec.ts
+++ b/test/nuxt/components/DateTime.spec.ts
@@ -9,6 +9,8 @@ vi.mock('~/composables/useSettings', () => ({
useSettings: () => ({
settings: ref({ relativeDates: mockRelativeDates.value }),
}),
+ useAccentColor: () => ({}),
+ initAccentOnPrehydrate: () => {},
}))
describe('DateTime', () => {
diff --git a/uno.config.ts b/uno.config.ts
index a9eab583c8..4322497726 100644
--- a/uno.config.ts
+++ b/uno.config.ts
@@ -47,8 +47,8 @@ export default defineConfig({
hover: '#404040',
},
accent: {
- DEFAULT: '#ffffff',
- muted: '#e5e5e5',
+ DEFAULT: 'var(--accent-color, #666666)',
+ fallback: '#666666',
},
// Syntax highlighting colors (inspired by GitHub Dark)
syntax: {