Skip to content

Upgrade to Astro v6#534

Merged
lucperkins merged 12 commits into
mainfrom
astro-v6
May 16, 2026
Merged

Upgrade to Astro v6#534
lucperkins merged 12 commits into
mainfrom
astro-v6

Conversation

@lucperkins
Copy link
Copy Markdown
Member

@lucperkins lucperkins commented May 14, 2026

Summary by CodeRabbit

  • New Features

    • Added per-page markdown export endpoints for individual content pages.
  • Bug Fixes & Improvements

    • Upgraded core platform and build tooling (major Astro, Tailwind, Vite and related plugins).
    • Improved routing and link generation to use stable page IDs.
    • Switched Tailwind integration to a Vite-based plugin and consolidated CSS.
  • Style

    • Refined and unified component styling and prose typography across the site.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 14, 2026

Deploy Preview for zero-to-nix ready!

Name Link
🔨 Latest commit 5aa035c
🔍 Latest deploy log https://app.netlify.com/projects/zero-to-nix/deploys/6a07c2009dd38d00080e9d75
😎 Deploy Preview https://deploy-preview-534--zero-to-nix.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

Caution

Review failed

Pull request was closed or merged during review

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

This PR upgrades Astro to v6 and modernizes the codebase's content handling and styling infrastructure. It introduces Astro content collections with Zod validation, renames internal page routing from slug to id, migrates Tailwind from the Astro integration to the Vite plugin, adds markdown export endpoints for collections, updates content rendering to use render() from astro:content, and consolidates CSS organization into a single theme-aware stylesheet. Nearly all components receive class reordering as part of the styling consolidation.

Changes

Content Collections & ID-based Routing

Layer / File(s) Summary
Collection definitions and schemas
src/content.config.ts
Defines four content collections (briefs, concepts, plain, start) using Astro's defineCollection with glob loaders and Zod schemas; concepts and start collections include detailed validation for optional fields like externalSources (with nested source objects) and summary.
Field renaming: slug → id
src/lib/utils.ts
Page path helpers (startPagePath, conceptPagePath, and internal pagePath) now accept id parameter instead of slug; URL construction and substring logic remain unchanged.
Navbar and pagination link updates
src/components/Navbar.astro, src/components/Pagination.astro
Navbar dropdown links and pagination prev/next links now destructure and pass id (instead of slug) to path helpers; import statements reorganized.
Collection index pages
src/pages/concepts/index.astro, src/pages/start/index.astro
Concept and start page listings iterate over collection entries using id and build link href values via startPagePath(id) / conceptPagePath(id).
Dynamic route params
src/pages/concepts/[id].astro, src/pages/start/[id].astro
getStaticPaths now emit route params using params: { id: page.id } (or with .substring(1) for start pages) instead of slug-based params; related-concept filtering rewritten to use id.
Template rendering
src/pages/llms.txt.ts, src/pages/llms-full.txt.ts
Handlebars template inputs for LLM-format exports now map pages using id and generate URLs via startPagePath(id) / conceptPagePath(id).

Content Rendering & MDX Updates

Layer / File(s) Summary
Render API migration
src/components/mdx/Brief.astro, src/pages/about.astro, src/pages/concepts/[id].astro, src/pages/start/[id].astro
Components and pages switch from page.render() to await render(page) imported from astro:content to obtain Content; props and data extraction unchanged.
Markdown collection export endpoints
src/pages/concepts/[id].md.ts, src/pages/start/[id].md.ts
New static routes that serve markdown for concept and start collection entries; getStaticPaths strips .md/.mdx extensions from entry.id to form route params, GET handler resolves entries and optionally prefixes Markdown body with # {title} header, returning text/markdown with cache headers or 404 when entry is missing.

Tailwind Build Tool Migration

