Skip to content

feat(ui): /noodles archive page#2779

Open
Adebesin-Cell wants to merge 36 commits into
npmx-dev:mainfrom
Adebesin-Cell:feat/noodles-archive
Open

feat(ui): /noodles archive page#2779
Adebesin-Cell wants to merge 36 commits into
npmx-dev:mainfrom
Adebesin-Cell:feat/noodles-archive

Conversation

@Adebesin-Cell
Copy link
Copy Markdown
Contributor

@Adebesin-Cell Adebesin-Cell commented May 22, 2026

A place to look back at noodles we've shipped — a little museum for past npmx logos.

/noodles is the archive grid. Each card opens a detail page with the full-size logo, the day/event it ran for, dates, the PR it shipped in, and credits.

The detail page is intentionally light: only the basics are required (title, slug, date, a one-line occasion). Longer write-ups and process images are optional, so adding a noodle never gets blocked on having a story written for it.

Highlights

  • /noodles archive grid, latest 10 shown with a load-more button.
  • Per-noodle detail page (/noodles/:slug) with a friendly bowl 404 for unknown slugs.
  • Hand-edited registry in app/noodles.ts — drop a logo, register it, add an entry.
  • Credits use the same AuthorList the blog does; avatars resolved at build time via a small modules/noodles.ts.
  • Seeded with the four noodles we've shipped so far: Kawaii, Kawaii (Pride), Artemis, Press Freedom Day.

Screenshots

Landing — /noodles archive Landing — /noodles archive
Screen.Recording.2026-05-22.at.16.33.17.mov

Introduces a public archive for every noodle (seasonal/event logo variants)
shown on npmx, plus a contributor-friendly layout for adding new ones.

What's new
- /noodles lists every entry as a card (logo preview + date range or
  "always available" badge for permanent noodles).
- /noodles/[slug] renders a per-noodle markdown page with hero logo,
  story body, authors, and an image gallery with click-to-lightbox.
