Skip to content

Commit 0ecce4e

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/atproto-oauth
2 parents e267dff + 592e0c5 commit 0ecce4e

107 files changed

Lines changed: 7133 additions & 1777 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/autofix.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ jobs:
4242
- name: 🏃 Update component test snapshots
4343
run: pnpm test:nuxt -u
4444

45-
- name: 🖥️ Update browser test snapshots
46-
run: pnpm test:browser --update-snapshots
45+
# TODO: re-enable when we have snapshots in browser tests
46+
# - name: 🖥️ Update browser test snapshots
47+
# run: pnpm test:browser --update-snapshots
4748

4849
- uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27

.github/workflows/semantic-pull-requests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ jobs:
2323
uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
2424
with:
2525
scopes: |
26+
a11y
27+
deps
2628
docs
2729
i18n
28-
deps
30+
ui
2931
subjectPattern: ^(?![A-Z]).+$
3032
subjectPatternError: |
3133
The subject "{subject}" found in the pull request title "{title}"

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ We've added some `UnoCSS` utilities styles to help you with that:
234234
- Do not use `rtl-` classes, such as `rtl-left-0`.
235235
- For icons that should be rotated for RTL, add `class="rtl-flip"`. This can only be used for icons outside of elements with `dir="auto"`.
236236
- For absolute positioned elements, don't use `left/right`: for example `left-0`. Use `inset-inline-start/end` instead. `UnoCSS` shortcuts are `inset-is` for `inset-inline-start` and `inset-ie` for `inset-inline-end`. Example: `left-0` should be replaced with `inset-is-0`.
237-
- If you need to change the border radius for an entire left or right side, use `border-inline-start/end`. `UnoCSS` shortcuts are `rounded-is` for left side, `rounded-ie` for right side. Example: `rounded-l-5` should be replaced with `rounded-ie-5`.
237+
- If you need to change the border radius for an entire left or right side, use `border-inline-start/end`. `UnoCSS` shortcuts are `rounded-is` for left side, `rounded-ie` for right side. Example: `rounded-l-5` should be replaced with `rounded-is-5`.
238238
- If you need to change the border radius for one corner, use `border-start-end-radius` and similar rules. `UnoCSS` shortcuts are `rounded` + top/bottom as either `-bs` (top) or `-be` (bottom) + left/right as either `-is` (left) or `-ie` (right). Example: `rounded-tl-0` should be replaced with `rounded-bs-is-0`.
239239