Layer / File(s) Summary
Dependency updates
package.json
Upgraded astro to ^6.3.3, upgraded Astro ecosystem packages (@astrojs/alpinejs, @astrojs/check, @astrojs/mdx, @astrojs/react), removed @astrojs/tailwind, upgraded @expressive-code plugins to ^0.42.0, upgraded astro-expressive-code to ^0.42.0, added @tailwindcss/vite and upgraded tailwindcss to ^4.3.0, upgraded vite to ^7.3.3.
Configuration switch to Vite plugin
astro.config.mjs
Removed tailwind() from Astro integrations; added vite: { plugins: [tailwindcss()] } block to register Tailwind via @tailwindcss/vite; import updated from @astrojs/tailwind to @tailwindcss/vite.
CSS structure reorganization
src/assets/css/main.css, src/assets/css/prose.css, src/assets/css/variables.css, src/assets/css/tailwind.css, src/assets/css/style.css
Consolidated stylesheet imports and global styles: cleared main.css, prose.css, variables.css, and tailwind.css; created new style.css with Tailwind directives, @theme block (colors, borders, transition, max-width, fonts), @utility rules (banner-link, inline-code), base-layer .content and .prose styling (links, headings, code, footnotes, admonitions).
Tailwind config removal
tailwind.config.ts
Deleted the Tailwind configuration file; settings (theme customization, typography plugin, content scanning, dark mode) are now defined in style.css via @theme block and style.css imports.
Layout stylesheet update
src/layouts/Layout.astro
Stylesheet import switched from main.css to style.css; body class attribute adjusted for new class organization.

Styling & Class Organization

Layer / File(s) Summary
Component class reordering
src/components/Banner.astro, src/components/Breadcrumb.astro, src/components/CookieConsent.astro, src/components/Drawer.astro, src/components/DrawerToggler.astro, src/components/Dropdown.astro, src/components/ExternalSources.astro, src/components/Features.astro, src/components/FeedbackBar.astro, src/components/Footer.astro, src/components/Hero.astro, src/components/HorizontalContainer.astro, src/components/HoverableExternalSourceLink.astro, src/components/HoverableLink.astro, src/components/NixTerms.astro, src/components/PageSurvey.tsx, src/components/QuickStart.astro, src/components/Related.astro, src/components/Signup.astro, src/components/SignupModal.astro, src/components/Summary.astro, src/components/ThemeSwitcher.astro
Widespread reordering of Tailwind utility class tokens across element class attributes and class:list conditionals; sets of classes remain the same (e.g., hover/dark-mode/sizing utilities), but their order within the string has changed. Some minor styling adjustments (e.g., Dropdown removes focus-visible:ring-opacity-75, Hero removes rounded from WIP notice).
MDX component styling updates
src/components/mdx/Admonition.astro, src/components/mdx/Install.astro, src/components/mdx/Languages.astro, src/components/mdx/NixStorePath.astro, src/components/mdx/Platforms.astro
Reordered Tailwind classes on admonition wrapper, OS selector buttons, language tabs, Nix store path container, and platform cards; Admonition adds admonition not-prose classes and restructures icon markup to use consistent text-* + size-* pattern; similar icon styling updates in icon-dependent components.
Page styling updates
src/pages/index.astro
Hero section heading and button class lists reordered/adjusted; section headers reordered for dark-mode hover styling; responsive padding/sizing on CTA buttons updated.
Component deprecation & removal
src/components/Header.astro, src/components/HeaderLink.astro
Removed top-level Header and HeaderLink components; their navigation markup and styling are no longer in the codebase.
Alpine.js parameter naming
src/entrypoint.ts
Local persisted helper and default plugin export renamed parameter from Alpine to alpine (lowercase) for consistency; store initialization and persist calls updated to use lowercase identifier; no behavior change to store logic or persistence.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Hops through collections with brand-new id,
Renders content modern, old slugs now rid,
Tailwind dances via Vite's swift hand,
Classes reordered across the whole land,
Astro six shines, the codebase stands grand!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Upgrade to Astro v6' accurately describes the main objective of this pull request, which comprehensively updates the Astro framework from v5 to v6 along with related ecosystem packages.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch astro-v6

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/pages/concepts/[id].md.ts (1)

8-8: 💤 Low value

Consider whether case-insensitive extension matching is necessary.

The stripExt function uses a case-insensitive regex (/\.(md|mdx)$/i). Verify whether mixed-case extensions (e.g., .MD, .Mdx) are actually used in your content files. If not, a case-sensitive match would be more precise.

