Skip to content
3 changes: 3 additions & 0 deletions app/components/AppFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ const showModal = () => modalRef.value?.showModal?.()
<LinkBase :to="{ name: 'privacy' }">
{{ $t('privacy_policy.title') }}
</LinkBase>
<LinkBase :to="{ name: 'accessibility' }">
{{ $t('a11y.footer_title') }}
</LinkBase>
<button
type="button"
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"
Expand Down
8 changes: 8 additions & 0 deletions app/components/AppHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ const mobileLinks = computed<NavigationConfigWithGroups>(() => [
external: false,
iconClass: 'i-carbon:security',
},
{
name: 'Accessibility',
label: $t('a11y.title'),
to: { name: 'accessibility' },
type: 'link',
external: false,
iconClass: 'i-carbon:accessibility-alt',
},
],
},
{
Expand Down
144 changes: 144 additions & 0 deletions app/pages/accessibility.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<script setup lang="ts">
definePageMeta({
name: 'accessibility',
})
useSeoMeta({
title: () => `${$t('a11y.title')} - npmx`,
description: () => $t('a11y.welcome', { app: 'npmx' }),
})
defineOgImageComponent('Default', {
title: () => $t('a11y.title'),
description: () => $t('a11y.welcome', { app: 'npmx' }),
})
const router = useRouter()
const canGoBack = useCanGoBack()
</script>

<template>
<main class="container flex-1 py-12 sm:py-16 overflow-x-hidden">
<article class="max-w-2xl mx-auto">
<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('a11y.title') }}
</h1>
<button
type="button"
class="cursor-pointer inline-flex items-center gap-2 font-mono text-sm text-fg-muted hover:text-fg transition-colors duration-200 rounded shrink-0"
@click="router.back()"
v-if="canGoBack"
>
<span class="i-carbon:arrow-left rtl-flip w-4 h-4" aria-hidden="true" />
<span class="sr-only sm:not-sr-only">{{ $t('nav.back') }}</span>
</button>
</div>
</header>

<section class="prose prose-invert max-w-none space-y-8">
<p class="text-fg-muted leading-relaxed">
<i18n-t keypath="a11y.welcome" tag="span" scope="global">
<template #app>
<strong class="text-fg">npmx</strong>
</template>
</i18n-t>
</p>

<!-- Our Approach -->
<div>
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
{{ $t('a11y.approach.title') }}
</h2>
<p class="text-fg-muted leading-relaxed mb-4">
{{ $t('a11y.approach.p1') }}
</p>
<p class="text-fg-muted leading-relaxed">
<i18n-t keypath="a11y.approach.p2" tag="span" scope="global">
<template #about>
<NuxtLink
:to="{ name: 'about' }"
class="text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg"
>
{{ $t('a11y.approach.about_link') }}
</NuxtLink>
</template>
</i18n-t>
</p>
</div>

<!-- What We Do -->
<div>
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
{{ $t('a11y.measures.title') }}
</h2>
<p class="text-fg-muted leading-relaxed mb-4">
{{ $t('a11y.measures.p1') }}
</p>
<ul class="space-y-3 text-fg-muted list-none p-0">
<li class="flex items-start gap-3">
<span class="text-fg-subtle shrink-0">&mdash;</span>
<span>{{ $t('a11y.measures.li1') }}</span>
</li>
<li class="flex items-start gap-3">
<span class="text-fg-subtle shrink-0">&mdash;</span>
<span>{{ $t('a11y.measures.li2') }}</span>
</li>
<li class="flex items-start gap-3">
<span class="text-fg-subtle shrink-0">&mdash;</span>
<span>{{ $t('a11y.measures.li3') }}</span>
</li>
<li class="flex items-start gap-3">
<span class="text-fg-subtle shrink-0">&mdash;</span>
<span>{{ $t('a11y.measures.li4') }}</span>
</li>
<li class="flex items-start gap-3">
<span class="text-fg-subtle shrink-0">&mdash;</span>
<span>{{ $t('a11y.measures.li5') }}</span>
</li>
<li class="flex items-start gap-3">
<span class="text-fg-subtle shrink-0">&mdash;</span>
<span>{{ $t('a11y.measures.li6') }}</span>
</li>
</ul>
</div>

<!-- Known Limitations -->
<div>
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
{{ $t('a11y.limitations.title') }}
</h2>
<p class="text-fg-muted leading-relaxed">
{{ $t('a11y.limitations.p1') }}
</p>
</div>

