Skip to content

Commit e977ff4

Browse files
committed
Merge branch 'main' into chore-add-compare-to-route-rules
# Conflicts: # nuxt.config.ts
2 parents 3e46cc8 + 01ecd65 commit e977ff4

File tree

14 files changed

+409
-21
lines changed

14 files changed

+409
-21
lines changed

.oxfmtignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

app/components/AppFooter.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ const showModal = () => modalRef.value?.showModal?.()
2323
<LinkBase :to="{ name: 'privacy' }">
2424
{{ $t('privacy_policy.title') }}
2525
</LinkBase>
26+
<LinkBase :to="{ name: 'accessibility' }">
27+
{{ $t('a11y.footer_title') }}
28+
</LinkBase>
2629
<button
2730
type="button"
2831
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"

app/components/AppHeader.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ const mobileLinks = computed<NavigationConfigWithGroups>(() => [
6464
external: false,
6565
iconClass: 'i-carbon:security',
6666
},
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+
},
6775
],
6876
},
6977
{

app/components/AppLogo.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ defineProps<{
2424
font-size="420"
2525
font-weight="500"
2626
text-anchor="middle"
27+
style="user-select: none"
2728
>
2829
<tspan>/</tspan>
2930
</text>

app/components/Readme.vue

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ defineProps<{
33
html: string
44
}>()
55
6-
const router = useRouter()
76
const { copy } = useClipboard()
87
98
// Combined click handler for:
@@ -13,6 +12,10 @@ function handleClick(event: MouseEvent) {
1312
const target = event.target as HTMLElement | undefined
1413
if (!target) return
1514
15+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button) {
16+
return
17+
}
18+
1619
// Handle copy button clicks
1720
const copyTarget = target.closest('[data-copy]')
1821
if (copyTarget) {
@@ -48,20 +51,11 @@ function handleClick(event: MouseEvent) {
4851
if (!href) return
4952
5053
// Handle relative anchor links
51-
if (href.startsWith('#')) {
54+
if (href.startsWith('#') || href.startsWith('/')) {
5255
event.preventDefault()
53-
router.push(href)
56+
navigateTo(href)
5457
return
5558
}
56-
57-
const match = href.match(/^(?:https?:\/\/)?(?:www\.)?npmjs\.(?:com|org)(\/.+)$/)
58-
if (!match || !match[1]) return
59-
60-
const route = router.resolve(match[1])
61-
if (route) {
62-
event.preventDefault()
63-
router.push(route)
64-
}
6559
}
6660
</script>
6761

@@ -141,15 +135,19 @@ function handleClick(event: MouseEvent) {
141135
}
142136
143137
.readme :deep(a) {
144-
color: var(--fg);
145-
text-decoration: underline;
146-
text-underline-offset: 4px;
147-
text-decoration-color: var(--fg-subtle);
148-
transition: text-decoration-color 0.2s ease;
138+
@apply underline-offset-[0.2rem] underline decoration-1 decoration-fg/30 font-mono text-fg transition-colors duration-200;
149139
}
150-
151140
.readme :deep(a:hover) {
152-
text-decoration-color: var(--accent);
141+
@apply decoration-accent text-accent;
142+
}
143+
.readme :deep(a:focus-visible) {
144+
@apply decoration-accent text-accent;
145+
}
146+
147+
.readme :deep(a[target='_blank']::after) {
148+
/* I don't know what kind of sorcery this is, but it ensures this icon can't wrap to a new line on its own. */
149+
content: '__';
150+
@apply inline i-carbon:launch rtl-flip ms-1 opacity-50;
153151
}
154152
155153
.readme :deep(code) {

app/pages/accessibility.vue

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
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>

i18n/locales/en.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,5 +1065,35 @@
10651065
"title": "Changes to this policy",
10661066
"p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date."
10671067
}
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+
}
10681098
}
10691099
}

i18n/schema.json

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3202,6 +3202,96 @@
32023202
},
32033203
"additionalProperties": false
32043204
},
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+
},
32053295
"$schema": {
32063296
"type": "string"
32073297
}

0 commit comments

Comments
 (0)