📝 Potential simplification
-const stripExt = (id: string) => id.replace(/\.(md|mdx)$/i, "");
+const stripExt = (id: string) => id.replace(/\.(md|mdx)$/, "");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/concepts/`[id].md.ts at line 8, stripExt currently uses a
case-insensitive regex (/\.(md|mdx)$/i) which may be overly permissive; if your
repository never contains mixed-case extensions, change the regex in the
stripExt function to a case-sensitive pattern (/\.(md|mdx)$/) to be more
precise, update any callers/tests that assume mixed-case behavior, and run the
content-file matching script to confirm no files rely on uppercase extensions
before committing.
src/content.config.ts (1)

18-19: ⚡ Quick win

Consider tightening the related field validation.

The TODO suggests making the related array schema more specific. Consider validating that referenced IDs exist in the concepts collection or restricting the format.

Would you like me to suggest a refined schema that validates concept references?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/content.config.ts` around lines 18 - 19, The current related:
z.array(z.string()) is too permissive; tighten it by replacing it with a schema
that enforces the expected ID format and/or verifies existence: e.g. change the
related field in src/content.config.ts to
z.array(z.string().regex(/^[0-9a-fA-F]{24}$/, "invalid ObjectId")) to enforce
Mongo ObjectId format, or use z.array(z.string().refine(async id => await
validateConceptExists(id), "unknown concept id")) if you want runtime existence
checks; add or reference a helper like validateConceptExists(conceptId) (or
ConceptsService.existsById) to perform the lookup and return a boolean.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/content.config.ts`:
- Around line 18-19: The current related: z.array(z.string()) is too permissive;
tighten it by replacing it with a schema that enforces the expected ID format
and/or verifies existence: e.g. change the related field in
src/content.config.ts to z.array(z.string().regex(/^[0-9a-fA-F]{24}$/, "invalid
ObjectId")) to enforce Mongo ObjectId format, or use
z.array(z.string().refine(async id => await validateConceptExists(id), "unknown
concept id")) if you want runtime existence checks; add or reference a helper
like validateConceptExists(conceptId) (or ConceptsService.existsById) to perform
the lookup and return a boolean.

In `@src/pages/concepts/`[id].md.ts:
- Line 8: stripExt currently uses a case-insensitive regex (/\.(md|mdx)$/i)
which may be overly permissive; if your repository never contains mixed-case
extensions, change the regex in the stripExt function to a case-sensitive
pattern (/\.(md|mdx)$/) to be more precise, update any callers/tests that assume
mixed-case behavior, and run the content-file matching script to confirm no
files rely on uppercase extensions before committing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 42da30bc-2ebe-45ab-847e-0cb31ca47dea

📥 Commits

Reviewing files that changed from the base of the PR and between 4df5f96 and 88b332c.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • package.json
  • src/components/Navbar.astro
  • src/components/Pagination.astro
  • src/components/mdx/Brief.astro
  • src/content.config.ts
  • src/content/config.ts
  • src/lib/utils.ts
  • src/pages/about.astro
  • src/pages/concepts/[id].astro
  • src/pages/concepts/[id].md.ts
  • src/pages/concepts/[slug].md.ts
  • src/pages/concepts/index.astro
  • src/pages/llms-full.txt.ts
  • src/pages/llms-small.txt.ts
  • src/pages/llms.txt.ts
  • src/pages/start/[id].astro
  • src/pages/start/[id].md.ts
  • src/pages/start/[slug].md.ts
  • src/pages/start/index.astro
💤 Files with no reviewable changes (3)
  • src/content/config.ts
  • src/pages/start/[slug].md.ts
  • src/pages/concepts/[slug].md.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
astro.config.mjs (1)

44-46: 💤 Low value

Inconsistent indentation: spaces vs tabs.

Lines 44-46 use 2-space indentation while the rest of the file uses tabs.

🔧 Proposed fix
-  vite: {
-    plugins: [tailwind()],
+	vite: {
+		plugins: [tailwind()],
   },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@astro.config.mjs` around lines 44 - 46, The vite configuration block (vite: {
plugins: [tailwind()], }) uses 2-space indentation that conflicts with the
file's tab-based formatting; update the indentation for the vite object, its
plugins property, and the tailwind() line to use tabs so the vite block matches
the rest of the file's tab indentation (look for the vite, plugins, and
tailwind() lines and reformat their leading whitespace to tabs).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@astro.config.mjs`:
- Line 5: The imported Vite plugin "tailwind" from "@tailwindcss/vite" in
astro.config.mjs is incompatible with the current Tailwind version; update the
"tailwindcss" dependency in package.json to a compatible v4.x release (and
reinstall / update lockfile) so the plugin import and any uses of the plugin in
astro.config.mjs (also referenced around the block at the end of file lines
~44-46) will work; after upgrading, verify the import statement import tailwind
from "@tailwindcss/vite" and existing plugin configuration remain unchanged and
run a dev/build to confirm no version conflicts.

In `@package.json`:
- Line 51: The package.json lists "@tailwindcss/vite": "^4.3.0" which requires
tailwindcss v4.x while your project currently has "tailwindcss": "3.4.19"; fix
by either upgrading the "tailwindcss" dependency to a compatible v4.x release
(and update any Tailwind config files to the new v4 config format) or remove the
"@tailwindcss/vite" entry and revert to the previous integration (e.g., add or
keep "@astrojs/tailwind") so Tailwind v3 remains supported; update package.json
dependencies accordingly and run install/build to verify no config migration is
needed.
- Around line 33-34: The package.json lists an invalid Astro version "astro":
"^6.3.3" which does not exist; change that dependency entry to "astro": "^6.3.2"
so npm install succeeds and keeps compatibility with "astro-expressive-code".
Locate the "astro" dependency in package.json and update the version string,
then run npm install (or yarn) and verify lockfile updates.

---

Nitpick comments:
In `@astro.config.mjs`:
- Around line 44-46: The vite configuration block (vite: { plugins:
[tailwind()], }) uses 2-space indentation that conflicts with the file's
tab-based formatting; update the indentation for the vite object, its plugins
property, and the tailwind() line to use tabs so the vite block matches the rest
of the file's tab indentation (look for the vite, plugins, and tailwind() lines
and reformat their leading whitespace to tabs).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1aea9387-2993-4498-8c36-7b36427b5b1d

📥 Commits

Reviewing files that changed from the base of the PR and between 88b332c and 4609ce0.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • astro.config.mjs
  • package.json

Comment thread astro.config.mjs Outdated
Comment thread package.json
Comment thread package.json
@lucperkins lucperkins enabled auto-merge May 14, 2026 19:32
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/QuickStart.astro (1)

19-27: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add an accessible name to the icon-only dismiss button.

This button has no text alternative, so assistive technologies won’t get a clear action label.

♿ Suggested fix
-    <button `@click`="quickStart = false">
+    <button `@click`="quickStart = false" aria-label="Dismiss quick start recommendation">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/QuickStart.astro` around lines 19 - 27, The dismiss button
with the click handler (`@click`="quickStart = false") is icon-only and lacks an
accessible name; add an accessible label by giving the button an aria-label
(e.g., aria-label="Close quick start") or include visually-hidden text inside
the button so screen readers get a clear action name; update the <button>
element in QuickStart.astro (the one wrapping the SVG) to include the chosen
accessible name and ensure it remains stylistically hidden if using visible
text.
src/components/mdx/Admonition.astro (1)