- modules/noodles.ts scans app/pages/noodles/*.md, validates frontmatter
  with valibot, resolves Bluesky avatars at build time, and exposes
  #noodles/entries.
- app/components/Noodle/index.ts now derives ACTIVE/PERMANENT noodle
  arrays from #noodles/entries, so contributors only edit the .md file
  and the logo registry.
- New NoodleGallery + NoodleLightbox (custom dialog-based, no external
  lib) and NoodleListCard for the archive grid.
- Dedicated NoodlePostWrapper registered by the noodles module with a
  scoped include pattern, so blog and noodle markdown files don't share
  a wrapper. Blog module's include narrowed to pages/blog/.

Other touches
- Footer "Other" section now links to /noodles.
- canonical-redirects.global.ts allowlist gains /noodles so the server
  middleware doesn't shortcut it to /package/noodles.
- a11y test coverage for the four new components.
- i18n keys under noodles.* (English only; CI syncs other locales).

Seed
Two stub .md files (press, kawaii) marked draft: true ship with this
commit so the archive has content immediately. Gallery images should
live under public/noodle-gallery/[key]/ — keeping them out of
public/noodles/ avoids shadowing the /noodles route in dev.
- Landing /noodles hero: solid filled "bowl" (thick theme-aware border,
  inner + outer shadow), npmx sticker overlapping, thicker dark stripe
  background with wider gaps. Headings bumped to mono text-xl semibold
  uppercase per the Figma spec.
- Individual noodle pages: restructured into an agency-style case study —
  full-bleed hero with the bowl, credits row (avatar + name + @handle, N
  authors), tombstone strip (dates · status), prose body, editorial
  numbered Figures section, footer back link.
- New Noodle/Figures.vue: numbered Fig.NN editorial figure list with
  lightbox, replaces the 4-tile gallery on individual noodle pages.
- New Noodle/BuildLog.vue: scroll-snap drafts → shipped strip, ready to
  wire once noodles get an authored drafts schema.
- ListCard icon: maximize-2 → arrow-up-right (clearer "navigate to").
- Kawaii noodle: gave it active dates (no more "always available"),
  removed the /?kawaii parenthetical from the body stub.
- Press noodle: populated with multi-author credits, real excerpt,
  4-section body (brief · decisions · process · cuts), and a 4-image
  gallery so the case-study layout has something to render.
- i18n (en): added noodles.credits, status, status_draft, status_shipped,
  figures.
- Gitignore public/noodle-avatar/ (Bluesky avatars are fetched at build
  time, mirrors the existing public/blog/avatar pattern).
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment May 22, 2026 3:40pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview May 22, 2026 3:40pm
npmx-lunaria Ignored Ignored May 22, 2026 3:40pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a noodle archive and Valibot schema, new logo components and a central resolver, list/detail Nuxt pages with pagination and SEO, a carousel and list card UI, a Nuxt module to cache Bluesky avatars to public/noodle-avatar, and app integration including header/footer links, i18n and accessibility tests.

Changes

Noodles Feature — Logo Doodles Archive

Layer / File(s) Summary
Data schema and archive
shared/schemas/noodle.ts, app/noodles.ts
Adds NoodleSchema and Noodle type; introduces archive entries, exports sorted noodles and findNoodle(slug) helper.
Avatar caching Nuxt module
modules/noodles.ts, .gitignore
Nuxt module parses registry handles, fetches Bluesky profiles, hashes avatar URLs to deterministic PNGs under public/noodle-avatar, emits noodles/avatars.ts resolver and registers #noodles/avatars; public/noodle-avatar added to .gitignore.
Logo components and resolution
app/components/Noodle/index.ts, app/components/Noodle/Artemis/Logo.vue, app/components/Noodle/KawaiiPride/Logo.vue, app/components/Noodle/Press/Logo.vue, app/components/Noodle/ListCard.vue
Introduces NOODLE_LOGOS map and resolveNoodleLogo; adds Artemis and KawaiiPride logo components; simplifies Press logo to direct ColorSchemeImg; adds NoodleListCard linking to /noodles/:slug with date display.
Noodles pages and routing
app/pages/noodles/index.vue, app/pages/noodles/[slug].vue
Archive index with client-side pagination/load-more and hero; detail page resolves noodle, logo and enriched authors (avatars/Bluesky URLs), sets SEO/OpenGraph, shows metadata, description and process images, and handles 404 with back-to-archive links.
Carousel and image UI
app/components/Noodle/Carousel.vue
Adds a snap-scrolling carousel component with prev/next and dot navigation, eager-first image loading, ARIA labels and active-index tracking; used for noodle process images.
App integration, i18n and tests
app/components/Landing/IntroHeader.vue, app/components/AppFooter.vue, app/components/AppHeader.vue, server/middleware/canonical-redirects.global.ts, modules/blog.ts, i18n/locales/en.json, i18n/schema.json, test/nuxt/a11y.spec.ts
IntroHeader refactor to use unified NOODLES dataset and date-range logic; AppHeader/AppFooter add /noodles links; canonical allowlist adds /noodles; blog Markdown include narrowed; new noodles i18n keys and schema; accessibility tests extended to new logos and NoodleListCard.

Possibly related PRs

Suggested reviewers

  • graphieros
  • alexdln
  • patak-cat
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main addition: a new /noodles archive page feature, which aligns with the substantial changes across multiple files implementing this archive grid and detail pages.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description clearly describes a new noodles archive feature with pages, registry, credits system, and details about implementation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 22, 2026

Lunaria Status Overview

🌕 This pull request will trigger status changes.

Learn more

By default, every PR changing files present in the Lunaria configuration's files property will be considered and trigger status changes accordingly.

You can change this by adding one of the keywords present in the ignoreKeywords property in your Lunaria configuration file in the PR's title (ignoring all files) or by including a tracker directive in the merged commit's description.

Tracked Files

File Note
i18n/locales/en.json Source changed, localizations will be marked as outdated.
Warnings reference
Icon Description
🔄️ The source for this localization has been updated since the creation of this pull request, make sure all changes in the source have been applied.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@Adebesin-Cell Adebesin-Cell changed the title feat(noodles): case-study layout for individual noodle pages feat(noodles): /noodles archive with per-noodle case studies May 22, 2026
@Adebesin-Cell Adebesin-Cell changed the title feat(noodles): /noodles archive with per-noodle case studies feat(ui): /noodles archive with per-noodle case studies May 22, 2026
- Add a11y tests for Noodle/Figures and Noodle/BuildLog.
- Remove unused Noodle/Gallery (replaced by Noodle/Figures).
- Drop unused i18n keys noodles.credits and noodles.gallery.
Team feedback: noodle write-ups would become a blocker, and the current
detail content is placeholder anyway. Strip the case-study scaffolding —
keep the /noodles archive landing page only.

- Delete NoodlePostWrapper, BuildLog, Figures, Lightbox.
- Delete press.md and kawaii.md (and the markdown vite plugin that
  turned them into routes).
- Replace the noodles Nuxt module with a hand-edited TS registry at
  app/noodles.ts. Adding a noodle is now: drop a logo, register it in
  Noodle/index.ts, append an entry. No frontmatter required.
- Slim shared/schemas/noodle.ts to a single Noodle type — only key,
  title, slug are required; date/dateTo/timezone/tagline/prUrl/draft
  optional.
- ListCard: when prUrl is set the card becomes an external link to
  the shipping PR; otherwise it's a non-interactive tile (no broken
  link to a detail page).
- Drop unused i18n keys (status, figures, lightbox, gallery, etc.).
- Drop a11y tests for the removed components.
- Revert storybook .md ignore + .gitignore noodle-avatar entries.
Team feedback: per-noodle write-ups would become a blocker, and the
existing detail content was placeholder anyway. Strip the case-study
scaffolding — keep the /noodles archive landing page only.

- Delete NoodlePostWrapper, BuildLog, Figures, Lightbox.
- Delete press.md and kawaii.md (and the markdown vite plugin that
  turned them into routes).
- Replace the noodles Nuxt module with a hand-edited TS registry at
  app/noodles.ts. Adding a noodle is now: drop a logo, register it
  in Noodle/index.ts, append an entry. No frontmatter required.
- Slim shared/schemas/noodle.ts to a single Noodle type — key,
  title, slug, date required; dateTo/timezone/tagline/prUrl optional.
- Drop the "permanent noodle" concept entirely — all noodles are
  historical (have a date). Query param ?<key> still triggers any
  noodle regardless of date; otherwise activation falls back to the
  noodle's date range.
- ListCard: when prUrl is set the card becomes an external link to
  the shipping PR; otherwise it's a non-interactive tile.
- Drop unused i18n keys (status, figures, lightbox, gallery, etc.).
- Drop a11y tests for the removed components.
- Revert storybook .md ignore + .gitignore noodle-avatar entries.
@Adebesin-Cell Adebesin-Cell changed the title feat(ui): /noodles archive with per-noodle case studies feat(ui): /noodles archive page May 22, 2026
@trueberryless trueberryless mentioned this pull request May 22, 2026
3 tasks
…on blend

- Restore the artemis noodle from npmx PR npmx-dev#2421/npmx-dev#2424. The light/dark
  SVGs were already in public/extra/; the SFC was removed when press
  was added. Registered in Noodle/index.ts and app/noodles.ts with the
  original April 2026 date range.
- Drop the `?<key>` query-param branch from IntroHeader — all noodles
  are historical now, so activation is purely date-based.
- Match Kawaii's hover/scale animation on Press, so all three noodles
  feel consistent on the homepage.
- Remove the press-day tooltip — it leaks into the /noodles archive
  card on hover.
- Bowl moon image was showing its black/white rectangle background in
  the bowl on /noodles. Add `mix-blend-lighten light:mix-blend-darken`
  so it dissolves into the bowl bg in both themes (same trick the
  artemis logo uses).
- Stamp prUrl on press + artemis so their cards link to the shipping PR.
Per Alex's note: detail page always exists, but content is non-blocking.

- New dynamic route app/pages/noodles/[slug].vue. Required content: hero
  bowl + title + occasion one-liner + dates. Optional sections only
  render when the registry entry has them filled in (description,
  processImages).
- Schema gains optional `occasion`, `description`, `processImages`.
  Each existing entry now ships with an `occasion` line; the rest is
  blank and can be added per-noodle later.
- Archive ListCard links back to the detail page (instead of jumping
  straight to the PR). The PR link moves to the detail page tombstone.
- Cool "empty bowl" 404 — when /noodles/<unknown> is hit, the page
  still renders the bowl shape and a "this noodle never made it out
  of the bowl" message with a path back to /noodles. Server returns
  a 404 status to crawlers; humans see the friendly UI.

Note on the missing "pride" noodle: no standalone pride PR exists.
The cute-transgender variant in public/extra/ is a revision of the
kawaii noodle (PR npmx-dev#2349), not a separate noodle. Surfaced in the
kawaii entry's `occasion` line.
…wl content

- Add Noodle/KawaiiPride/Logo.vue using npmx-cute-transgender.svg.
- Original Kawaii reverts to npmx-cute.svg (pink/peach), tied to
  PR npmx-dev#2346 — the same-day pride revision (PR npmx-dev#2349) now lives as a
  distinct registry entry so both ship in the archive as history.
- Detail-page bowl gains `flex items-center justify-center` so the
  logo / 404 ? glyph centers properly inside the circle.
…nsive pass

- Restore the "what is noodles" section on /noodles landing — Alex
  flagged it had gone missing during the slim-down. Body simplified
  to a single paragraph (the previous copy was repeated 5x).
- Authors field on noodles. Default duo Alex (alexdln.com) + Alfon
  (alfon.dev) credited on all four current noodles since they paired
  on both design and shipping PRs.
- Credits column on the detail-page tombstone — name + linked @handle,
  flex-wrap so multiple authors stay tidy.
- Load-more pagination on /noodles. Default shows the latest 10, a
  "Load N more" button reveals the next page. Renders nothing when
  the archive fits in one screen.
- Responsive tweaks: smaller bowl (w-60 → w-96), thinner bowl border
  (10px → 14px), smaller hero title (text-3xl → text-6xl) and
  sticker (w-20 → w-40) on mobile; detail-page header/title scale
  down too. Tombstone goes one column on mobile, three on sm+.
- Drop the duplicate what_is/what_is_body i18n keys.
- max-w-prose removed from the what-is paragraph per request.
- Replace `container` on noodle main with plain px-4 sm:px-6 — the
  container utility was disturbing the bowl hero layout. Padding is
  what we actually needed.
- Credits now render via the shared AuthorList expanded variant (same
  component the blog uses) — avatar + name + @handle, flex-wrap.
  Authors registry entries stay minimal: { name, blueskyHandle }.
  Avatars fall back to initials when not resolved.
- Drop the manual credits column from the tombstone; tombstone is
  back to two columns (dates · shipped in).
- Default duo Alex (alexdln.com) + Alfon (alfon.dev) credited on
  all four noodles per the team — they pair on design + shipping.
Per feedback, credits don't belong above the title — they live in the
metadata row alongside dates / shipped-in. Switched to the compact
AuthorList variant so the row stays tight (stacked avatars + names),
tombstone is now three columns on sm+, one on mobile.

Also drop the px-4 sm:px-6 from main: padding belongs on the content
article, so the hero spans full bleed without negative margins.
- Tombstone is two columns again (dates · shipped in). Credits sits
  on its own row below with col-span-full, so multi-author noodles
  no longer crowd the dates/PR columns. Switch back to the expanded
  AuthorList variant since we have the horizontal room.
- Add px-4 sm:px-6 to /noodles landing article so cards + headings
  don't hug the viewport edge on mobile.
- Remove the px-4 sm:px-6 I left on the [slug].vue main; padding
  lives on content blocks now (consistent with landing), hero stays
  full-bleed.
Addresses team feedback on PR npmx-dev#2779:

- New Noodle/Carousel.vue — horizontal scroll-snap carousel with
  prev/next chevrons and pagination dots. Replaces the stacked
  figures section on the noodle detail page.
- Press noodle seeded with dummy processImages so the carousel
  is reachable for review at /noodles/press.
- Mobile nav: /noodles link added to the About & Policies group.
  Was missing from the mobile menu (only existed in the footer).
- All four noodle Logo SFCs: drop the hover:scale-105 zoom (Alex's
  note that hover-zoom-without-fullscreen-open is confusing) and
  the mb-8 bottom margin (was throwing the archive card centering
  off, especially on mobile).
- Artemis reverted to wordmark-only — the moon overlay was fighting
  the bowl/card layout. We can reintroduce it as a parent-positioned
  backdrop on the homepage hero later if needed.
- /noodles landing bowl: stop rendering the moon image in light mode.
  The PNG has a hard black rectangle bg and mix-blend can't make black
  drop out cleanly against a near-white bowl — fix is just to skip it
  in light. Dark mode still gets the moon with mix-blend-lighten.
- Artemis logo gets its moon overlay back (per Alex / further design
  discussion).
Per Graphieros — the bowl reads like a magnifying glass, so use it
as one. Logo + processImages now scroll horizontally inside the bowl;
the lens stays fixed, slides pass under it. Drops the separate
Process section since it's redundant now.

- Lens chrome (rounded clip, border, inset shadow, drop shadow) is
  unchanged — just hosts a snap-x scroller now.
- Logo is slide 1; each processImage follows.
- Chevrons float off the lens edges; dots sit below the bowl. Both
  only render when there are 2+ slides, so single-logo noodles read
  as before.
- New i18n: noodles.lens_label, noodles.lens_slide.
- Lens carousel now loops infinitely. Slides are rendered 3× so
  native scroll-snap can wrap seamlessly: on mount the scroll
  position lands in the middle copy, and a scrollend handler
  snaps us back when the user crosses either edge. Prev/next
  pick the shortest-path direction so wrapping always feels
  like a single step. Chevrons are no longer disabled at the
  extremes.
- Carousel only mounts the scroller when the noodle has process
  images. Without process images, the bowl statically renders
  the logo (no scroll container, no chevrons, no dots).
- Keyboard navigation: scroller is focusable (tabindex=0) with
  ArrowLeft/ArrowRight to step through slides, Home/End to jump
  to first/last. Focus ring uses the accent token, applied with
  focus-visible only so mouse users don't see it.
- ARIA: role=region + aria-roledescription=carousel on the
  scroller; each visible slide gets role=group + an
  aria-label like "Slide N of M". Duplicate copies are
  aria-hidden so screen readers don't read the same content
  three times.
- prefers-reduced-motion is honored when scrolling between slides
  (uses VueUse's useMediaQuery — same pattern as the rest of the
  codebase). Drops the inline matchMedia setup.
- Pagination dots get a focus-visible ring (accent-coloured, offset
  from the bowl bg) — they were keyboard-focusable already but the
  focus state was invisible.
- New sr-only aria-live=polite region under the dots: announces
  "Slide N of M" when activeSlide changes, since focus stays on the
  scroller while slides move under it.
- Drop aria-label="404" from the 404 page's kicker text — was
  overriding the visible "404 — empty bowl" string for screen
  readers; visible text is fine to read as-is.
- New OgImage/Noodle.takumi template renders the noodle's posterImage
  alongside title + occasion. Detail page picks Noodle.takumi when an
  entry has a posterImage, falls back to Page.takumi otherwise.
- /noodles landing also gets a Page.takumi og image so the archive
  link previews properly.
- posterImage stamped on every noodle: dark press PNG for press, the
  cute SVG for kawaii, trans SVG for kawaii-pride, dark artemis SVG
  for artemis.
- Chevrons: hidden on mobile (touch swipe is the primary input there),
  bigger and pushed off the lens on desktop (-inset-is-16 / lg:-24,
  text-3xl, larger padding).
- Comment pass per request: dropped redundant <!-- HERO/BODY/404 -->
  labels, collapsed multi-line explanations to single lines where the
  WHY needed surfacing, and trimmed the JSDoc blocks on the noodle
  registry + schema since the types already convey shape.
OgBrand uses Nuxt's auto-imported `computed` — when nuxt-og-image
re-loads the component via its og-image-depth pipeline, the auto-
imports don't apply and rendering throws "computed is not defined".
Inlining the logo keeps the template self-contained.
The flex layout had max-w-[60%] + max-w-[40%] + gap-12 which sums
above 100% and pushed the poster image off the right edge of the
OG canvas. Switched to a flex-1 left + shrink-0 fixed-width right
arrangement, and used explicit inline styles for the poster's
object-fit to be safe under Takumi rendering.
Takumi/Satori doesn't support mix-blend-mode, which is what the
landing/detail bowl uses to drop the moon image's black rectangle
against the dark bowl bg. Without it, the OG card showed the bare
photo rectangle.

Keeping the schema's optional posterBackdrop field — works as soon
as we ship a properly alpha-channelled moon asset.
- Detail page is now a two-column grid inside the striped hero:
  - Left: the lens (bowl + chevrons + dots + sr-live region),
    sticky from lg: so it stays in view while the right column
    grows with description content.
  - Right: a rounded card holding title, occasion, optional
    description, then a divider and the dates / shipped-in /
    credits metadata.
- 404 reuses the exact same shell (lens with a `?` glyph, card
  with the empty-bowl message), so missing slugs feel like the
  same page rather than a separate template.
- "Back to all noodles" lives at the top of the hero, inside the
  striped band, replacing the bottom footer link.
- Chevrons stay as the big buttons next to the lens; dots and the
  live region hang under the bowl.
…point

- Hero section now flex-1 inside a flex-col main, so the striped
  background fills the viewport instead of stopping at the content
  and leaving a black void above the footer on tall screens.
- Two-column grid + sticky lens kick in at xl: (1280px) instead of
  lg: (1024px). iPad Pro 1024 was hitting the desktop layout too
  early and looked cramped — that breakpoint now stacks like md.
Section is now flex flex-col with the nav pinned at the top
and the grid in flex-1 + content-center so the lens/card sit
in the middle of the remaining vertical space. Stops the lens
from hugging the top with a huge striped void below.
Centering broke the sticky scroll feel. Grid is back to items-start
so the lens column anchors to the top — and stays there via
xl:sticky xl:top-24 while the card scrolls past on tall content.
- Remove overflow-x-hidden from the detail page main. overflow-x:
  hidden with overflow-y: visible computes overflow-y as auto per
  spec, which turned main into a scroll container and trapped the
  sticky lens so it never engaged on page scroll.
- Strip the dummy processImages and description from the press
  entry now that previews are no longer needed.
When I moved the carousel into the lens, the standalone
Noodle/Carousel.vue stopped being imported (knip + a11y
coverage flagged it) and the noodles.process heading was
never re-used (i18n flagged it as unused). Removing both.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant