Skip to content

Commit c01e64c

Browse files
committed
feat(footer): redesign with sectioned columns, logo, and social links
1 parent c8fcd11 commit c01e64c

2 files changed

Lines changed: 229 additions & 151 deletions

File tree

app/components/AppFooter.vue

Lines changed: 223 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -9,167 +9,243 @@ const { commandPaletteShortcutLabel } = usePlatformModifierKey()
99
const modalRef = useTemplateRef('modalRef')
1010
const showModal = () => modalRef.value?.showModal?.()
1111
const 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

Comments
 (0)