Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/components/AppFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ const footerSections = computed<Array<{ label: string; links: FooterLink[] }>>((
name: t('footer.about'),
href: '/about',
},
{
name: t('footer.sponsors'),
href: '/sponsors',
},
{
name: t('footer.brand'),
href: '/brand',
Expand Down
10 changes: 10 additions & 0 deletions app/composables/useCommandPaletteGlobalCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,16 @@ export function useCommandPaletteGlobalCommands() {
),
to: { name: 'noodles' },
},
{
id: 'sponsors',
group: 'npmx',
label: t('sponsors_page.title'),
keywords: [t('sponsors_page.title')],
iconClass: 'i-lucide:heart',
active: route.name === 'sponsors',
activeLabel: activeLabel(route.name === 'sponsors', t('command_palette.here')),
to: { name: 'sponsors' },
},
{
id: 'brand',
group: 'npmx',
Expand Down
5 changes: 5 additions & 0 deletions app/pages/about.vue
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ const communityContributors = computed(
<h2 class="text-lg text-fg uppercase tracking-wider mb-4">
{{ $t('about.sponsors.title') }}
</h2>
<p class="text-fg-muted text-sm leading-relaxed mb-4">
<LinkBase to="/sponsors" no-new-tab-icon>
{{ $t('sponsors_page.cta') }}
</LinkBase>
</p>
<h3 class="block text-sm text-fg uppercase tracking-wider mb-3">
{{ $t('about.sponsors.gold') }}
</h3>
Expand Down
250 changes: 250 additions & 0 deletions app/pages/sponsors.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
<script setup lang="ts">
import { SPONSORS } from '~/assets/logos/sponsors'

const SPONSORSHIP_TIER_PRICES = {
silver: 500,
gold: 1000,
} as const

const { t } = useI18n()
const currencyFormatter = useNumberFormatter({
style: 'currency',
currency: 'USD',
maximumFractionDigits: 0,
})

function formatTierPrice(amount: number) {
return `${currencyFormatter.value.format(amount)}${t('sponsors_page.tiers.per_month')}`
}

useSeoMeta({
title: () => `${$t('sponsors_page.title')} - npmx`,
ogTitle: () => `${$t('sponsors_page.title')} - npmx`,
twitterTitle: () => `${$t('sponsors_page.title')} - npmx`,
description: () => $t('sponsors_page.meta_description'),
ogDescription: () => $t('sponsors_page.meta_description'),
twitterDescription: () => $t('sponsors_page.meta_description'),
})

defineOgImage(
'Page.takumi',
{
title: () => $t('sponsors_page.title'),
description: () => $t('sponsors_page.meta_description'),
},
{ alt: () => `${$t('sponsors_page.title')} — npmx` },
)
</script>

<template>
<main class="container flex-1 py-12 sm:py-16">
<header class="mb-12">
<div class="flex items-baseline justify-between gap-4 mb-4">
<h1 class="font-mono text-3xl sm:text-4xl font-medium">
{{ $t('sponsors_page.heading') }}
</h1>
<BackButton />
</div>
<p class="text-fg-muted text-lg leading-relaxed">
{{ $t('sponsors_page.intro') }}
</p>
</header>

<div class="space-y-12" :class="$style.grid">
<section :class="$style.intro">
<h2 class="text-lg text-fg uppercase tracking-wider mb-4">
{{ $t('sponsors_page.what_we_do.title') }}
</h2>
<p class="text-fg-muted leading-relaxed">
{{ $t('sponsors_page.what_we_do.description') }}
</p>
</section>

<aside :class="$style.sponsors">
<h2 class="text-lg text-fg uppercase tracking-wider mb-4">
{{ $t('about.sponsors.title') }}
</h2>
<h3 class="block text-sm text-fg uppercase tracking-wider mb-3">
{{ $t('about.sponsors.gold') }}
</h3>
<AboutLogoList
:list="SPONSORS.gold"
class="grid gap-3 grid-cols-[repeat(auto-fill,minmax(160px,1fr))] lg:grid-cols-1"
/>
<h3 class="block text-sm text-fg uppercase tracking-wider mb-3 mt-6">
{{ $t('about.sponsors.silver') }}
</h3>
<AboutLogoList
:list="SPONSORS.silver"
class="grid gap-3 grid-cols-[repeat(auto-fill,minmax(160px,1fr))] lg:grid-cols-1"
/>
</aside>

<section :class="$style.content">
<div>
<h2 class="text-lg text-fg uppercase tracking-wider mb-4">
{{ $t('sponsors_page.what_this_means_for_you.title') }}
</h2>
<p class="text-fg-muted leading-relaxed mb-4">
{{ $t('sponsors_page.what_this_means_for_you.description') }}
</p>
<div class="grid gap-4 sm:grid-cols-12">
<section class="border border-border bg-bg-elevated rounded-lg p-5 sm:col-span-7">
<div class="grid grid-cols-2 gap-4">
<div>
<p class="font-mono text-2xl text-fg uppercase tracking-wider mb-2">250+</p>
<p class="text-fg-muted text-sm leading-relaxed m-0">
{{ $t('sponsors_page.what_this_means_for_you.cards.people.contributors') }}
</p>
</div>
<div>
<p class="font-mono text-2xl text-fg uppercase tracking-wider mb-2">700+</p>
<p class="text-fg-muted text-sm leading-relaxed m-0">
{{ $t('sponsors_page.what_this_means_for_you.cards.people.community_members') }}
</p>
</div>
</div>
</section>
<section class="border border-border rounded-lg p-5 bg-bg-subtle sm:col-span-5">
<p class="font-mono text-2xl text-fg uppercase tracking-wider mb-2">200000+</p>
<p class="text-fg-muted text-sm leading-relaxed m-0">
{{ $t('sponsors_page.what_this_means_for_you.cards.visitors.description') }}
</p>
</section>
<section class="border border-border rounded-lg p-5 bg-bg-subtle sm:col-span-4">
<p class="font-mono text-2xl text-fg uppercase tracking-wider mb-2">3400+</p>
<p class="text-fg-muted text-sm leading-relaxed m-0">
{{ $t('sponsors_page.what_this_means_for_you.cards.stars.title') }}
</p>
</section>
<section class="border border-border rounded-lg p-5 bg-bg-subtle sm:col-span-8">
<p class="font-mono text-2xl text-fg uppercase tracking-wider mb-2">
{{ $t('sponsors_page.what_this_means_for_you.cards.community.title') }}
</p>
<p class="text-fg-muted text-sm leading-relaxed m-0">
{{ $t('sponsors_page.what_this_means_for_you.cards.community.description')
}}<sup>*</sup>
</p>
</section>
<section class="border border-border rounded-lg p-5 bg-bg-subtle sm:col-span-6">
<p class="font-mono text-2xl text-fg uppercase tracking-wider mb-2">
{{ $t('sponsors_page.what_this_means_for_you.cards.adoption.title') }}
</p>
<p class="text-fg-muted text-sm leading-relaxed m-0">
{{ $t('sponsors_page.what_this_means_for_you.cards.adoption.description') }}
</p>
</section>
<section class="border border-border rounded-lg p-5 bg-bg-subtle sm:col-span-6">
<p class="font-mono text-2xl text-fg uppercase tracking-wider mb-2">
{{ $t('sponsors_page.what_this_means_for_you.cards.default_source.title') }}
</p>
<p class="text-fg-muted text-sm leading-relaxed m-0">
{{ $t('sponsors_page.what_this_means_for_you.cards.default_source.description') }}
</p>
</section>
</div>
Comment thread
alexdln marked this conversation as resolved.
</div>

<div class="mt-6">
<h2 class="text-lg text-fg uppercase tracking-wider mb-4">
{{ $t('sponsors_page.what_support_means.title') }}
</h2>
<p class="text-fg-muted leading-relaxed">
{{ $t('sponsors_page.what_support_means.description') }}
</p>
</div>

<div class="mt-6">
<h2 class="text-lg text-fg uppercase tracking-wider mb-4">
{{ $t('sponsors_page.tiers.title') }}
</h2>

<div class="border border-fg/80 rounded-xl p-5 bg-bg-muted">
<div class="flex items-center justify-between gap-3 mb-2">
<h3 class="font-mono text-base text-fg uppercase tracking-wider">
{{ $t('sponsors_page.tiers.silver.name') }}
</h3>
<span class="i-lucide:coins size-4 text-fg-subtle" aria-hidden="true" />
</div>
<p class="text-sm text-fg-subtle font-mono">
{{ formatTierPrice(SPONSORSHIP_TIER_PRICES.silver) }}
</p>
</div>

<div
class="border border-badge-yellow/80 rounded-xl p-5 bg-linear-to-br from-badge-yellow/8 to-bg-subtle/40 relative overflow-hidden mt-4"
>
<div class="flex items-center justify-between gap-3 mb-2 relative">
<h3 class="font-mono text-base text-fg uppercase tracking-wider">
{{ $t('sponsors_page.tiers.gold.name') }}
</h3>
<span class="i-lucide:medal size-4 text-badge-yellow" aria-hidden="true" />
</div>
<p class="text-sm text-fg-subtle font-mono">
{{ formatTierPrice(SPONSORSHIP_TIER_PRICES.gold) }}
</p>
</div>

<div
class="border border-badge-blue/80 rounded-xl p-5 bg-linear-to-br from-badge-blue/8 to-bg-subtle/40 relative overflow-hidden mt-4"
>
<div class="flex items-center justify-between gap-3 mb-2">
<h3 class="font-mono text-base text-fg uppercase tracking-wider">
{{ $t('sponsors_page.tiers.platinum.name') }}
</h3>
<span class="i-lucide:crown size-4 text-badge-blue" aria-hidden="true" />
</div>
<p class="text-sm text-fg-subtle font-mono">
{{ $t('sponsors_page.tiers.custom') }}
</p>
</div>
</div>
</section>
</div>

<p class="text-fg-subtle text-xs leading-relaxed mt-12">
<i18n-t keypath="sponsors_page.community_growth_footnote" tag="span" scope="global">
<template #link>
<LinkBase to="https://osscar.dev/"> OSSCAR </LinkBase>
</template>
</i18n-t>
</p>
</main>
</template>

<style module>
.grid {
display: grid;
column-gap: 2rem;
row-gap: 1rem;
grid-template-columns: minmax(0, 1fr);
grid-template-areas:
'intro'
'sponsors'
'content';
}

@media (min-width: 64rem) {
.grid {
grid-template-columns: 1fr 20rem;
grid-template-areas: 'intro sponsors' 'content sponsors';
}
}

.grid > * {
min-width: 0;
}

.intro {
grid-area: intro;
}

.sponsors {
grid-area: sponsors;
align-self: start;
}

.content {
grid-area: content;
}
</style>
59 changes: 59 additions & 0 deletions i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"trademark_disclaimer": "npm is a registered trademark of npm, Inc. This site is not affiliated with npm, Inc.",
"footer": {
"about": "about",
"sponsors": "sponsors",
"blog": "blog",
"docs": "docs",
"source": "source",
Expand Down Expand Up @@ -1268,6 +1269,64 @@
}
}
},
"sponsors_page": {
"title": "Sponsors",
"heading": "sponsors",
"meta_description": "Support npmx and help us accelerate ecosystem work around security, trust, optimization, and research.",
"intro": "Support npmx and help us grow the ecosystem work we do for developers and maintainers.",
"what_we_do": {
"title": "What we do",
"description": "We're building an ecosystem that solves problems around security, trust, optimization, and research for the tools we use in everyday development - quickly, conveniently, and with high quality. This project is built by developers for developers. As authors of widely used libraries, we understand team needs and actively study what maintainers and projects need most."
},
"what_support_means": {
"title": "What support means",
"description": "Our plans and connections keep growing, along with our desire to share what we've built and learn from others. Your support helps fund the project, external talks, conferences, and the broader ecosystem, and, most importantly, enables us to keep growing our community."
},
"cta": "See sponsorship tiers",
"community_growth_footnote": "* According to {link} Q1 2026 research.",
"what_this_means_for_you": {
"title": "What this means for you",
"description": "npmx is not only about improving developer experience and closing critical everyday gaps. In our first six months, we have already become the default source for thousands of teams and a huge number of developers. You get not only a more stable tool for your teams, but also visibility in front of a constantly growing audience of top-tier engineers.",
"cards": {
"people": {
"contributors": "contributors",
"community_members": "community members"
},
"visitors": {
"description": "unique monthly visitors"
},
"stars": {
"title": "stars"
},
"community": {
"title": "fast-growing",
"description": "We are one of the fastest-growing communities in the ecosystem"
},
"adoption": {
"title": "adoption",
"description": "charts at conferences, talks, and articles rely on our data"
},
"default_source": {
"title": "default source",
"description": "pnpm made npmx the default source, many packages have been configured to link to npmx"
}
}
},
"tiers": {
"title": "Sponsorship tiers",
"per_month": "/month",
"custom": "Custom",
"silver": {
"name": "Silver"
},
"gold": {
"name": "Gold"
},
"platinum": {
"name": "Platinum"
}
}
},
"account_menu": {
"connect": "connect",
"account": "Account",
Expand Down
Loading
Loading