47-59: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make the collapsible header keyboard-accessible.

This toggle is interactive but implemented as a non-focusable <div> with pointer-only interaction, so keyboard users can’t operate it.

♿ Suggested fix
           <div
             class:list={[
               "duration-hover hover:text-dark dark:bg-dark dark:hover:bg-darker-gray dark:hover:text-light-gray flex items-center justify-between rounded px-3 py-1.5 hover:cursor-pointer md:px-3.5 md:py-2 lg:px-4 lg:py-3",
               danger && "border-b-red bg-pale-red hover:bg-middle-red",
               info && "border-b-blue bg-pale-blue hover:bg-middle-blue",
               success && "border-b-green bg-pale-green hover:bg-middle-green",
               warning &&
                 "border-b-yellow bg-pale-yellow hover:bg-middle-yellow",
             ]}
+            role="button"
+            tabindex="0"
+            x-bind:aria-expanded="open"
             `@mouseover`="buttonHover = true"
             `@mouseleave`="buttonHover = false"
             `@click`="toggle()"
+            `@keydown.enter.prevent`="toggle()"
+            `@keydown.space.prevent`="toggle()"
           >
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/mdx/Admonition.astro` around lines 47 - 59, The collapsible
header is a non-focusable <div> and must be made keyboard-accessible: replace or
update the interactive element in Admonition.astro (the container using
`@mouseover/`@mouseleave/@click and calling toggle()) so it is focusable and
exposes proper ARIA — either convert it to a semantic <button type="button"> or
add tabindex="0" role="button", aria-expanded="{open}" and aria-controls="{id}"
if keeping a div; also hook keydown handling to call toggle() on Enter and Space
and map focus/blur to the existing buttonHover logic so keyboard users can see
the same hover/focus styles.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/assets/css/style.css`:
- Around line 127-129: The CSS selector .data-footnote-backref is wrong because
it targets a class instead of the attribute; change the selector to target the
attribute (use [data-footnote-backref]) and keep the same rule (apply
no-underline) so the footnote backlink styling applies to elements with the
data-footnote-backref attribute.
- Around line 51-58: The CSS custom properties --font-sans, --font-serif and
--font-mono contain unquoted single-word font family identifiers flagged by the
value-keyword-case lint rule; update those variables so every single-word font
family name (e.g., Roboto, Arial, Georgia, Cambria, Times, Monaco, Menlo,
Consolas, Monaco, etc.) is wrapped in quotes (leave multi-word names and generic
families like sans-serif, serif, monospace unquoted) to satisfy the linter while
preserving the existing order and commas.