240240
## Localization (i18n)
@@ -244,7 +244,7 @@ npmx.dev uses [@nuxtjs/i18n](https://i18n.nuxtjs.org/) for internationalization.
244244
### Approach
245245

246246
- All user-facing strings should use translation keys via `$t()` in templates and script
247-
- Translation files live in `i18n/locales/` (e.g., `en-US.json`)
247+
- Translation files live in [`i18n/locales/`](i18n/locales) (e.g., `en-US.json`)
248248
- We use the `no_prefix` strategy (no `/en-US/` or `/fr-FR/` in URLs)
249249
- Locale preference is stored in cookies and respected on subsequent visits
250250

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
<p align="center">
66
<a href="https://npmx.dev/">
7-
<img width="1090" alt="Screenshot of npmx.dev showing the nuxt package" src="https://github.com/user-attachments/assets/229497a2-8491-461c-aa1d-fba981215340">
7+
<img width="1090" alt="Screenshot of npmx.dev showing the nuxt package" src="https://github.com/user-attachments/assets/1a2a3205-0227-46dc-b1f9-48f9a65691d3">
88
</a>
99
</p>
1010

app/app.vue

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,38 @@
11
<script setup lang="ts">
2+
import type { Directions } from '@nuxtjs/i18n'
23
import { useEventListener } from '@vueuse/core'
34
45
const route = useRoute()
56
const router = useRouter()
7+
const { locale, locales } = useI18n()
68
7-
// Initialize accent color before hydration to prevent flash
8-
initAccentOnPrehydrate()
9+
// Initialize user preferences (accent color, package manager) before hydration to prevent flash/CLS
10+
initPreferencesOnPrehydrate()
911
1012
const isHomepage = computed(() => route.name === 'index')
1113
14+
const localeMap = locales.value.reduce(
15+
(acc, l) => {
16+
acc[l.code] = l.dir ?? 'ltr'
17+
return acc
18+
},
19+
{} as Record<string, Directions>,
20+
)
21+
1222
useHead({
23+
htmlAttrs: {
24+
lang: () => locale.value,
25+
dir: () => localeMap[locale.value] ?? 'ltr',
26+
},
1327
titleTemplate: titleChunk => {
1428
return titleChunk ? titleChunk : 'npmx - Better npm Package Browser'
1529
},
1630
})
1731
32+
if (import.meta.server) {
33+
setJsonLd(createWebSiteSchema())
34+
}
35+
1836
// Global keyboard shortcut: "/" focuses search or navigates to search page
1937
function handleGlobalKeydown(e: KeyboardEvent) {
2038
const target = e.target as HTMLElement

app/assets/main.css

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,25 @@
2222
--border-subtle: oklch(0.239 0 0);
2323
--border-hover: oklch(0.371 0 0);
2424

25+
/* accent color, set by user from settings */
2526
--accent: var(--accent-color, oklch(1 0 0));
2627
--accent-muted: var(--accent-color, oklch(0.922 0 0));
2728

29+
/* syntax highlighting colors */
2830
--syntax-fn: oklch(0.727 0.137 299.149);
2931
--syntax-str: oklch(0.829 0.088 252.458);
3032
--syntax-kw: oklch(0.721 0.162 15.494);
3133
--syntax-comment: oklch(0.551 0.019 250.976);
34+
35+
/* badge colors for background & text */
36+
--badge-orange: oklch(0.67 0.185 55);
37+
--badge-yellow: oklch(0.588 0.183 91);
38+
--badge-green: oklch(0.566 0.202 165);
39+
--badge-cyan: oklch(0.571 0.181 210);
40+
--badge-blue: oklch(0.579 0.191 252);
41+
--badge-indigo: oklch(0.573 0.262 276.966);
42+
--badge-purple: oklch(0.495 0.172 295);
43+
--badge-pink: oklch(0.584 0.189 343);
3244
}
3345

3446
:root[data-theme='light'] {
@@ -49,9 +61,18 @@
4961
--accent-muted: var(--accent-color, oklch(0.205 0 0));
5062

5163
--syntax-fn: oklch(0.502 0.188 294.988);
52-
--syntax-str: oklch(0.54 0.191 257.481);
64+
--syntax-str: oklch(0.425 0.152 252);
5365
--syntax-kw: oklch(0.588 0.193 20.469);
5466
--syntax-comment: oklch(0.551 0.019 250.976);
67+
68+
--badge-blue: oklch(0.579 0.191 252);
69+
--badge-yellow: oklch(0.588 0.183 91);
70+
--badge-green: oklch(0.566 0.202 165);
71+
--badge-indigo: oklch(0.457 0.24 277.023);
72+
--badge-purple: oklch(0.495 0.172 295);
73+
--badge-orange: oklch(0.67 0.185 55);
74+
--badge-pink: oklch(0.584 0.189 343);
75+
--badge-cyan: oklch(0.571 0.181 210);
5576
}
5677

5778
html {
@@ -154,9 +175,9 @@ button {
154175

155176
/* Skip link */
156177
.skip-link {
157-
position: absolute;
178+
position: fixed;
158179
top: -100%;
159-
left: 0;
180+
inset-inline-start: 0;
160181
padding: 0.5rem 1rem;
161182
background: var(--fg);
162183
color: var(--bg);
@@ -165,6 +186,10 @@ button {
165186
transition: top 0.2s ease;
166187
}
167188

189+
.skip-link:hover {
190+
color: var(--bg);
191+
text-decoration: underline;
192+
}
168193
.skip-link:focus {
169194
top: 0;
170195
}
@@ -281,12 +306,15 @@ html.light .shiki span {
281306
white-space: pre;
282307
word-break: normal;
283308
overflow-wrap: normal;
309+
/* Makes unicode and ascii art work properly */
310+
line-height: 1.25;
311+
display: inline-block;
284312
}
285313

286314
.readme-content ul,
287315
.readme-content ol {
288316
margin: 1rem 0;
289-
padding-left: 1.5rem;
317+
padding-inline-start: 1.5rem;
290318
}
291319

292320
.readme-content ul {
@@ -307,17 +335,18 @@ html.light .shiki span {
307335
}
308336

309337
.readme-content blockquote {
310-
border-left: 2px solid var(--border);
311-
padding-left: 1rem;
338+
border-inline-start: 2px solid var(--border);
339+
padding-inline-start: 1rem;
312340
margin: 1.5rem 0;
313341
color: var(--fg-subtle);
314342
font-style: italic;
315343
}
316344

317345
/* GitHub-style callouts/alerts */
318346
.readme-content blockquote[data-callout] {
319-
border-left-width: 3px;
320-
padding: 1rem 1rem 1rem 1.25rem;
347+
border-inline-start-width: 3px;
348+
padding: 1rem;
349+
padding-inline-start: 1.25rem;
321350
background: var(--bg-subtle);
322351
font-style: normal;
323352
color: var(--fg-subtle);
@@ -332,7 +361,7 @@ html.light .shiki span {
332361
text-transform: uppercase;
333362
letter-spacing: 0.05em;
334363
margin-bottom: 0.5rem;
335-
padding-left: 1.5rem;
364+
padding-inline-start: 1.5rem;
336365
}
337366

338367
.readme-content blockquote[data-callout]::after {
@@ -353,7 +382,7 @@ html.light .shiki span {
353382

354383
/* Note - blue */
355384
.readme-content blockquote[data-callout='note'] {
356-
border-left-color: var(--syntax-str);
385+
border-inline-start-color: var(--syntax-str);
357386
background: rgba(59, 130, 246, 0.05);
358387
}
359388
.readme-content blockquote[data-callout='note']::before {
@@ -368,7 +397,7 @@ html.light .shiki span {
368397

369398
/* Tip - green */
370399
.readme-content blockquote[data-callout='tip'] {
371-
border-left-color: #22c55e;
400+
border-inline-start-color: #22c55e;
372401
background: rgba(34, 197, 94, 0.05);
373402
}
374403
.readme-content blockquote[data-callout='tip']::before {
@@ -383,7 +412,7 @@ html.light .shiki span {
383412

384413
/* Important - purple */
385414
.readme-content blockquote[data-callout='important'] {
386-
border-left-color: var(--syntax-fn);
415+
border-inline-start-color: var(--syntax-fn);
387416
background: rgba(168, 85, 247, 0.05);
388417
}
389418
.readme-content blockquote[data-callout='important']::before {
@@ -398,7 +427,7 @@ html.light .shiki span {
398427

399428
/* Warning - yellow/orange */
400429
.readme-content blockquote[data-callout='warning'] {
401-
border-left-color: #eab308;
430+
border-inline-start-color: #eab308;
402431
background: rgba(234, 179, 8, 0.05);
403432
}
404433
.readme-content blockquote[data-callout='warning']::before {
@@ -413,7 +442,7 @@ html.light .shiki span {
413442

414443
/* Caution - red */
415444
.readme-content blockquote[data-callout='caution'] {
416-
border-left-color: #ef4444;
445+
border-inline-start-color: #ef4444;
417446
background: rgba(239, 68, 68, 0.05);
418447
}
419448
.readme-content blockquote[data-callout='caution']::before {
@@ -440,7 +469,7 @@ html.light .shiki span {
440469
.readme-content td {
441470
border: 1px solid var(--border);
442471
padding: 0.75rem 1rem;
443-
text-align: left;
472+
text-align: start;
444473
}
445474

446475
.readme-content th {

app/components/AccentColorPicker.vue

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,51 @@
22
import { useAccentColor } from '~/composables/useSettings'
33
44
const { accentColors, selectedAccentColor, setAccentColor } = useAccentColor()
5+
6+
onPrehydrate(el => {
7+
const settings = JSON.parse(localStorage.getItem('npmx-settings') || '{}')
8+
const id = settings.accentColorId
9+
if (id) {
10+
const input = el.querySelector<HTMLInputElement>(`input[value="${id}"]`)
11+
if (input) {
12+
input.checked = true
13+
}
14+
}
15+
})
516
</script>
617

718
<template>
8-
<div role="listbox" :aria-label="$t('settings.accent_colors')" class="flex items-center gap-4">
9-
<button
19+
<fieldset class="flex items-center gap-4">
20+
<legend class="sr-only">{{ $t('settings.accent_colors') }}</legend>
21+
<label
1022
v-for="color in accentColors"
1123
:key="color.id"
12-
type="button"
13-
role="option"
14-
:aria-selected="selectedAccentColor === color.id"
15-
:aria-label="color.name"
16-
class="size-6 rounded-full transition-transform duration-150 motion-safe:hover:scale-110 focus-ring aria-selected:(ring-2 ring-fg ring-offset-2 ring-offset-bg-subtle)"
24+
class="size-6 rounded-full transition-transform duration-150 motion-safe:hover:scale-110 cursor-pointer has-[:checked]:(ring-2 ring-fg ring-offset-2 ring-offset-bg-subtle) has-[:focus-visible]:(ring-2 ring-fg ring-offset-2 ring-offset-bg-subtle)"
1725
:style="{ backgroundColor: color.value }"
18-
@click="setAccentColor(color.id)"
19-
/>
20-
<button
21-
type="button"
22-
:aria-label="$t('settings.clear_accent')"
23-
class="size-6 rounded-full transition-transform duration-150 motion-safe:hover:scale-110 focus-ring flex items-center justify-center bg-accent-fallback"
24-
@click="setAccentColor(null)"
2526
>
27+
<input
28+
type="radio"
29+
name="accent-color"
30+
class="sr-only"
31+
:value="color.id"
32+
:checked="selectedAccentColor === color.id"
33+
:aria-label="color.name"
34+
@change="setAccentColor(color.id)"
35+
/>
36+
</label>
37+
<label
38+
class="size-6 rounded-full transition-transform duration-150 motion-safe:hover:scale-110 cursor-pointer has-[:checked]:(ring-2 ring-fg ring-offset-2 ring-offset-bg-subtle) has-[:focus-visible]:(ring-2 ring-fg ring-offset-2 ring-offset-bg-subtle) flex items-center justify-center bg-accent-fallback"
39+
>
40+
<input
41+
type="radio"
42+
name="accent-color"
43+
class="sr-only"
44+
value=""
45+
:checked="selectedAccentColor === null"
46+
:aria-label="$t('settings.clear_accent')"
47+
@change="setAccentColor(null)"
48+
/>
2649
<span class="i-carbon-error size-4 text-bg" aria-hidden="true" />
27-
</button>
28-
</div>
50+
</label>
51+
</fieldset>
2952
</template>

app/components/AppFooter.vue

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,51 @@
11
<template>
22
<footer class="border-t border-border mt-auto">
33
<div class="container py-3 sm:py-8 flex flex-col gap-2 sm:gap-4 text-fg-subtle text-sm">
4-
<div class="flex flex-col sm:flex-row items-center justify-start gap-2 sm:gap-4">
5-
<p class="font-mono m-0 hidden sm:block">{{ $t('tagline') }}</p>
6-
<span aria-hidden="true" class="flex-shrink-1 flex-grow-1" />
7-
<div class="flex items-center justify-start gap-3 sm:gap-6">
4+
<div
5+
class="flex flex-col sm:flex-row items-center sm:items-baseline justify-between gap-2 sm:gap-4"
6+
>
7+
<p class="font-mono text-balance m-0 hidden sm:block">{{ $t('tagline') }}</p>
8+
<div class="flex items-center gap-3 sm:gap-6">
89
<NuxtLink
910
to="/about"
1011
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center"
1112
>
12-
<span>{{ $t('footer.about') }}</span>
13+
{{ $t('footer.about') }}
1314
</NuxtLink>
1415
<a
1516
href="https://docs.npmx.dev"
1617
target="_blank"
1718
rel="noopener noreferrer"
18-
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center justify-start gap-1"
19+
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center gap-1"
1920
>
20-
<span>{{ $t('footer.docs') }}</span>
21+
{{ $t('footer.docs') }}
2122
<span class="i-carbon:launch rtl-flip w-3 h-3" aria-hidden="true" />
2223
</a>
2324
<a
2425
href="https://repo.npmx.dev"
2526
target="_blank"
2627
rel="noopener noreferrer"
27-
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex justify-start items-center gap-1"
28+
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center gap-1"
2829
>
29-
<span>{{ $t('footer.source') }}</span>
30+
{{ $t('footer.source') }}
3031
<span class="i-carbon:launch rtl-flip w-3 h-3" aria-hidden="true" />
3132
</a>
3233
<a
3334
href="https://social.npmx.dev"
3435
target="_blank"
3536
rel="noopener noreferrer"
36-
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center justify-start gap-1"
37+
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center gap-1"
3738
>
38-
<span>{{ $t('footer.social') }}</span>
39+
{{ $t('footer.social') }}
3940
<span class="i-carbon:launch rtl-flip w-3 h-3" aria-hidden="true" />
4041
</a>
4142
<a
4243
href="https://chat.npmx.dev"
4344
target="_blank"
4445
rel="noopener noreferrer"
45-
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center justify-start gap-1"
46+
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center gap-1"
4647
>
47-
<span>{{ $t('footer.chat') }}</span>
48+
{{ $t('footer.chat') }}
4849
<span class="i-carbon:launch rtl-flip w-3 h-3" aria-hidden="true" />
4950
</a>
5051
</div>

0 commit comments

Comments
 (0)