Skip to content

Commit 4f696f4

Browse files
mrtnzlmlclaude
andcommitted
chore: complete Astro 7 migration, bump CI actions
Drop the `compressHTML: true` pin from ac977de and adopt Astro 7's `'jsx'` default, after auditing every whitespace boundary it changes. Of 607 boundaries where 'jsx' removes whitespace the v6 build emitted, 604 are benign: the two text runs are separated by a block-level box, or are flex/grid items that space themselves with `gap-*` (nav, footer, breadcrumbs, chip rows). The mobile menu looks like a regression in the markup but is not — it is `hidden flex-col gap-4` and gets `display:flex` from the JS-toggled `.nav-open` class, so its links are flex items. The remaining 3 are real, all the same pattern in Pricing: `{plan.perLesson} USD` followed by a newline and an inline unit <span>, which 'jsx' welds into "24 USD/ lesson". Fixed with an explicit `{" "}`. <EsTip> is fine as written — every usage already sits on one line with its surrounding text — but it is inline-block in running prose, so it is the most likely thing to break next. Both it and the price line are now covered by a test asserting rendered spacing, a property nothing else in the suite read. Verified the test fails ("24 USD/ lesson") when the fix is reverted. Net: rendered text is identical to the pre-upgrade build apart from the intended Pricing fix, structure is unchanged, and HTML shrinks ~2.9 KB. Also bump actions/checkout and actions/setup-node v4 → v7, clearing the CI annotation about being force-run on Node 24. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent ac977de commit 4f696f4

5 files changed

Lines changed: 38 additions & 11 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ jobs:
1010
test:
1111
runs-on: ubuntu-latest
1212
steps:
13-
- uses: actions/checkout@v4
13+
- uses: actions/checkout@v7
1414

15-
- uses: actions/setup-node@v4
15+
- uses: actions/setup-node@v7
1616
with:
1717
node-version: 22
1818
cache: npm

CLAUDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Requires Node.js >=22.12.0. Deployed to Cloudflare Workers Builds: push to `main
1919

2020
**Static site built with Astro 7 + Tailwind CSS 4.** Zero client-side JavaScript by default — games use inline `<script>` blocks with vanilla JS, no framework hydration. The homepage additionally ships three tiny progressive-enhancement scripts (seasonal papel-picado swap, daily word-of-the-day, EsTip width measurement); with JS disabled it renders the year-round banner, the fallback word, and Spanish-sized swap boxes.
2121

22+
**Whitespace (Astro 7):** `compressHTML` defaults to `'jsx'`, so whitespace and line breaks *around* elements are stripped; whitespace within a single line is kept. Putting an inline element (`<span>`, `<a>`, `<EsTip>`) on its own source line inside running text welds the words together — `24 USD` + newline + `<span>/ lesson</span>` renders as `24 USD/ lesson`. Keep the inline element on the same line as its neighbouring text, or add an explicit `{" "}`. This only bites inline elements: flex/grid children (nav, footer, breadcrumbs, chip rows) space themselves with `gap-*` and are unaffected. The `whitespace survives around inline elements split across source lines` test in `tests/build.test.ts` guards this.
23+
2224
### Key directories
2325

2426
- `src/pages/` — File-based routing. Each `.astro` file = one page.

astro.config.mjs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,6 @@ function sharedMtime() {
7373
export default defineConfig({
7474
site: "https://elenkaspanish.com",
7575

76-
// Astro 7 switched the default to 'jsx', which strips whitespace and line
77-
// breaks around elements — that silently closes up the gaps between inline
78-
// elements written across separate lines (nav links, footer links, the
79-
// inline-block <EsTip> spans). `true` is the pre-v7 lossless mode, which
80-
// preserves whitespace where it affects visual rendering. Keep it until the
81-
// markup is audited for JSX whitespace rules.
82-
compressHTML: true,
83-
8476
vite: {
8577
plugins: [tailwindcss()],
8678
},

src/components/Pricing.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ const courseJsonLd = {
165165

166166
<div class="mb-4">
167167
<p class="text-3xl font-bold text-primary">
168-
{plan.perLesson} USD
168+
{plan.perLesson} USD{" "}
169169
<span class="text-sm font-normal text-gray-500">/ {plan.group ? "student / " : ""}lesson</span>
170170
</p>
171171
<p class="text-xs text-gray-500 mt-1">

tests/build.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,39 @@ describe("design system (visual uplift)", () => {
11861186
expect(planCards.length).toBe(3);
11871187
planCards.each((_, el) => expect($(el).attr("class")).toContain("bg-white"));
11881188
});
1189+
// Astro 7's `compressHTML: 'jsx'` strips whitespace and line breaks around
1190+
// elements. Where an inline element sits on its own source line inside a
1191+
// block of text, that silently welds the two runs together ("24 USD/ lesson").
1192+
// Nothing else in this suite reads rendered spacing, so these assert it
1193+
// directly for the inline elements embedded in prose. A failure here means a
1194+
// source line needs an explicit `{" "}`.
1195+
it("whitespace survives around inline elements split across source lines", () => {
1196+
const $ = readPage("/");
1197+
// Price + its inline unit <span> (Pricing.astro).
1198+
const prices = $("#pricing p.text-3xl")
1199+
.map((_, el) => $(el).text().replace(/\s+/g, " ").trim())
1200+
.get();
1201+
expect(prices).toEqual([
1202+
"24 USD / lesson",
1203+
"18 USD / student / lesson",
1204+
"14 USD / student / lesson",
1205+
]);
1206+
// <EsTip> renders inline-block inside running text, so the words on either
1207+
// side must stay separated.
1208+
$(".es-tip").each((_, el) => {
1209+
const parent = $(el).parent().text().replace(/\s+/g, " ");
1210+
const es = $(el).find(".es-tip-es").first().text().trim();
1211+
if (!es || !parent.includes(es)) return;
1212+
const i = parent.indexOf(es);
1213+
const before = parent[i - 1];
1214+
const after = parent[i + es.length];
1215+
// A neighbouring character must be whitespace or punctuation — never a
1216+
// letter, which would mean two words ran together.
1217+
if (before !== undefined) expect(before).not.toMatch(/\p{L}/u);
1218+
if (after !== undefined) expect(after).not.toMatch(/\p{L}/u);
1219+
});
1220+
});
1221+
11891222
it("the university proper noun is NOT tooltip-wrapped", () => {
11901223
const $ = readPage("/");
11911224
const uni = $("span[lang='es']:contains('Universidad')");

0 commit comments

Comments
 (0)