In `@src/components/Dropdown.astro`:
- Line 16: Update the Tailwind opacity utilities removed in v4 by replacing the
old opacity classes with slash-opacity syntax: in Dropdown.astro, change the
class that contains "focus-visible:ring-opacity-75" (used alongside
"focus-visible:ring-2" / "focus-visible:ring-white") to use
"focus-visible:ring-white/75" (or "ring-white/75" with the focus-visible
variant), and change the class that contains "ring-opacity-5" to use
"ring-black/5" instead; edit the class attribute strings where these symbols
appear to swap the old opacity utilities for the new slash syntax.

In `@src/pages/start/`[id].astro:
- Line 26: The route param creation uses page.id.substring(1) which wrongly
truncates characters instead of removing file extensions; replace that with the
same extension-stripping used elsewhere (call or inline the stripExt logic) so
params: { id: stripExt(page.id) } (or equivalent that removes .md/.mdx/.ts
extensions) in src/pages/start/[id].astro, and make the identical fix in the
startPagePath utility in src/lib/utils.ts (use stripExt or the same regex-based
ext removal there) so IDs like 1.install.mdx become 1.install.

---

Outside diff comments:
In `@src/components/mdx/Admonition.astro`:
- Around line 47-59: The collapsible header is a non-focusable <div> and must be
made keyboard-accessible: replace or update the interactive element in
Admonition.astro (the container using `@mouseover/`@mouseleave/@click and calling
toggle()) so it is focusable and exposes proper ARIA — either convert it to a
semantic <button type="button"> or add tabindex="0" role="button",
aria-expanded="{open}" and aria-controls="{id}" if keeping a div; also hook
keydown handling to call toggle() on Enter and Space and map focus/blur to the
existing buttonHover logic so keyboard users can see the same hover/focus
styles.