<!-- Feedback -->
<div>
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
{{ $t('a11y.contact.title') }}
</h2>
<p class="text-fg-muted leading-relaxed">
<i18n-t keypath="a11y.contact.p1" tag="span" scope="global">
<template #app>
<strong class="text-fg">npmx</strong>
</template>
<template #link>
<a
href="https://github.com/npmx-dev/npmx.dev/issues"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center gap-1 text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg"
>
{{ $t('a11y.contact.link') }}
<span class="i-carbon:launch rtl-flip w-4 h-4" aria-hidden="true" />
</a>
</template>
</i18n-t>
</p>
</div>
</section>
</article>
</main>
</template>
30 changes: 30 additions & 0 deletions i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1065,5 +1065,35 @@
"title": "Changes to this policy",
"p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date."
}
},
"a11y": {
"title": "accessibility",
"footer_title": "a11y",
"welcome": "We want {app} to be usable by as many people as possible.",
"approach": {
"title": "Our approach",
"p1": "We try to follow the Web Content Accessibility Guidelines (WCAG) 2.2 and use them as a reference when building features. We don't claim full conformance with any level of WCAG — accessibility is a continual process and there is always more work to do.",
"p2": "This site is an {about}. Accessibility improvements are made incrementally as part of our regular development.",
"about_link": "open-source, community-driven project"
},
"measures": {
"title": "What we do",
"p1": "Some of the things we aim to do across the site:",
"li1": "Use semantic HTML and ARIA attributes where appropriate.",
"li2": "Use relative text sizes so you can adjust them in your browser.",
"li3": "Support keyboard navigation throughout the interface.",
"li4": "Respect the prefers-reduced-motion and prefers-color-scheme media queries.",
"li5": "Design with sufficient color contrast in mind.",
"li6": "Ensure essential content is available without JavaScript, though some interactive features require it."
},
"limitations": {
"title": "Known limitations",
"p1": "Some parts of the site — particularly third-party content like package READMEs — may not meet accessibility standards. We are working to improve these areas over time."
},
"contact": {
"title": "Feedback",
"p1": "If you encounter an accessibility barrier on {app}, please let us know by opening an issue on our {link}. We take these reports seriously and will do our best to address them.",
"link": "GitHub repository"
}
}
}
90 changes: 90 additions & 0 deletions i18n/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3202,6 +3202,96 @@
},
"additionalProperties": false
},
"a11y": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"footer_title": {
"type": "string"
},
"welcome": {
"type": "string"
},
"approach": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"p1": {
"type": "string"
},
"p2": {
"type": "string"
},
"about_link": {
"type": "string"
}
},
"additionalProperties": false
},
"measures": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"p1": {
"type": "string"
},
"li1": {
"type": "string"
},
"li2": {
"type": "string"
},
"li3": {
"type": "string"
},
"li4": {
"type": "string"
},
"li5": {
"type": "string"
},
"li6": {
"type": "string"
}
},
"additionalProperties": false
},
"limitations": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"p1": {
"type": "string"
}
},
"additionalProperties": false
},
"contact": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"p1": {
"type": "string"
},
"link": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"$schema": {
"type": "string"
}
Expand Down
30 changes: 30 additions & 0 deletions lunaria/files/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -1064,5 +1064,35 @@
"title": "Changes to this policy",
"p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date."
}
},
"a11y": {
"title": "accessibility",
"footer_title": "a11y",
"welcome": "We want {app} to be usable by as many people as possible.",
"approach": {
"title": "Our approach",
"p1": "We try to follow the Web Content Accessibility Guidelines (WCAG) 2.2 and use them as a reference when building features. We don't claim full conformance with any level of WCAG — accessibility is a continual process and there is always more work to do.",
"p2": "This site is an {about}. Accessibility improvements are made incrementally as part of our regular development.",
"about_link": "open-source, community-driven project"
},
"measures": {
"title": "What we do",
"p1": "Some of the things we aim to do across the site:",
"li1": "Use semantic HTML and ARIA attributes where appropriate.",
"li2": "Use relative text sizes so you can adjust them in your browser.",
"li3": "Support keyboard navigation throughout the interface.",
"li4": "Respect the prefers-reduced-motion and prefers-color-scheme media queries.",
"li5": "Design with sufficient color contrast in mind.",
"li6": "Ensure essential content is available without JavaScript, though some interactive features require it."
},
"limitations": {
"title": "Known limitations",
"p1": "Some parts of the site — particularly third-party content like package READMEs — may not meet accessibility standards. We are working to improve these areas over time."
},
"contact": {
"title": "Feedback",
"p1": "If you encounter an accessibility barrier on {app}, please let us know by opening an issue on our {link}. We take these reports seriously and will do our best to address them.",
"link": "GitHub repository"
}
}
}
30 changes: 30 additions & 0 deletions lunaria/files/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1064,5 +1064,35 @@
"title": "Changes to this policy",
"p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date."
}
},
"a11y": {
"title": "accessibility",
"footer_title": "a11y",
"welcome": "We want {app} to be usable by as many people as possible.",
"approach": {
"title": "Our approach",
"p1": "We try to follow the Web Content Accessibility Guidelines (WCAG) 2.2 and use them as a reference when building features. We don't claim full conformance with any level of WCAG — accessibility is a continual process and there is always more work to do.",
"p2": "This site is an {about}. Accessibility improvements are made incrementally as part of our regular development.",
"about_link": "open-source, community-driven project"
},
"measures": {
"title": "What we do",
"p1": "Some of the things we aim to do across the site:",
"li1": "Use semantic HTML and ARIA attributes where appropriate.",
"li2": "Use relative text sizes so you can adjust them in your browser.",
"li3": "Support keyboard navigation throughout the interface.",
"li4": "Respect the prefers-reduced-motion and prefers-color-scheme media queries.",
"li5": "Design with sufficient color contrast in mind.",
"li6": "Ensure essential content is available without JavaScript, though some interactive features require it."
},
"limitations": {
"title": "Known limitations",
"p1": "Some parts of the site — particularly third-party content like package READMEs — may not meet accessibility standards. We are working to improve these areas over time."
},
"contact": {
"title": "Feedback",
"p1": "If you encounter an accessibility barrier on {app}, please let us know by opening an issue on our {link}. We take these reports seriously and will do our best to address them.",
"link": "GitHub repository"
}
}
}
1 change: 1 addition & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export default defineNuxtConfig({
'/': { prerender: true },
'/200.html': { prerender: true },
'/about': { prerender: true },
'/accessibility': { prerender: true },
'/privacy': { prerender: true },
'/search': { isr: false, cache: false }, // never cache
'/settings': { prerender: true },
Expand Down
1 change: 1 addition & 0 deletions server/middleware/canonical-redirects.global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const pages = [
'/oauth-client-metadata.json',
'/200.html',
'/about',
'/accessibility',
'/compare',
'/org',
'/package',
Expand Down
Loading