@@ -9,167 +9,243 @@ const { commandPaletteShortcutLabel } = usePlatformModifierKey()
99const modalRef = useTemplateRef (' modalRef' )
1010const showModal = () => modalRef .value ?.showModal ?.()
1111const closeModal = () => modalRef .value ?.close ?.()
12+
13+ type FooterLink =
14+ | { name: string ; href: string ; kbd? : never ; type? : never }
15+ | { name: string ; kbd: string ; href? : never ; type? : never }
16+ | { name: string ; type: ' button' ; href? : never ; kbd? : never }
17+
18+ const socialLinks = computed (() => [
19+ {
20+ id: ' github' ,
21+ href: ' https://repo.npmx.dev' ,
22+ icon: ' i-simple-icons:github' ,
23+ },
24+ {
25+ id: ' discord' ,
26+ href: discord .value .url ,
27+ icon: ' i-simple-icons:discord' ,
28+ },
29+ {
30+ id: ' bluesky' ,
31+ href: ' https://social.npmx.dev' ,
32+ icon: ' i-simple-icons:bluesky' ,
33+ },
34+ ])
35+
36+ const footerSections: Array <{ label: string ; links: FooterLink [] }> = [
37+ {
38+ label: ' resources' ,
39+ links: [
40+ {
41+ name: ' footer.blog' ,
42+ href: ' blog' ,
43+ },
44+ {
45+ name: ' footer.about' ,
46+ href: ' about' ,
47+ },
48+ {
49+ name: ' a11y.footer_title' ,
50+ href: ' accessibility' ,
51+ },
52+ {
53+ name: ' privacy_policy.title' ,
54+ href: ' privacy' ,
55+ },
56+ ],
57+ },
58+ {
59+ label: ' features' ,
60+ links: [
61+ {
62+ name: ' shortcuts.compare' ,
63+ href: ' compare' ,
64+ },
65+ {
66+ name: ' shortcuts.settings' ,
67+ href: ' settings' ,
68+ },
69+ {
70+ name: ' footer.keyboard_shortcuts' ,
71+ type: ' button' ,
72+ },
73+ ],
74+ },
75+ {
76+ label: ' other' ,
77+ links: [
78+ {
79+ name: ' pds.title' ,
80+ href: ' pds' ,
81+ },
82+ {
83+ name: ' footer.docs' ,
84+ href: NPMX_DOCS_SITE ,
85+ },
86+ ],
87+ },
88+ ]
1289 </script >
1390
1491<template >
15- <footer class =" border-t border-border mt-auto" >
16- <div class =" container py-3 sm:py-8 flex flex-col gap-2 sm:gap-4 text-fg-subtle text-sm" >
17- <div class =" flex flex-col lg:flex-row lg:items-baseline justify-between gap-2 sm:gap-4" >
18- <div >
19- <p class =" font-mono text-balance m-0 hidden sm:block mb-3" >
20- {{ $t('tagline') }}
21- </p >
22- <BuildEnvironment v-if =" !isHome" footer />
23- </div >
24- <!-- Desktop: Show all links. Mobile: Links are in MobileMenu -->
25- <div class =" hidden sm:flex flex-col lg:items-end gap-3 min-h-11 text-xs" >
26- <div class =" flex items-center gap-5" >
27- <LinkBase :to =" { name: 'about' }" >
28- {{ $t('footer.about') }}
29- </LinkBase >
30- <LinkBase :to =" { name: 'blog' }" >
31- {{ $t('footer.blog') }}
32- </LinkBase >
33- <LinkBase :to =" { name: 'privacy' }" >
34- {{ $t('privacy_policy.title') }}
35- </LinkBase >
36- <LinkBase :to =" { name: 'accessibility' }" >
37- {{ $t('a11y.footer_title') }}
38- </LinkBase >
39- <LinkBase :to =" { name: 'translation-status' }" >
40- {{ $t('translation_status.title') }}
41- </LinkBase >
42- <LinkBase :to =" { name: 'brand' }" >
43- {{ $t('footer.brand') }}
44- </LinkBase >
45- <button
46- type =" button"
47- class =" cursor-pointer group inline-flex gap-x-1 items-center justify-center underline-offset-[0.2rem] underline decoration-1 decoration-fg/30 font-mono text-fg hover:(decoration-accent text-accent) focus-visible:(decoration-accent text-accent) transition-colors duration-200"
48- @click.prevent =" showModal"
49- aria-haspopup =" dialog"
50- >
51- {{ $t('footer.keyboard_shortcuts') }}
52- </button >
53-
54- <Modal
55- id =" keyboard-shortcuts-modal"
56- ref =" modalRef"
57- :modalTitle =" $t('footer.keyboard_shortcuts')"
58- class =" w-auto max-w-lg"
92+ <footer class =" border-t border-border md:mt-auto md:pt-8 duration-200 transition-all" >
93+ <div class =" container flex flex-col gap-3" >
94+ <!-- Desktop: Show all links. Mobile: Links are in MobileMenu -->
95+ <div
96+ class =" hidden md:flex flex-col lg:flex-row gap-6 lg:gap-0 lg:justify-between py-3 duration-200 transition-all"
97+ >
98+ <div class =" flex flex-col gap-6" >
99+ <div class =" flex flex-col items-start gap-3" >
100+ <AppLogo class =" h-7 w-auto" />
101+ <BuildEnvironment v-if =" !isHome" footer />
102+ </div >
103+ <div class =" space-x-3" >
104+ <NuxtLink
105+ v-for =" link in socialLinks"
106+ :key =" link.id"
107+ :to =" link.href"
108+ target =" _blank"
109+ class =" text-fg-muted hover:text-accent transition-all duration-200"
59110 >
60- <p class =" mb-4 text-sm leading-relaxed text-fg-muted" >
61- {{
62- $t('shortcuts.command_palette_description', { ctrlKey: $t('shortcuts.ctrl_key') })
63- }}
64- </p >
65- <p class =" mb-2 font-mono text-fg-subtle" >
66- {{ $t('shortcuts.section.global') }}
67- </p >
68- <ul class =" mb-6 flex flex-col gap-2" >
69- <li class =" flex gap-2 items-center" >
70- <kbd class =" kbd" >{{ commandPaletteShortcutLabel }}</kbd >
71- <span >{{ $t('shortcuts.command_palette') }}</span >
72- </li >
73- <li class =" flex gap-2 items-center" >
74- <kbd class =" kbd" >/</kbd >
75- <span >{{ $t('shortcuts.focus_search') }}</span >
76- </li >
77- <li class =" flex gap-2 items-center" >
78- <kbd class =" kbd" >?</kbd >
79- <span >{{ $t('shortcuts.show_kbd_hints') }}</span >
80- </li >
81- <li class =" flex gap-2 items-center" >
82- <kbd class =" kbd" >,</kbd >
83- <span >{{ $t('shortcuts.settings') }}</span >
84- </li >
85- <li class =" flex gap-2 items-center" >
86- <kbd class =" kbd" >c</kbd >
87- <span >{{ $t('shortcuts.compare') }}</span >
88- </li >
89- </ul >
90- <p class =" mb-2 font-mono text-fg-subtle" >
91- {{ $t('shortcuts.section.search') }}
92- </p >
93- <ul class =" mb-6 flex flex-col gap-2" >
94- <li class =" flex gap-2 items-center" >
95- <kbd class =" kbd" >↑</kbd >/<kbd class =" kbd" >↓</kbd >
96- <span >{{ $t('shortcuts.navigate_results') }}</span >
97- </li >
98- <li class =" flex gap-2 items-center" >
99- <kbd class =" kbd" >Enter</kbd >
100- <span >{{ $t('shortcuts.go_to_result') }}</span >
101- </li >
102- </ul >
103- <p class =" mb-2 font-mono text-fg-subtle" >
104- {{ $t('shortcuts.section.package') }}
105- </p >
106- <ul class =" mb-8 flex flex-col gap-2" >
107- <li class =" flex gap-2 items-center" >
108- <kbd class =" kbd" >m</kbd >
109- <span >{{ $t('shortcuts.open_main') }}</span >
110- </li >
111- <li class =" flex gap-2 items-center" >
112- <kbd class =" kbd" >d</kbd >
113- <span >{{ $t('shortcuts.open_docs') }}</span >
114- </li >
115- <li class =" flex gap-2 items-center" >
116- <kbd class =" kbd" >.</kbd >
117- <span >{{ $t('shortcuts.open_code_view') }}</span >
118- </li >
119- <li class =" flex gap-2 items-center" >
120- <kbd class =" kbd" >f</kbd >
121- <span >{{ $t('shortcuts.open_diff') }}</span >
122- </li >
123- <li class =" flex gap-2 items-center" >
124- <kbd class =" kbd" >t</kbd >
125- <span >{{ $t('shortcuts.open_timeline') }}</span >
126- </li >
127- <li class =" flex gap-2 items-center" >
128- <kbd class =" kbd" >c</kbd >
129- <span >{{ $t('shortcuts.compare_from_package') }}</span >
130- </li >
131- </ul >
132- <p class =" text-fg-muted leading-relaxed" >
133- <i18n-t keypath =" shortcuts.disable_shortcuts" tag =" span" scope =" global" >
134- <template #settings >
135- <NuxtLink
136- :to =" { name: 'settings' }"
137- class =" hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg"
138- @click =" closeModal"
139- >
140- {{ $t('settings.title') }}
141- </NuxtLink >
142- </template >
143- </i18n-t >
144- </p >
145- </Modal >
111+ <component :class =" link.icon" class =" size-7" />
112+ </NuxtLink >
146113 </div >
147- <div class =" flex items-center gap-5" >
148- <LinkBase :to =" NPMX_DOCS_SITE" >
149- {{ $t('footer.docs') }}
150- </LinkBase >
151- <LinkBase to =" https://repo.npmx.dev" >
152- {{ $t('footer.source') }}
153- </LinkBase >
154- <LinkBase to =" https://social.npmx.dev" >
155- {{ $t('footer.social') }}
156- </LinkBase >
157- <LinkBase :to =" discord.url" >
158- {{ discord.label }}
159- </LinkBase >
114+ </div >
115+
116+ <div class =" font-mono flex gap-6" >
117+ <div
118+ v-for =" section in footerSections"
119+ :key =" section.label"
120+ class =" flex flex-col gap-3 min-w-40 max-w-50"
121+ >
122+ <p class =" uppercase text-fg-muted" >
123+ {{ section.label }}
124+ </p >
125+ <template v-for =" link in section .links " :key =" link .name " >
126+ <button
127+ v-if =" link.type === 'button'"
128+ class =" cursor-pointer text-start font-mono text-fg-subtle text-sm hover:text-accent transition-colors duration-200"
129+ @click =" showModal()"
130+ >
131+ {{ $t(link.name) }}
132+ </button >
133+
134+ <LinkBase v-else :key =" link.name" :to =" link?.href" variant =" footer" >
135+ {{ $t(link.name) }}
136+ </LinkBase >
137+ </template >
160138 </div >
161139 </div >
162140 </div >
163- <small class =" text-xs text-fg-muted text-center sm:text-start m-0" >
164- <span class =" sm:hidden" >{{ $t('non_affiliation_disclaimer') }}</span >
165- <span class =" hidden sm:inline" >{{ $t('trademark_disclaimer') }}</span >
141+
142+ <small
143+ class =" border-border py-7.75 md:border-t md:py-4 duration-200 transition-all text-xs text-fg-muted text-center md:text-start m-0"
144+ >
145+ <span class =" lg:hidden" >{{ $t('non_affiliation_disclaimer') }}</span >
146+ <span class =" hidden lg:block" >{{ $t('trademark_disclaimer') }}</span >
166147 </small >
167148 </div >
149+
150+ <Modal
151+ id =" keyboard-shortcuts-modal"
152+ ref =" modalRef"
153+ :modalTitle =" $t('footer.keyboard_shortcuts')"
154+ class =" w-auto max-w-lg"
155+ >
156+ <p class =" mb-4 text-sm leading-relaxed text-fg-muted" >
157+ {{
158+ $t('shortcuts.command_palette_description', {
159+ ctrlKey: $t('shortcuts.ctrl_key'),
160+ })
161+ }}
162+ </p >
163+ <p class =" mb-2 font-mono text-fg-subtle" >
164+ {{ $t('shortcuts.section.global') }}
165+ </p >
166+ <ul class =" mb-6 flex flex-col gap-2" >
167+ <li class =" flex gap-2 items-center" >
168+ <kbd class =" kbd" >{{ commandPaletteShortcutLabel }}</kbd >
169+ <span >{{ $t('shortcuts.command_palette') }}</span >
170+ </li >
171+ <li class =" flex gap-2 items-center" >
172+ <kbd class =" kbd" >/</kbd >
173+ <span >{{ $t('shortcuts.focus_search') }}</span >
174+ </li >
175+ <li class =" flex gap-2 items-center" >
176+ <kbd class =" kbd" >?</kbd >
177+ <span >{{ $t('shortcuts.show_kbd_hints') }}</span >
178+ </li >
179+ <li class =" flex gap-2 items-center" >
180+ <kbd class =" kbd" >,</kbd >
181+ <span >{{ $t('shortcuts.settings') }}</span >
182+ </li >
183+ <li class =" flex gap-2 items-center" >
184+ <kbd class =" kbd" >c</kbd >
185+ <span >{{ $t('shortcuts.compare') }}</span >
186+ </li >
187+ </ul >
188+ <p class =" mb-2 font-mono text-fg-subtle" >
189+ {{ $t('shortcuts.section.search') }}
190+ </p >
191+ <ul class =" mb-6 flex flex-col gap-2" >
192+ <li class =" flex gap-2 items-center" >
193+ <kbd class =" kbd" >↑</kbd >/<kbd class =" kbd" >↓</kbd >
194+ <span >{{ $t('shortcuts.navigate_results') }}</span >
195+ </li >
196+ <li class =" flex gap-2 items-center" >
197+ <kbd class =" kbd" >Enter</kbd >
198+ <span >{{ $t('shortcuts.go_to_result') }}</span >
199+ </li >
200+ </ul >
201+ <p class =" mb-2 font-mono text-fg-subtle" >
202+ {{ $t('shortcuts.section.package') }}
203+ </p >
204+ <ul class =" mb-8 flex flex-col gap-2" >
205+ <li class =" flex gap-2 items-center" >
206+ <kbd class =" kbd" >m</kbd >
207+ <span >{{ $t('shortcuts.open_main') }}</span >
208+ </li >
209+ <li class =" flex gap-2 items-center" >
210+ <kbd class =" kbd" >d</kbd >
211+ <span >{{ $t('shortcuts.open_docs') }}</span >
212+ </li >
213+ <li class =" flex gap-2 items-center" >
214+ <kbd class =" kbd" >.</kbd >
215+ <span >{{ $t('shortcuts.open_code_view') }}</span >
216+ </li >
217+ <li class =" flex gap-2 items-center" >
218+ <kbd class =" kbd" >f</kbd >
219+ <span >{{ $t('shortcuts.open_diff') }}</span >
220+ </li >
221+ <li class =" flex gap-2 items-center" >
222+ <kbd class =" kbd" >t</kbd >
223+ <span >{{ $t('shortcuts.open_timeline') }}</span >
224+ </li >
225+ <li class =" flex gap-2 items-center" >
226+ <kbd class =" kbd" >c</kbd >
227+ <span >{{ $t('shortcuts.compare_from_package') }}</span >
228+ </li >
229+ </ul >
230+ <p class =" text-fg-muted leading-relaxed" >
231+ <i18n-t keypath =" shortcuts.disable_shortcuts" tag =" span" scope =" global" >
232+ <template #settings >
233+ <NuxtLink
234+ :to =" { name: 'settings' }"
235+ class =" hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg"
236+ @click =" closeModal"
237+ >
238+ {{ $t('settings.title') }}
239+ </NuxtLink >
240+ </template >
241+ </i18n-t >
242+ </p >
243+ </Modal >
168244 </footer >
169245</template >
170246
171247<style scoped>
172248.kbd {
173- @apply items-center justify-center text-sm text-fg bg-bg-muted border border-border rounded px- 2;
249+ @apply items-center justify-center text-xs text-fg bg-bg-muted border border-border rounded px- 2;
174250}
175251 </style >
0 commit comments