@@ -13,6 +13,24 @@ withDefaults(
1313const { isConnected, npmUser } = useConnector ()
1414
1515const router = useRouter ()
16+ const route = useRoute ()
17+
18+ const searchQuery = ref (' ' )
19+ const isSearchFocused = ref (false )
20+
21+ const showSearchBar = computed (() => {
22+ return route .name !== ' search' && route .name !== ' index'
23+ })
24+
25+ async function handleSearchInput() {
26+ const query = searchQuery .value .trim ()
27+ await router .push ({
28+ name: ' search' ,
29+ query: query ? { q: query } : undefined ,
30+ })
31+ searchQuery .value = ' '
32+ }
33+
1634onKeyStroke (' ,' , e => {
1735 // Don't trigger if user is typing in an input
1836 const target = e .target as HTMLElement
@@ -45,40 +63,73 @@ onKeyStroke(',', e => {
4563 <span v-else class =" w-1" />
4664 </div >
4765
48- <!-- Center: Main nav items -->
49- <ul class =" flex-1 flex items-center justify-center gap-4 sm:gap-6 list-none m-0 p-0" >
50- <li class =" flex items-center" >
51- <NuxtLink
52- to =" /search"
53- class =" link-subtle font-mono text-sm inline-flex items-center gap-2"
54- aria-keyshortcuts =" /"
66+ <!-- Center: Search bar + nav items -->
67+ <div class =" flex-1 flex items-center justify-center gap-4 sm:gap-6" >
68+ <!-- Search bar (shown on all pages except home and search) -->
69+ <search v-if =" showSearchBar" class =" hidden sm:block flex-1 max-w-md" >
70+ <form
71+ role =" search"
72+ method =" GET"
73+ action =" /search"
74+ class =" relative"
75+ @submit.prevent =" handleSearchInput"
5576 >
56- {{ $t('nav.search') }}
57- <kbd
58- class =" hidden sm:inline-flex items-center justify-center w-5 h-5 text-xs bg-bg-muted border border-border rounded"
59- aria-hidden =" true"
60- >
61- /
62- </kbd >
63- </NuxtLink >
64- </li >
65-
66- <!-- Packages dropdown (when connected) -->
67- <li v-if =" isConnected && npmUser" class =" flex items-center" >
68- <HeaderPackagesDropdown :username =" npmUser" />
69- </li >
70-
71- <!-- Orgs dropdown (when connected) -->
72- <li v-if =" isConnected && npmUser" class =" flex items-center" >
73- <HeaderOrgsDropdown :username =" npmUser" />
74- </li >
75- </ul >
77+ <label for =" header-search" class =" sr-only" >
78+ {{ $t('search.label') }}
79+ </label >
80+
81+ <div class =" relative group" :class =" { 'is-focused': isSearchFocused }" >
82+ <div class =" search-box relative flex items-center" >
83+ <span
84+ class =" absolute left-3 text-fg-subtle font-mono text-sm pointer-events-none transition-colors duration-200 motion-reduce:transition-none group-focus-within:text-accent z-1"
85+ >
86+ /
87+ </span >
88+
89+ <input
90+ id =" header-search"
91+ v-model =" searchQuery"
92+ type =" search"
93+ name =" q"
94+ :placeholder =" $t('search.placeholder')"
95+ v-bind =" noCorrect"
96+ class =" w-full bg-bg-subtle border border-border rounded-md pl-7 pr-3 py-1.5 font-mono text-sm text-fg placeholder:text-fg-subtle transition-border-color duration-300 motion-reduce:transition-none focus:border-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50"
97+ autocomplete =" off"
98+ @input =" handleSearchInput"
99+ @focus =" isSearchFocused = true"
100+ @blur =" isSearchFocused = false"
101+ />
102+ <button type =" submit" class =" sr-only" >{{ $t('search.button') }}</button >
103+ </div >
104+ </div >
105+ </form >
106+ </search >
107+
108+ <ul class =" flex items-center gap-4 sm:gap-6 list-none m-0 p-0" >
109+ <!-- Packages dropdown (when connected) -->
110+ <li v-if =" isConnected && npmUser" class =" flex items-center" >
111+ <HeaderPackagesDropdown :username =" npmUser" />
112+ </li >
113+
114+ <!-- Orgs dropdown (when connected) -->
115+ <li v-if =" isConnected && npmUser" class =" flex items-center" >
116+ <HeaderOrgsDropdown :username =" npmUser" />
117+ </li >
118+ </ul >
119+ </div >
76120
77121 <!-- Right: User status + GitHub -->
78- <div class =" flex-shrink-0 flex items-center gap-6" >
122+ <div class =" flex-shrink-0 flex items-center gap-4 sm:gap-6 ml-auto sm:ml-0" >
123+ <NuxtLink
124+ to =" /about"
125+ class =" sm:hidden link-subtle font-mono text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50 rounded"
126+ >
127+ {{ $t('footer.about') }}
128+ </NuxtLink >
129+
79130 <NuxtLink
80131 to =" /settings"
81- class =" link-subtle font-mono text-sm inline-flex items-center gap-2"
132+ class =" link-subtle font-mono text-sm inline-flex items-center gap-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50 rounded "
82133 aria-keyshortcuts =" ,"
83134 >
84135 {{ $t('nav.settings') }}
@@ -90,19 +141,9 @@ onKeyStroke(',', e => {
90141 </kbd >
91142 </NuxtLink >
92143
93- <div v-if =" showConnector" >
144+ <div v-if =" showConnector" class = " hidden sm:block " >
94145 <ConnectorStatus />
95146 </div >
96-
97- <a
98- href =" https://github.com/npmx-dev/npmx.dev"
99- target =" _blank"
100- rel =" noopener noreferrer"
101- class =" link-subtle"
102- :aria-label =" $t('header.github')"
103- >
104- <span class =" i-carbon-logo-github w-5 h-5" aria-hidden =" true" />
105- </a >
106147 </div >
107148 </nav >
108149 </header >
0 commit comments