[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add CTAs to landing page (#678)

authored by

Felix Schneider and committed by
GitHub
1a5622a6 409842e7

+188 -181
+86
app/components/CallToAction.vue
··· 1 + <script setup lang="ts"> 2 + const socialLinks = { 3 + github: 'https://repo.npmx.dev', 4 + discord: 'https://chat.npmx.dev', 5 + bluesky: 'https://social.npmx.dev', 6 + } 7 + </script> 8 + 9 + <template> 10 + <div> 11 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-6"> 12 + {{ $t('about.get_involved.title') }} 13 + </h2> 14 + 15 + <div class="grid gap-4 sm:grid-cols-3"> 16 + <a 17 + :href="socialLinks.github" 18 + target="_blank" 19 + rel="noopener noreferrer" 20 + class="group flex flex-col gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200" 21 + > 22 + <div class="flex gap-2"> 23 + <span class="i-carbon:logo-github shrink-0 mt-1 w-5 h-5 text-fg" aria-hidden="true" /> 24 + <span class="font-medium text-fg"> 25 + {{ $t('about.get_involved.contribute.title') }} 26 + </span> 27 + </div> 28 + <p class="text-sm text-fg-muted leading-relaxed"> 29 + {{ $t('about.get_involved.contribute.description') }} 30 + </p> 31 + <span 32 + class="text-sm text-fg-muted group-hover:text-fg inline-flex items-center gap-1 mt-auto" 33 + > 34 + {{ $t('about.get_involved.contribute.cta') }} 35 + <span class="i-carbon:arrow-right rtl-flip w-3 h-3" aria-hidden="true" /> 36 + </span> 37 + </a> 38 + 39 + <a 40 + :href="socialLinks.discord" 41 + target="_blank" 42 + rel="noopener noreferrer" 43 + class="group flex flex-col gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200" 44 + > 45 + <div class="flex gap-2"> 46 + <span class="i-carbon:chat shrink-0 mt-1 w-5 h-5 text-fg" aria-hidden="true" /> 47 + <span class="font-medium text-fg"> 48 + {{ $t('about.get_involved.community.title') }} 49 + </span> 50 + </div> 51 + <p class="text-sm text-fg-muted leading-relaxed"> 52 + {{ $t('about.get_involved.community.description') }} 53 + </p> 54 + <span 55 + class="text-sm text-fg-muted group-hover:text-fg inline-flex items-center gap-1 mt-auto" 56 + > 57 + {{ $t('about.get_involved.community.cta') }} 58 + <span class="i-carbon:arrow-right rtl-flip w-3 h-3" aria-hidden="true" /> 59 + </span> 60 + </a> 61 + 62 + <a 63 + :href="socialLinks.bluesky" 64 + target="_blank" 65 + rel="noopener noreferrer" 66 + class="group flex flex-col gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200" 67 + > 68 + <div class="flex gap-2"> 69 + <span class="i-simple-icons:bluesky shrink-0 mt-1 w-5 h-5 text-fg" aria-hidden="true" /> 70 + <span class="font-medium text-fg"> 71 + {{ $t('about.get_involved.follow.title') }} 72 + </span> 73 + </div> 74 + <p class="text-sm text-fg-muted leading-relaxed"> 75 + {{ $t('about.get_involved.follow.description') }} 76 + </p> 77 + <span 78 + class="text-sm text-fg-muted group-hover:text-fg inline-flex items-center gap-1 mt-auto" 79 + > 80 + {{ $t('about.get_involved.follow.cta') }} 81 + <span class="i-carbon:arrow-right rtl-flip w-3 h-3" aria-hidden="true" /> 82 + </span> 83 + </a> 84 + </div> 85 + </div> 86 + </template>
+1 -90
app/pages/about.vue
··· 27 27 vlt: 'https://www.vlt.sh/', 28 28 } 29 29 30 - const socialLinks = { 31 - github: 'https://repo.npmx.dev', 32 - discord: 'https://chat.npmx.dev', 33 - bluesky: 'https://social.npmx.dev', 34 - } 35 - 36 30 const { data: contributors, status: contributorsStatus } = useFetch<GitHubContributor[]>( 37 31 '/api/contributors', 38 32 { ··· 210 204 </div> 211 205 </div> 212 206 213 - <!-- Get Involved CTAs --> 214 - <div> 215 - <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-6"> 216 - {{ $t('about.get_involved.title') }} 217 - </h2> 218 - 219 - <div class="grid gap-4 sm:grid-cols-3"> 220 - <!-- Contribute CTA --> 221 - <a 222 - :href="socialLinks.github" 223 - target="_blank" 224 - rel="noopener noreferrer" 225 - class="group flex flex-col gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200" 226 - > 227 - <div class="flex gap-2"> 228 - <span 229 - class="i-carbon:logo-github shrink-0 mt-1 w-5 h-5 text-fg" 230 - aria-hidden="true" 231 - /> 232 - <span class="font-medium text-fg">{{ 233 - $t('about.get_involved.contribute.title') 234 - }}</span> 235 - </div> 236 - <p class="text-sm text-fg-muted leading-relaxed"> 237 - {{ $t('about.get_involved.contribute.description') }} 238 - </p> 239 - <span 240 - class="text-sm text-fg-muted group-hover:text-fg inline-flex items-center gap-1 mt-auto" 241 - > 242 - {{ $t('about.get_involved.contribute.cta') }} 243 - <span class="i-carbon:arrow-right rtl-flip w-3 h-3" aria-hidden="true" /> 244 - </span> 245 - </a> 246 - 247 - <!-- Community CTA --> 248 - <a 249 - :href="socialLinks.discord" 250 - target="_blank" 251 - rel="noopener noreferrer" 252 - class="group flex flex-col gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200" 253 - > 254 - <div class="flex gap-2"> 255 - <span class="i-carbon:chat shrink-0 mt-1 w-5 h-5 text-fg" aria-hidden="true" /> 256 - <span class="font-medium text-fg">{{ 257 - $t('about.get_involved.community.title') 258 - }}</span> 259 - </div> 260 - <p class="text-sm text-fg-muted leading-relaxed"> 261 - {{ $t('about.get_involved.community.description') }} 262 - </p> 263 - <span 264 - class="text-sm text-fg-muted group-hover:text-fg inline-flex items-center gap-1 mt-auto" 265 - > 266 - {{ $t('about.get_involved.community.cta') }} 267 - <span class="i-carbon:arrow-right rtl-flip w-3 h-3" aria-hidden="true" /> 268 - </span> 269 - </a> 270 - 271 - <!-- Follow CTA --> 272 - <a 273 - :href="socialLinks.bluesky" 274 - target="_blank" 275 - rel="noopener noreferrer" 276 - class="group flex flex-col gap-3 p-4 rounded-lg bg-bg-subtle hover:bg-bg-elevated border border-border hover:border-border-hover transition-all duration-200" 277 - > 278 - <div class="flex gap-2"> 279 - <span 280 - class="i-simple-icons:bluesky shrink-0 mt-1 w-5 h-5 text-fg" 281 - aria-hidden="true" 282 - /> 283 - <span class="font-medium text-fg">{{ $t('about.get_involved.follow.title') }}</span> 284 - </div> 285 - <p class="text-sm text-fg-muted leading-relaxed"> 286 - {{ $t('about.get_involved.follow.description') }} 287 - </p> 288 - <span 289 - class="text-sm text-fg-muted group-hover:text-fg inline-flex items-center gap-1 mt-auto" 290 - > 291 - {{ $t('about.get_involved.follow.cta') }} 292 - <span class="i-carbon:arrow-right rtl-flip w-3 h-3" aria-hidden="true" /> 293 - </span> 294 - </a> 295 - </div> 296 - </div> 207 + <CallToAction /> 297 208 </section> 298 209 299 210 <footer class="mt-16 pt-8 border-t border-border">
+92 -91
app/pages/index.vue
··· 34 34 </script> 35 35 36 36 <template> 37 - <main class="container min-h-screen flex flex-col"> 38 - <!-- Hero section with vertical centering --> 39 - <header class="flex-1 flex flex-col items-center justify-center text-center py-20"> 40 - <!-- Animated title --> 41 - <h1 42 - dir="ltr" 43 - 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" 44 - > 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 - /> 53 - <span class="pb-4">npmx</span> 54 - </h1> 37 + <main> 38 + <section class="container min-h-screen flex flex-col"> 39 + <header class="flex-1 flex flex-col items-center justify-center text-center py-20"> 40 + <h1 41 + dir="ltr" 42 + 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" 43 + > 44 + <img 45 + aria-hidden="true" 46 + :alt="$t('alt_logo')" 47 + src="/logo.svg" 48 + width="48" 49 + height="48" 50 + class="w-12 h-12 sm:w-20 sm:h-20 md:w-24 md:h-24 rounded-2xl sm:rounded-3xl" 51 + /> 52 + <span class="pb-4">npmx</span> 53 + </h1> 55 54 56 - <p 57 - class="text-fg-muted text-lg sm:text-xl max-w-md mb-12 motion-safe:animate-slide-up motion-safe:animate-fill-both" 58 - style="animation-delay: 0.1s" 59 - > 60 - {{ $t('tagline') }} 61 - </p> 55 + <p 56 + class="text-fg-muted text-lg sm:text-xl max-w-md mb-12 motion-safe:animate-slide-up motion-safe:animate-fill-both" 57 + style="animation-delay: 0.1s" 58 + > 59 + {{ $t('tagline') }} 60 + </p> 62 61 63 - <!-- Search form with micro-interactions --> 64 - <search 65 - class="w-full max-w-xl motion-safe:animate-slide-up motion-safe:animate-fill-both" 66 - style="animation-delay: 0.2s" 67 - > 68 - <form method="GET" action="/search" class="relative" @submit.prevent.trim="search"> 69 - <label for="home-search" class="sr-only"> 70 - {{ $t('search.label') }} 71 - </label> 62 + <search 63 + class="w-full max-w-xl motion-safe:animate-slide-up motion-safe:animate-fill-both" 64 + style="animation-delay: 0.2s" 65 + > 66 + <form method="GET" action="/search" class="relative" @submit.prevent.trim="search"> 67 + <label for="home-search" class="sr-only"> 68 + {{ $t('search.label') }} 69 + </label> 72 70 73 - <!-- Search input with glow effect on focus --> 74 - <div class="relative group" :class="{ 'is-focused': isSearchFocused }"> 75 - <!-- Subtle glow effect --> 76 - <div 77 - class="absolute -inset-px rounded-lg bg-gradient-to-r from-fg/0 via-fg/5 to-fg/0 opacity-0 transition-opacity duration-500 blur-sm group-[.is-focused]:opacity-100" 78 - /> 71 + <div class="relative group" :class="{ 'is-focused': isSearchFocused }"> 72 + <div 73 + class="absolute -inset-px rounded-lg bg-gradient-to-r from-fg/0 via-fg/5 to-fg/0 opacity-0 transition-opacity duration-500 blur-sm group-[.is-focused]:opacity-100" 74 + /> 79 75 80 - <div class="search-box relative flex items-center"> 81 - <span 82 - class="absolute inset-is-4 text-fg-subtle font-mono text-sm pointer-events-none transition-colors duration-200 group-focus-within:text-accent z-1" 83 - > 84 - / 85 - </span> 76 + <div class="search-box relative flex items-center"> 77 + <span 78 + class="absolute inset-is-4 text-fg-subtle font-mono text-sm pointer-events-none transition-colors duration-200 group-focus-within:text-accent z-1" 79 + > 80 + / 81 + </span> 86 82 87 - <input 88 - id="home-search" 89 - ref="searchInputRef" 90 - v-model="searchQuery" 91 - type="search" 92 - name="q" 93 - autofocus 94 - :placeholder="$t('search.placeholder')" 95 - v-bind="noCorrect" 96 - class="w-full bg-bg-subtle border border-border rounded-lg ps-8 pe-24 py-4 font-mono text-base text-fg placeholder:text-fg-subtle transition-border-color duration-300 focus:border-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50" 97 - @input="handleInput" 98 - /> 83 + <input 84 + id="home-search" 85 + ref="searchInputRef" 86 + v-model="searchQuery" 87 + type="search" 88 + name="q" 89 + autofocus 90 + :placeholder="$t('search.placeholder')" 91 + v-bind="noCorrect" 92 + class="w-full bg-bg-subtle border border-border rounded-lg ps-8 pe-24 py-4 font-mono text-base text-fg placeholder:text-fg-subtle transition-border-color duration-300 focus:border-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50" 93 + @input="handleInput" 94 + /> 99 95 100 - <button 101 - type="submit" 102 - class="absolute inset-ie-2 px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-[background-color,transform] duration-200 hover:bg-fg/90 active:scale-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50" 103 - > 104 - {{ $t('search.button') }} 105 - </button> 96 + <button 97 + type="submit" 98 + class="absolute inset-ie-2 px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-[background-color,transform] duration-200 hover:bg-fg/90 active:scale-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50" 99 + > 100 + {{ $t('search.button') }} 101 + </button> 102 + </div> 106 103 </div> 107 - </div> 108 - </form> 109 - </search> 104 + </form> 105 + </search> 110 106 111 - <!-- Build info badge --> 112 - <BuildEnvironment class="mt-4" /> 113 - </header> 107 + <BuildEnvironment class="mt-4" /> 108 + </header> 114 109 115 - <!-- Popular packages --> 116 - <nav 117 - :aria-label="$t('nav.popular_packages')" 118 - class="pt-4 pb-36 sm:pb-40 text-center motion-safe:animate-fade-in motion-safe:animate-fill-both" 119 - style="animation-delay: 0.3s" 120 - > 121 - <ul class="flex flex-wrap items-center justify-center gap-x-6 gap-y-3 list-none m-0 p-0"> 122 - <li 123 - v-for="pkg in ['nuxt', 'vue', 'react', 'svelte', 'vite', 'next', 'astro', 'typescript']" 124 - :key="pkg" 125 - > 126 - <NuxtLink 127 - :to="{ name: 'package', params: { package: [pkg] } }" 128 - class="link-subtle font-mono text-sm inline-flex items-center gap-2 group" 110 + <nav 111 + :aria-label="$t('nav.popular_packages')" 112 + class="pt-4 pb-36 sm:pb-40 text-center motion-safe:animate-fade-in motion-safe:animate-fill-both" 113 + style="animation-delay: 0.3s" 114 + > 115 + <ul class="flex flex-wrap items-center justify-center gap-x-6 gap-y-3 list-none m-0 p-0"> 116 + <li 117 + v-for="pkg in ['nuxt', 'vue', 'react', 'svelte', 'vite', 'next', 'astro', 'typescript']" 118 + :key="pkg" 129 119 > 130 - <span 131 - class="w-1 h-1 rounded-full bg-accent group-hover:bg-fg transition-colors duration-200" 132 - /> 133 - {{ pkg }} 134 - </NuxtLink> 135 - </li> 136 - </ul> 137 - </nav> 120 + <NuxtLink 121 + :to="{ name: 'package', params: { package: [pkg] } }" 122 + class="link-subtle font-mono text-sm inline-flex items-center gap-2 group" 123 + > 124 + <span 125 + class="w-1 h-1 rounded-full bg-accent group-hover:bg-fg transition-colors duration-200" 126 + /> 127 + {{ pkg }} 128 + </NuxtLink> 129 + </li> 130 + </ul> 131 + </nav> 132 + </section> 133 + 134 + <section class="border-t border-border py-24 bg-bg-subtle/10"> 135 + <div class="container max-w-3xl mx-auto"> 136 + <CallToAction /> 137 + </div> 138 + </section> 138 139 </main> 139 140 </template>
+9
test/nuxt/a11y.spec.ts
··· 58 58 AppFooter, 59 59 AppHeader, 60 60 BuildEnvironment, 61 + CallToAction, 61 62 CodeDirectoryListing, 62 63 CodeFileTree, 63 64 CodeMobileTreeDrawer, ··· 1444 1445 const component = await mountSuspended(BuildEnvironment, { 1445 1446 props: { footer: true }, 1446 1447 }) 1448 + const results = await runAxe(component) 1449 + expect(results.violations).toEqual([]) 1450 + }) 1451 + }) 1452 + 1453 + describe('CallToAction', () => { 1454 + it('should have no accessibility violations', async () => { 1455 + const component = await mountSuspended(CallToAction) 1447 1456 const results = await runAxe(component) 1448 1457 expect(results.violations).toEqual([]) 1449 1458 })