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

feat(i18n,ui): add accessibility statement page (#1096)

Co-authored-by: Daniel Roe <daniel@roe.dev>

authored by

Joaquín Sánchez
Daniel Roe
and committed by
GitHub
5ff2b17f 78e01880

+337
+3
app/components/AppFooter.vue
··· 23 23 <LinkBase :to="{ name: 'privacy' }"> 24 24 {{ $t('privacy_policy.title') }} 25 25 </LinkBase> 26 + <LinkBase :to="{ name: 'accessibility' }"> 27 + {{ $t('a11y.footer_title') }} 28 + </LinkBase> 26 29 <button 27 30 type="button" 28 31 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"
+8
app/components/AppHeader.vue
··· 64 64 external: false, 65 65 iconClass: 'i-carbon:security', 66 66 }, 67 + { 68 + name: 'Accessibility', 69 + label: $t('a11y.title'), 70 + to: { name: 'accessibility' }, 71 + type: 'link', 72 + external: false, 73 + iconClass: 'i-carbon:accessibility-alt', 74 + }, 67 75 ], 68 76 }, 69 77 {
+144
app/pages/accessibility.vue
··· 1 + <script setup lang="ts"> 2 + definePageMeta({ 3 + name: 'accessibility', 4 + }) 5 + 6 + useSeoMeta({ 7 + title: () => `${$t('a11y.title')} - npmx`, 8 + description: () => $t('a11y.welcome', { app: 'npmx' }), 9 + }) 10 + 11 + defineOgImageComponent('Default', { 12 + title: () => $t('a11y.title'), 13 + description: () => $t('a11y.welcome', { app: 'npmx' }), 14 + }) 15 + 16 + const router = useRouter() 17 + const canGoBack = useCanGoBack() 18 + </script> 19 + 20 + <template> 21 + <main class="container flex-1 py-12 sm:py-16 overflow-x-hidden"> 22 + <article class="max-w-2xl mx-auto"> 23 + <header class="mb-12"> 24 + <div class="flex items-baseline justify-between gap-4 mb-4"> 25 + <h1 class="font-mono text-3xl sm:text-4xl font-medium"> 26 + {{ $t('a11y.title') }} 27 + </h1> 28 + <button 29 + type="button" 30 + 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" 31 + @click="router.back()" 32 + v-if="canGoBack" 33 + > 34 + <span class="i-carbon:arrow-left rtl-flip w-4 h-4" aria-hidden="true" /> 35 + <span class="sr-only sm:not-sr-only">{{ $t('nav.back') }}</span> 36 + </button> 37 + </div> 38 + </header> 39 + 40 + <section class="prose prose-invert max-w-none space-y-8"> 41 + <p class="text-fg-muted leading-relaxed"> 42 + <i18n-t keypath="a11y.welcome" tag="span" scope="global"> 43 + <template #app> 44 + <strong class="text-fg">npmx</strong> 45 + </template> 46 + </i18n-t> 47 + </p> 48 + 49 + <!-- Our Approach --> 50 + <div> 51 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 52 + {{ $t('a11y.approach.title') }} 53 + </h2> 54 + <p class="text-fg-muted leading-relaxed mb-4"> 55 + {{ $t('a11y.approach.p1') }} 56 + </p> 57 + <p class="text-fg-muted leading-relaxed"> 58 + <i18n-t keypath="a11y.approach.p2" tag="span" scope="global"> 59 + <template #about> 60 + <NuxtLink 61 + :to="{ name: 'about' }" 62 + class="text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg" 63 + > 64 + {{ $t('a11y.approach.about_link') }} 65 + </NuxtLink> 66 + </template> 67 + </i18n-t> 68 + </p> 69 + </div> 70 + 71 + <!-- What We Do --> 72 + <div> 73 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 74 + {{ $t('a11y.measures.title') }} 75 + </h2> 76 + <p class="text-fg-muted leading-relaxed mb-4"> 77 + {{ $t('a11y.measures.p1') }} 78 + </p> 79 + <ul class="space-y-3 text-fg-muted list-none p-0"> 80 + <li class="flex items-start gap-3"> 81 + <span class="text-fg-subtle shrink-0">&mdash;</span> 82 + <span>{{ $t('a11y.measures.li1') }}</span> 83 + </li> 84 + <li class="flex items-start gap-3"> 85 + <span class="text-fg-subtle shrink-0">&mdash;</span> 86 + <span>{{ $t('a11y.measures.li2') }}</span> 87 + </li> 88 + <li class="flex items-start gap-3"> 89 + <span class="text-fg-subtle shrink-0">&mdash;</span> 90 + <span>{{ $t('a11y.measures.li3') }}</span> 91 + </li> 92 + <li class="flex items-start gap-3"> 93 + <span class="text-fg-subtle shrink-0">&mdash;</span> 94 + <span>{{ $t('a11y.measures.li4') }}</span> 95 + </li> 96 + <li class="flex items-start gap-3"> 97 + <span class="text-fg-subtle shrink-0">&mdash;</span> 98 + <span>{{ $t('a11y.measures.li5') }}</span> 99 + </li> 100 + <li class="flex items-start gap-3"> 101 + <span class="text-fg-subtle shrink-0">&mdash;</span> 102 + <span>{{ $t('a11y.measures.li6') }}</span> 103 + </li> 104 + </ul> 105 + </div> 106 + 107 + <!-- Known Limitations --> 108 + <div> 109 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 110 + {{ $t('a11y.limitations.title') }} 111 + </h2> 112 + <p class="text-fg-muted leading-relaxed"> 113 + {{ $t('a11y.limitations.p1') }} 114 + </p> 115 + </div> 116 + 117 + <!-- Feedback --> 118 + <div> 119 + <h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4"> 120 + {{ $t('a11y.contact.title') }} 121 + </h2> 122 + <p class="text-fg-muted leading-relaxed"> 123 + <i18n-t keypath="a11y.contact.p1" tag="span" scope="global"> 124 + <template #app> 125 + <strong class="text-fg">npmx</strong> 126 + </template> 127 + <template #link> 128 + <a 129 + href="https://github.com/npmx-dev/npmx.dev/issues" 130 + target="_blank" 131 + rel="noopener noreferrer" 132 + class="inline-flex items-center gap-1 text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg" 133 + > 134 + {{ $t('a11y.contact.link') }} 135 + <span class="i-carbon:launch rtl-flip w-4 h-4" aria-hidden="true" /> 136 + </a> 137 + </template> 138 + </i18n-t> 139 + </p> 140 + </div> 141 + </section> 142 + </article> 143 + </main> 144 + </template>
+30
i18n/locales/en.json
··· 1065 1065 "title": "Changes to this policy", 1066 1066 "p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date." 1067 1067 } 1068 + }, 1069 + "a11y": { 1070 + "title": "accessibility", 1071 + "footer_title": "a11y", 1072 + "welcome": "We want {app} to be usable by as many people as possible.", 1073 + "approach": { 1074 + "title": "Our approach", 1075 + "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.", 1076 + "p2": "This site is an {about}. Accessibility improvements are made incrementally as part of our regular development.", 1077 + "about_link": "open-source, community-driven project" 1078 + }, 1079 + "measures": { 1080 + "title": "What we do", 1081 + "p1": "Some of the things we aim to do across the site:", 1082 + "li1": "Use semantic HTML and ARIA attributes where appropriate.", 1083 + "li2": "Use relative text sizes so you can adjust them in your browser.", 1084 + "li3": "Support keyboard navigation throughout the interface.", 1085 + "li4": "Respect the prefers-reduced-motion and prefers-color-scheme media queries.", 1086 + "li5": "Design with sufficient color contrast in mind.", 1087 + "li6": "Ensure essential content is available without JavaScript, though some interactive features require it." 1088 + }, 1089 + "limitations": { 1090 + "title": "Known limitations", 1091 + "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." 1092 + }, 1093 + "contact": { 1094 + "title": "Feedback", 1095 + "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.", 1096 + "link": "GitHub repository" 1097 + } 1068 1098 } 1069 1099 }
+90
i18n/schema.json
··· 3202 3202 }, 3203 3203 "additionalProperties": false 3204 3204 }, 3205 + "a11y": { 3206 + "type": "object", 3207 + "properties": { 3208 + "title": { 3209 + "type": "string" 3210 + }, 3211 + "footer_title": { 3212 + "type": "string" 3213 + }, 3214 + "welcome": { 3215 + "type": "string" 3216 + }, 3217 + "approach": { 3218 + "type": "object", 3219 + "properties": { 3220 + "title": { 3221 + "type": "string" 3222 + }, 3223 + "p1": { 3224 + "type": "string" 3225 + }, 3226 + "p2": { 3227 + "type": "string" 3228 + }, 3229 + "about_link": { 3230 + "type": "string" 3231 + } 3232 + }, 3233 + "additionalProperties": false 3234 + }, 3235 + "measures": { 3236 + "type": "object", 3237 + "properties": { 3238 + "title": { 3239 + "type": "string" 3240 + }, 3241 + "p1": { 3242 + "type": "string" 3243 + }, 3244 + "li1": { 3245 + "type": "string" 3246 + }, 3247 + "li2": { 3248 + "type": "string" 3249 + }, 3250 + "li3": { 3251 + "type": "string" 3252 + }, 3253 + "li4": { 3254 + "type": "string" 3255 + }, 3256 + "li5": { 3257 + "type": "string" 3258 + }, 3259 + "li6": { 3260 + "type": "string" 3261 + } 3262 + }, 3263 + "additionalProperties": false 3264 + }, 3265 + "limitations": { 3266 + "type": "object", 3267 + "properties": { 3268 + "title": { 3269 + "type": "string" 3270 + }, 3271 + "p1": { 3272 + "type": "string" 3273 + } 3274 + }, 3275 + "additionalProperties": false 3276 + }, 3277 + "contact": { 3278 + "type": "object", 3279 + "properties": { 3280 + "title": { 3281 + "type": "string" 3282 + }, 3283 + "p1": { 3284 + "type": "string" 3285 + }, 3286 + "link": { 3287 + "type": "string" 3288 + } 3289 + }, 3290 + "additionalProperties": false 3291 + } 3292 + }, 3293 + "additionalProperties": false 3294 + }, 3205 3295 "$schema": { 3206 3296 "type": "string" 3207 3297 }
+30
lunaria/files/en-GB.json
··· 1064 1064 "title": "Changes to this policy", 1065 1065 "p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date." 1066 1066 } 1067 + }, 1068 + "a11y": { 1069 + "title": "accessibility", 1070 + "footer_title": "a11y", 1071 + "welcome": "We want {app} to be usable by as many people as possible.", 1072 + "approach": { 1073 + "title": "Our approach", 1074 + "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.", 1075 + "p2": "This site is an {about}. Accessibility improvements are made incrementally as part of our regular development.", 1076 + "about_link": "open-source, community-driven project" 1077 + }, 1078 + "measures": { 1079 + "title": "What we do", 1080 + "p1": "Some of the things we aim to do across the site:", 1081 + "li1": "Use semantic HTML and ARIA attributes where appropriate.", 1082 + "li2": "Use relative text sizes so you can adjust them in your browser.", 1083 + "li3": "Support keyboard navigation throughout the interface.", 1084 + "li4": "Respect the prefers-reduced-motion and prefers-color-scheme media queries.", 1085 + "li5": "Design with sufficient color contrast in mind.", 1086 + "li6": "Ensure essential content is available without JavaScript, though some interactive features require it." 1087 + }, 1088 + "limitations": { 1089 + "title": "Known limitations", 1090 + "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." 1091 + }, 1092 + "contact": { 1093 + "title": "Feedback", 1094 + "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.", 1095 + "link": "GitHub repository" 1096 + } 1067 1097 } 1068 1098 }
+30
lunaria/files/en-US.json
··· 1064 1064 "title": "Changes to this policy", 1065 1065 "p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date." 1066 1066 } 1067 + }, 1068 + "a11y": { 1069 + "title": "accessibility", 1070 + "footer_title": "a11y", 1071 + "welcome": "We want {app} to be usable by as many people as possible.", 1072 + "approach": { 1073 + "title": "Our approach", 1074 + "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.", 1075 + "p2": "This site is an {about}. Accessibility improvements are made incrementally as part of our regular development.", 1076 + "about_link": "open-source, community-driven project" 1077 + }, 1078 + "measures": { 1079 + "title": "What we do", 1080 + "p1": "Some of the things we aim to do across the site:", 1081 + "li1": "Use semantic HTML and ARIA attributes where appropriate.", 1082 + "li2": "Use relative text sizes so you can adjust them in your browser.", 1083 + "li3": "Support keyboard navigation throughout the interface.", 1084 + "li4": "Respect the prefers-reduced-motion and prefers-color-scheme media queries.", 1085 + "li5": "Design with sufficient color contrast in mind.", 1086 + "li6": "Ensure essential content is available without JavaScript, though some interactive features require it." 1087 + }, 1088 + "limitations": { 1089 + "title": "Known limitations", 1090 + "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." 1091 + }, 1092 + "contact": { 1093 + "title": "Feedback", 1094 + "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.", 1095 + "link": "GitHub repository" 1096 + } 1067 1097 } 1068 1098 }
+1
nuxt.config.ts
··· 138 138 '/': { prerender: true }, 139 139 '/200.html': { prerender: true }, 140 140 '/about': { prerender: true }, 141 + '/accessibility': { prerender: true }, 141 142 '/privacy': { prerender: true }, 142 143 '/search': { isr: false, cache: false }, // never cache 143 144 '/settings': { prerender: true },
+1
server/middleware/canonical-redirects.global.ts
··· 16 16 '/oauth-client-metadata.json', 17 17 '/200.html', 18 18 '/about', 19 + '/accessibility', 19 20 '/compare', 20 21 '/org', 21 22 '/package',