In `@src/components/QuickStart.astro`:
- Around line 19-27: The dismiss button with the click handler
(`@click`="quickStart = false") is icon-only and lacks an accessible name; add an
accessible label by giving the button an aria-label (e.g., aria-label="Close
quick start") or include visually-hidden text inside the button so screen
readers get a clear action name; update the <button> element in QuickStart.astro
(the one wrapping the SVG) to include the chosen accessible name and ensure it
remains stylistically hidden if using visible text.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 537548e1-5528-4807-aaf3-9703c08df723

📥 Commits

Reviewing files that changed from the base of the PR and between 33d6a09 and 1e47bb6.

📒 Files selected for processing (42)
  • astro.config.mjs
  • src/assets/css/style.css
  • src/components/Banner.astro
  • src/components/Breadcrumb.astro
  • src/components/CookieConsent.astro
  • src/components/Drawer.astro
  • src/components/DrawerToggler.astro
  • src/components/Dropdown.astro
  • src/components/ExternalSources.astro
  • src/components/Features.astro
  • src/components/FeedbackBar.astro
  • src/components/Footer.astro
  • src/components/Header.astro
  • src/components/HeaderLink.astro
  • src/components/Hero.astro
  • src/components/HorizontalContainer.astro
  • src/components/HoverableExternalSourceLink.astro
  • src/components/HoverableLink.astro
  • src/components/Navbar.astro
  • src/components/NixTerms.astro
  • src/components/PageSurvey.tsx
  • src/components/Pagination.astro
  • src/components/QuickStart.astro
  • src/components/Related.astro
  • src/components/Signup.astro
  • src/components/SignupModal.astro
  • src/components/Summary.astro
  • src/components/ThemeSwitcher.astro
  • src/components/mdx/Admonition.astro
  • src/components/mdx/Install.astro
  • src/components/mdx/Languages.astro
  • src/components/mdx/NixStorePath.astro
  • src/components/mdx/Platforms.astro
  • src/content.config.ts
  • src/pages/about.astro
  • src/pages/concepts/[id].astro
  • src/pages/concepts/[id].md.ts
  • src/pages/index.astro
  • src/pages/llms-full.txt.ts
  • src/pages/llms.txt.ts
  • src/pages/start/[id].astro
  • src/pages/start/[id].md.ts
💤 Files with no reviewable changes (2)
  • src/components/HeaderLink.astro
  • src/components/Header.astro
✅ Files skipped from review due to trivial changes (23)
  • src/components/Features.astro
  • src/components/DrawerToggler.astro
  • src/components/FeedbackBar.astro
  • src/pages/index.astro
  • src/components/ThemeSwitcher.astro
  • src/components/HoverableLink.astro
  • src/components/Banner.astro
  • src/components/Summary.astro
  • src/components/PageSurvey.tsx
  • src/components/NixTerms.astro
  • src/components/Related.astro
  • src/components/CookieConsent.astro
  • src/components/Footer.astro
  • src/components/Breadcrumb.astro
  • src/components/Signup.astro
  • src/components/HorizontalContainer.astro
  • src/components/mdx/Languages.astro
  • src/components/mdx/NixStorePath.astro
  • src/components/SignupModal.astro
  • src/components/mdx/Install.astro
  • src/components/HoverableExternalSourceLink.astro
  • src/components/Hero.astro
  • src/components/mdx/Platforms.astro
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/pages/about.astro
  • src/components/Pagination.astro
  • src/pages/concepts/[id].astro
  • astro.config.mjs
  • src/content.config.ts
  • src/pages/start/[id].md.ts

Comment thread src/assets/css/style.css
Comment on lines +51 to +58
--font-sans:
-apple-system, ui-sans-serif, system-ui, BlinkMacSystemFont, "Segoe UI",
Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
--font-mono:
ui-monospace, Menlo, Consolas, SFMono-Regular, Monaco, "Liberation Mono",
"Courier New", monospace;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Quote single-word font family names to satisfy the active lint rule.

Current value-keyword-case settings flag these unquoted identifiers.

🧹 Suggested fix
   --font-sans:
-    -apple-system, ui-sans-serif, system-ui, BlinkMacSystemFont, "Segoe UI",
-    Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
+    -apple-system, ui-sans-serif, system-ui, "BlinkMacSystemFont", "Segoe UI",
+    "Roboto", "Helvetica Neue", "Arial", "Noto Sans", sans-serif,
     "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
-  --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
+  --font-serif: ui-serif, "Georgia", "Cambria", "Times New Roman", "Times", serif;
   --font-mono:
-    ui-monospace, Menlo, Consolas, SFMono-Regular, Monaco, "Liberation Mono",
+    ui-monospace, "Menlo", "Consolas", "SFMono-Regular", "Monaco", "Liberation Mono",
     "Courier New", monospace;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
--font-sans:
-apple-system, ui-sans-serif, system-ui, BlinkMacSystemFont, "Segoe UI",
Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
--font-mono:
ui-monospace, Menlo, Consolas, SFMono-Regular, Monaco, "Liberation Mono",
"Courier New", monospace;
--font-sans:
-apple-system, ui-sans-serif, system-ui, "BlinkMacSystemFont", "Segoe UI",
"Roboto", "Helvetica Neue", "Arial", "Noto Sans", sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-serif: ui-serif, "Georgia", "Cambria", "Times New Roman", "Times", serif;
--font-mono:
ui-monospace, "Menlo", "Consolas", "SFMono-Regular", "Monaco", "Liberation Mono",
"Courier New", monospace;
🧰 Tools
🪛 Stylelint (17.11.0)

[error] 52-52: Expected "BlinkMacSystemFont" to be "blinkmacsystemfont" (value-keyword-case)

(value-keyword-case)


[error] 53-53: Expected "Roboto" to be "roboto" (value-keyword-case)

(value-keyword-case)


[error] 53-53: Expected "Arial" to be "arial" (value-keyword-case)

(value-keyword-case)


[error] 55-55: Expected "Georgia" to be "georgia" (value-keyword-case)

(value-keyword-case)


[error] 55-55: Expected "Cambria" to be "cambria" (value-keyword-case)

(value-keyword-case)


[error] 55-55: Expected "Times" to be "times" (value-keyword-case)

(value-keyword-case)


[error] 57-57: Expected "Menlo" to be "menlo" (value-keyword-case)

(value-keyword-case)


[error] 57-57: Expected "Consolas" to be "consolas" (value-keyword-case)

(value-keyword-case)


[error] 57-57: Expected "SFMono-Regular" to be "sfmono-regular" (value-keyword-case)

(value-keyword-case)


[error] 57-57: Expected "Monaco" to be "monaco" (value-keyword-case)

(value-keyword-case)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/assets/css/style.css` around lines 51 - 58, The CSS custom properties
--font-sans, --font-serif and --font-mono contain unquoted single-word font
family identifiers flagged by the value-keyword-case lint rule; update those
variables so every single-word font family name (e.g., Roboto, Arial, Georgia,
Cambria, Times, Monaco, Menlo, Consolas, Monaco, etc.) is wrapped in quotes
(leave multi-word names and generic families like sans-serif, serif, monospace
unquoted) to satisfy the linter while preserving the existing order and commas.

Comment thread src/assets/css/style.css
Comment on lines +127 to +129
.data-footnote-backref {
@apply no-underline;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix footnote backlink selector to target the actual attribute.

This currently selects a class (.data-footnote-backref) instead of the footnote backlink attribute.

🔧 Suggested fix
-      .data-footnote-backref {
+      [data-footnote-backref] {
         `@apply` no-underline;
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.data-footnote-backref {
@apply no-underline;
}
[data-footnote-backref] {
`@apply` no-underline;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/assets/css/style.css` around lines 127 - 129, The CSS selector
.data-footnote-backref is wrong because it targets a class instead of the
attribute; change the selector to target the attribute (use
[data-footnote-backref]) and keep the same rule (apply no-underline) so the
footnote backlink styling applies to elements with the data-footnote-backref
attribute.

<button
@click="open = !open"
class="text-sm duration-hover hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 md:text-base lg:text-lg"
class="duration-hover hover:text-primary focus-visible:ring-opacity-75 text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-white md:text-base lg:text-lg"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Tailwind CSS v4.3.0, were ring-opacity-*utilities removed in favor of slash opacity syntax (e.g.,ring-black/5, ring-white/75)?

💡 Result:

Yes, in Tailwind CSS v4.3.0, the ring-opacity-* utilities were removed in favor of the slash opacity syntax (e.g., ring-black/5, ring-white/75). This change occurred as part of the v4.0 release, where several opacity utilities were deprecated and removed to simplify the framework and leverage modern CSS features like color-mix() [1][2][3][4]. The official upgrade guide explicitly lists ring-opacity-* as deprecated, recommending replacement with opacity modifiers like ring-black/50 [1]. No changes to this in v4.3.0 release notes, which focus on new utilities like scrollbars and containers [5][6]. Current v4 docs show ring colors using the slash syntax directly, e.g., ring-blue-500/50 [7].

Citations:


🏁 Script executed:

cat -n src/components/Dropdown.astro | head -40

Repository: DeterminateSystems/zero-to-nix

Length of output: 1625


🏁 Script executed:

rg 'ring-opacity|text-opacity|bg-opacity|border-opacity|shadow-opacity' --type astro --type jsx --type tsx --type ts --type js

Repository: DeterminateSystems/zero-to-nix

Length of output: 104


🏁 Script executed:

rg 'ring-opacity|text-opacity|bg-opacity|border-opacity' -g '*.astro' -g '*.tsx' -g '*.jsx' -g '*.ts' -g '*.js'

Repository: DeterminateSystems/zero-to-nix

Length of output: 495


🏁 Script executed:

rg 'tailwindcss' package.json package-lock.json pnpm-lock.yaml yarn.lock

Repository: DeterminateSystems/zero-to-nix

Length of output: 5299


Replace removed Tailwind v4 opacity utilities.

Lines 16 and 25 use focus-visible:ring-opacity-75 and ring-opacity-5, which were removed in Tailwind v4. Replace with slash opacity syntax: ring-white/75 and ring-black/5 respectively.

Suggested fix
-    class="duration-hover hover:text-primary focus-visible:ring-opacity-75 text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-white md:text-base lg:text-lg"
+    class="duration-hover hover:text-primary text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75 md:text-base lg:text-lg"
-    class="ring-opacity-5 dark:border-gray dark:bg-dark absolute top-10 right-0 max-h-[75vh] w-72 origin-top-right overflow-scroll bg-white shadow-md ring-1 ring-black focus:outline-none"
+    class="dark:border-gray dark:bg-dark absolute top-10 right-0 max-h-[75vh] w-72 origin-top-right overflow-scroll bg-white shadow-md ring-1 ring-black/5 focus:outline-none"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class="duration-hover hover:text-primary focus-visible:ring-opacity-75 text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-white md:text-base lg:text-lg"
class="duration-hover hover:text-primary text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75 md:text-base lg:text-lg"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Dropdown.astro` at line 16, Update the Tailwind opacity
utilities removed in v4 by replacing the old opacity classes with slash-opacity
syntax: in Dropdown.astro, change the class that contains
"focus-visible:ring-opacity-75" (used alongside "focus-visible:ring-2" /
"focus-visible:ring-white") to use "focus-visible:ring-white/75" (or
"ring-white/75" with the focus-visible variant), and change the class that
contains "ring-opacity-5" to use "ring-black/5" instead; edit the class
attribute strings where these symbols appear to swap the old opacity utilities
for the new slash syntax.

export const getStaticPaths = (async () => {
return (await getStartPagesByOrderParam()).map((page) => ({
params: { slug: page.slug.substring(1) },
params: { id: page.id.substring(1) },
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== getStartPagesByOrderParam definitions/usages =="
rg -n -C4 '\bgetStartPagesByOrderParam\b' src --type=ts

echo "== startPagePath definitions/usages =="
rg -n -C4 '\bstartPagePath\s*\(' src --type=ts

echo "== id param generation in start/concepts routes =="
rg -n -C3 'params:\s*\{\s*id:' src/pages/start src/pages/concepts

Repository: DeterminateSystems/zero-to-nix

Length of output: 6312


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Looking for stripExt function definition =="
rg -n 'stripExt' src --type=ts --type=js -B2 -A5

echo "== Checking content collection schema/types =="
rg -n 'collection\|schema' src/content --type=ts -B1 -A3

echo "== Checking for ID format patterns in start collection =="
find src/content -name "*.md" -path "*/start/*" | head -5 | while read f; do
  echo "File: $f"
  head -10 "$f"
done

echo "== How startPagePath uses the id parameter =="
rg -n 'startPagePath' src/lib --type=ts -B2 -A8

Repository: DeterminateSystems/zero-to-nix

Length of output: 2791


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Content start directory structure =="
find src/content/start -type f | head -10

echo "== Looking at how Astro collection processes start IDs =="
rg -n 'getCollection.*start' src --type=ts --type=astro -B2 -A2

echo "== Check if page.id is used elsewhere in Astro start route =="
rg -n 'page\.id' src/pages/start --type=astro -B3 -A3

echo "== Look for any comments explaining ID format =="
rg -n 'id.*start|start.*id|slash|prefix' src/pages/start --type=astro --type=ts -i

Repository: DeterminateSystems/zero-to-nix

Length of output: 493


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Searching for astro config and collection setup =="
find src -name "*.config.*" -o -name "astro.config.*" | xargs cat 2>/dev/null || true

echo "== Look for any type definitions or schemas for collections =="
find src -name "*.ts" -o -name "*.astro" | xargs grep -l "CollectionEntry\|collection.*start" 2>/dev/null | head -5

echo "== Check startPagePath utility function =="
cat src/lib/utils.ts 2>/dev/null | head -100

echo "== Examine the Astro start route more thoroughly =="
cat -n src/pages/start/[id].astro

Repository: DeterminateSystems/zero-to-nix

Length of output: 5443


Fix substring(1) to properly remove file extension from start page IDs.

Line 26 uses substring(1) to remove the first character, but Astro's glob loader produces IDs like 1.install.mdx. This generates route params like .install.mdx instead of 1.install. Match the Markdown route's approach and use extension stripping (as in src/pages/start/[id].md.ts with stripExt), not character truncation.

Note: The same bug exists in src/lib/utils.ts in the startPagePath utility function.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/start/`[id].astro at line 26, The route param creation uses
page.id.substring(1) which wrongly truncates characters instead of removing file
extensions; replace that with the same extension-stripping used elsewhere (call
or inline the stripExt logic) so params: { id: stripExt(page.id) } (or
equivalent that removes .md/.mdx/.ts extensions) in src/pages/start/[id].astro,
and make the identical fix in the startPagePath utility in src/lib/utils.ts (use
stripExt or the same regex-based ext removal there) so IDs like 1.install.mdx
become 1.install.

@cole-h
Copy link
Copy Markdown
Member

cole-h commented May 14, 2026

I did a little bit of clicking around in the deploy preview, and I noticed just one weirdness:

Some headings look a little different than they used to:

(new)
image

(old)
image

(look at the bottom of both images: one heading is underlined and shorter than the other)

URL to reproduce: https://deploy-preview-534--zero-to-nix.netlify.app/concepts/nix-installer/ vs https://zero-to-nix.com/concepts/nix-installer/

@lucperkins
Copy link
Copy Markdown
Member Author

Copy link
Copy Markdown
Member

@cole-h cole-h left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me in the preview, thanks!

@lucperkins lucperkins merged commit 09042ad into main May 16, 2026
8 of 9 checks passed
@lucperkins lucperkins deleted the astro-v6 branch May 16, 2026 01:06
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.

2 participants