From 7ded010b71ebe79305093045fab0b6ebe0cad45e Mon Sep 17 00:00:00 2001 From: Eric Dahl Date: Wed, 6 May 2026 13:22:28 -0400 Subject: [PATCH 01/11] chore(content): add src/content/_index.md --- src/content/_index.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/content/_index.md diff --git a/src/content/_index.md b/src/content/_index.md new file mode 100644 index 0000000..1f138e2 --- /dev/null +++ b/src/content/_index.md @@ -0,0 +1,13 @@ +--- +title: "Spacebot Blog" +pubDate: "2026-05-06T17:17:00Z" +description: "Engineering notes and practical guides for creators building fast, readable web experiences." +tags: [blog, homepage, engineering] +readingTime: "1 min" +--- + +Tagline: "Ship smaller—iterate faster." + +Spacebot Blog is a concise engineering-first collection of notes, case studies, and design decisions for engineers and creators building lightweight, testable web experiences. Expect short technical posts, practical patterns, and the reasoning behind small, high-impact iterations. + +Target audience: frontend and tooling engineers, indie creators, and designers who ship code and care about readability, performance, and reproducible builds. From fe489e1179e93081208f72d245d20e9150a24a36 Mon Sep 17 00:00:00 2001 From: Eric Dahl Date: Wed, 6 May 2026 13:22:28 -0400 Subject: [PATCH 02/11] chore(content): add src/content/posts/ship-small-iterate-fast.md --- src/content/posts/ship-small-iterate-fast.md | 74 ++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/content/posts/ship-small-iterate-fast.md diff --git a/src/content/posts/ship-small-iterate-fast.md b/src/content/posts/ship-small-iterate-fast.md new file mode 100644 index 0000000..83e9792 --- /dev/null +++ b/src/content/posts/ship-small-iterate-fast.md @@ -0,0 +1,74 @@ +--- +title: "Ship small, iterate fast: tiny redesign, big signal" +pubDate: "2026-05-06T17:17:00Z" +description: "Case study of a focused tiny redesign and the outcomes it unlocked." +tags: [case-study, design, ux] +readingTime: "6 min" +slug: "ship-small-iterate-fast" +--- + +Small changes often produce the clearest signal. This case study walks through a tiny redesign shipped as a single PR: what we changed, why we scoped it that way, and how we measured impact. + +Why scope small? + +Large redesigns carry coordination overhead, long review cycles, and high risk. By contrast, a focused, well-scoped change is easier to review, faster to iterate on, and simpler to revert if it regresses. For content-driven sites, small visual and navigation improvements can yield measurable engagement gains without a full rewrite. + +The problem + +Our static site showed three small, high-impact issues: + +- Inconsistent vertical rhythm across headings and paragraphs caused uneven reading flow. +- Dark-mode body text used a slightly desaturated blue that read as low contrast in some lighting. +- The blog link was nested inside a secondary menu, reducing discoverability. + +The scope + +To keep the PR reviewable, we limited changes to three files: + +1. root CSS variables (spacing + color tokens) +2. theme CSS for dark mode color tweak +3. header/nav markup to promote the blog link + +Implementation details + +Spacing and rhythm + +We introduced two variables: --sp-base (8px) and --sp-rhythm (calc(var(--sp-base) * 2)). They controlled margins on headings and paragraphs so a single token tweak propagated cleanly: + +```css +:root { --sp-base: 8px; --sp-rhythm: calc(var(--sp-base) * 2); } +h1, h2, p { margin-top: var(--sp-rhythm); margin-bottom: var(--sp-rhythm); } +``` + +Dark-mode contrast + +Rather than choose a pure white for body text, we picked #e6eef7 — slightly desaturated — to reduce glare while preserving a contrast ratio > 7:1 for normal text sizes. This small change improved legibility without altering the overall aesthetic. + +Navigation + +We promoted the blog link to a primary nav slot and added an underline-on-focus rule to improve keyboard discoverability. The markup change was one-line and the focus style was a 2-line CSS addition. + +Testing and review + +Because the PR touched CSS and nav markup, we ran the static site's smoke tests and the link-checker locally (linkinator) before opening the PR. We added a screenshot snapshot for the header to visually confirm the nav placement across breakpoints. The PR description included the rationale, a before/after screenshot, and a short QA checklist. + +Outcomes + +- The PR merged in under 24 hours after two quick reviews. +- Initial telemetry (first week) showed an ~18% increase in clicks to /blog and a 7% decrease in bounce on long-form posts. +- No regressions were reported in cross-browser smoke tests. + +Lessons learned + +- Small scope wins: keep PRs tightly focused and explain the user problem clearly in the description. +- Make tiny visual tokens reusable: spacing and color variables make future small adjustments trivial. +- Measure quickly: even simple telemetry (clicks, bounce, screenshots) helps validate assumptions. + +Next steps + +- Iterate on rhythm if longer-form posts still feel cramped. +- Continue to centralize tokens (typography, colors) so future tiny PRs stay small. + +Takeaway + +Ship a single, well-scoped change targeting a clear user problem. Fast iteration uncovers signal early and keeps the codebase healthy. From 81fd61d0fd82faf49c4bdb5ab0bc342dad60b5b9 Mon Sep 17 00:00:00 2001 From: Eric Dahl Date: Wed, 6 May 2026 13:22:30 -0400 Subject: [PATCH 03/11] chore(content): add src/content/posts/tdd-for-content-ui.md --- src/content/posts/tdd-for-content-ui.md | 70 +++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/content/posts/tdd-for-content-ui.md diff --git a/src/content/posts/tdd-for-content-ui.md b/src/content/posts/tdd-for-content-ui.md new file mode 100644 index 0000000..856471e --- /dev/null +++ b/src/content/posts/tdd-for-content-ui.md @@ -0,0 +1,70 @@ +--- +title: "TDD for content & UI: tests for a static blog" +pubDate: "2026-05-06T17:17:00Z" +description: "Why tests matter even for static content and a practical link-checking setup." +tags: [testing, ci, content] +readingTime: "6 min" +slug: "tdd-for-content-ui" +--- + +Tests aren't just for application logic. For a static blog, regressions come from broken links, missing assets, and layout shifts introduced by CSS changes. Treat content as code: write tests that exercise the things readers rely on. + +What to test + +- Link validity (internal and external) +- Frontmatter presence and required fields +- Basic accessibility smoke checks (alt on images, headings order) +- Build output sanity (no orphaned 404 pages) + +Example: simple link-check script + +Use a link-checker that crawls the built site after your static build step. Here's a minimal node script using linkinator (install as dev dep): + +```bash +# package.json devDeps: "linkinator" +npx linkinator ./public --concurrency 10 --timeout 5000 +``` + +In CI, run: + +1. npm run build +2. npx linkinator ./public --failLevel error + +This fails the build when links are broken — a fast guardrail for authors. + +Frontmatter linting + +Simple frontmatter lints can be done with a tiny Node script or existing tools. Example rule set: + +- title: required +- pubDate: required and ISO-8601 +- description: required +- tags: array + +Minimal JS check (pseudo): + +```js +const glob = require('glob'); +const fm = require('front-matter'); +const fs = require('fs'); + +glob.sync('src/content/posts/**/*.md').forEach(file => { + const raw = fs.readFileSync(file,'utf8'); + const { attributes } = fm(raw); + if (!attributes.title || !attributes.pubDate) throw new Error(file + ' missing frontmatter'); +}); +``` + +Why this matters + +Content regressions are visible and erode trust quickly. Tests keep the author flow fast — they surface problems before a PR is merged and make small changes low-cost. + +Integration tips + +- Run link checks after the build step (not before) +- Cache external checks where possible (rate limits) +- Allow a triage mode for flaky external links: mark as warning, not fail, if needed + +Takeaway + +Treat your blog's content pipeline like code: minimal tests that run fast will save time and keep the site reliable. From a684c191e075b344cce7dab34bc21fc72f3ee601 Mon Sep 17 00:00:00 2001 From: Eric Dahl Date: Wed, 6 May 2026 13:22:31 -0400 Subject: [PATCH 04/11] chore(content): add src/content/posts/minimalist-dark-first-design.md --- .../posts/minimalist-dark-first-design.md | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/content/posts/minimalist-dark-first-design.md diff --git a/src/content/posts/minimalist-dark-first-design.md b/src/content/posts/minimalist-dark-first-design.md new file mode 100644 index 0000000..22d5ae6 --- /dev/null +++ b/src/content/posts/minimalist-dark-first-design.md @@ -0,0 +1,56 @@ +--- +title: "Minimalist dark-first design: choices & tradeoffs" +pubDate: "2026-05-06T17:17:00Z" +description: "Design decisions for a dark-first, minimalist blog focused on readability and code." +tags: [design, ux, accessibility] +readingTime: "6 min" +slug: "minimalist-dark-first-design" +--- + +Dark-first doesn't mean high-contrast harshness. It's an intentional palette that prioritizes long-form readability, code legibility, and low visual noise. + +Key choices + +1. Color scale and contrast +- Use a muted high-contrast body color: off-white (#e6eef7) on a warm near-black background (#0b0f14). +- Reserve pure white for accents and CTA backgrounds. + +2. Typography & rhythm +- Large base font-size (18px) with a 1.45 line-height for comfortable reading. +- Rhythm variables: --sp-base: 8px; --sp-rhythm: calc(var(--sp-base) * 2); + +3. Code presentation +- Use a mono font with clear weight (e.g., JetBrains Mono or Menlo) +- Add subtle background for code blocks, with a thin border and 8px padding. + +Example CSS snippets + +```css +:root { + --bg: #0b0f14; + --text: #e6eef7; + --muted: #8b95a4; + --sp-base: 8px; +} + +pre, code { + background: rgba(255,255,255,0.03); + border-radius: 6px; + padding: 8px; +} +``` + +Tradeoffs + +- Dark themes can reduce eye strain in low-light but may reduce scan speed in bright environments. +- Higher contrast for body text helps readability but must avoid glare — slightly desaturated light text is often a sweet spot. +- Developer-focused sites benefit from visible code but must balance line length and horizontal scrolling. + +Accessibility + +- Always check color contrast ratios for body text and small UI elements +- Provide a light-mode toggle, saved in localStorage, to honor user preference + +Takeaway + +Design minimal, test the reading experience, and keep code presentation crisp. Small, deliberate choices beat large, undecided redesigns. From d06e5cff840e12ec08407726285ed7c047892589 Mon Sep 17 00:00:00 2001 From: Eric Dahl Date: Wed, 6 May 2026 13:33:27 -0400 Subject: [PATCH 05/11] chore(content): update src/content/_index.md --- src/content/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/_index.md b/src/content/_index.md index 1f138e2..565625b 100644 --- a/src/content/_index.md +++ b/src/content/_index.md @@ -6,7 +6,7 @@ tags: [blog, homepage, engineering] readingTime: "1 min" --- -Tagline: "Ship smaller—iterate faster." +Tagline: "Ship smaller — iterate faster." Spacebot Blog is a concise engineering-first collection of notes, case studies, and design decisions for engineers and creators building lightweight, testable web experiences. Expect short technical posts, practical patterns, and the reasoning behind small, high-impact iterations. From 1ecb4c78ac85a94071a7a76501bf927669549bc1 Mon Sep 17 00:00:00 2001 From: Eric Dahl Date: Wed, 6 May 2026 13:33:28 -0400 Subject: [PATCH 06/11] chore(content): update src/content/posts/ship-small-iterate-fast.md --- src/content/posts/ship-small-iterate-fast.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/content/posts/ship-small-iterate-fast.md b/src/content/posts/ship-small-iterate-fast.md index 83e9792..e993665 100644 --- a/src/content/posts/ship-small-iterate-fast.md +++ b/src/content/posts/ship-small-iterate-fast.md @@ -4,6 +4,7 @@ pubDate: "2026-05-06T17:17:00Z" description: "Case study of a focused tiny redesign and the outcomes it unlocked." tags: [case-study, design, ux] readingTime: "6 min" +draft: true slug: "ship-small-iterate-fast" --- From 998bfd68ac51aaa834467b25621c4b87f302c65b Mon Sep 17 00:00:00 2001 From: Eric Dahl Date: Wed, 6 May 2026 13:33:30 -0400 Subject: [PATCH 07/11] chore(content): update src/content/posts/tdd-for-content-ui.md --- src/content/posts/tdd-for-content-ui.md | 80 ++++++++++++++++--------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/src/content/posts/tdd-for-content-ui.md b/src/content/posts/tdd-for-content-ui.md index 856471e..ba363c6 100644 --- a/src/content/posts/tdd-for-content-ui.md +++ b/src/content/posts/tdd-for-content-ui.md @@ -3,68 +3,92 @@ title: "TDD for content & UI: tests for a static blog" pubDate: "2026-05-06T17:17:00Z" description: "Why tests matter even for static content and a practical link-checking setup." tags: [testing, ci, content] -readingTime: "6 min" +readingTime: "7 min" +draft: true slug: "tdd-for-content-ui" --- -Tests aren't just for application logic. For a static blog, regressions come from broken links, missing assets, and layout shifts introduced by CSS changes. Treat content as code: write tests that exercise the things readers rely on. +Tests aren't just for application logic. For a static blog, regressions come from broken links, missing assets, and layout shifts introduced by CSS changes. Treat content as code: write tests that exercise the things readers rely on and make small changes safe to ship. What to test - Link validity (internal and external) - Frontmatter presence and required fields -- Basic accessibility smoke checks (alt on images, headings order) -- Build output sanity (no orphaned 404 pages) +- Basic accessibility smoke checks (alt on images, heading order) +- Build output sanity (no orphaned 404 pages, expected routes) +- Visual snapshots for critical UI like the header/nav and article layout -Example: simple link-check script +Test tooling and flow -Use a link-checker that crawls the built site after your static build step. Here's a minimal node script using linkinator (install as dev dep): +The fastest guardrail is a link checker that runs after your static build. You should also add a frontmatter lint step and a couple of lightweight accessibility checks. Keep tests fast — a slow pipeline discourages iteration. + +Example: link checks with linkinator + +Install as a dev dependency and run against the published output directory. This catches 404s, malformed URLs, and server errors from external sites: ```bash -# package.json devDeps: "linkinator" -npx linkinator ./public --concurrency 10 --timeout 5000 +npm install --save-dev linkinator +npm run build # produces ./public or ./dist depending on your setup +npx linkinator ./public --concurrency 10 --timeout 5000 --failLevel error ``` -In CI, run: +This fails CI on link-level errors. To avoid flakiness from transient external failures, consider: -1. npm run build -2. npx linkinator ./public --failLevel error - -This fails the build when links are broken — a fast guardrail for authors. +- caching results for external domains +- treating external-only failures as warnings on the first failure and failing only on persistent errors +- maintaining a small allowlist of known flaky domains Frontmatter linting -Simple frontmatter lints can be done with a tiny Node script or existing tools. Example rule set: - -- title: required -- pubDate: required and ISO-8601 -- description: required -- tags: array - -Minimal JS check (pseudo): +Frontmatter fields are contract points between authoring and rendering. A missing or malformed pubDate, for example, can break RSS generation or sorting. A minimal Node script using front-matter will keep you honest: ```js const glob = require('glob'); const fm = require('front-matter'); const fs = require('fs'); +const required = ['title','pubDate','description']; + glob.sync('src/content/posts/**/*.md').forEach(file => { const raw = fs.readFileSync(file,'utf8'); const { attributes } = fm(raw); - if (!attributes.title || !attributes.pubDate) throw new Error(file + ' missing frontmatter'); + required.forEach(k => { if (!attributes[k]) throw new Error(`${file} missing ${k}`); }); + if (isNaN(Date.parse(attributes.pubDate))) throw new Error(`${file} has invalid pubDate`); }); ``` +Accessibility & visual checks + +Run a couple of axe-core rules against a headless build snapshot or add `eslint-plugin-jsx-a11y` if your site generates React code. Small automated checks to include: + +- images have alt text +- form controls are labelled +- headings increase logically (H1 → H2 → H3) + +Add a visual snapshot for the header and article template using a tiny puppeteer script or Playwright. If the header/nav changes unexpectedly in a PR, the snapshot diff is a fast signal. + +CI integration suggestions + +- Execute linters and frontmatter checks in parallel to keep runtime low. +- Run link checks after build but before publishing artifacts (so failures block merges). +- Keep external checks optional or run them in a separate stage that can be retried without blocking faster unit-style checks. + +Handling false positives and flaky external links + +- Provide a `--allow` file for known flaky external links and mark them as warnings. +- Offer a quick triage workflow: when a link check fails, open an issue that references the PR and mark it for follow-up. + Why this matters -Content regressions are visible and erode trust quickly. Tests keep the author flow fast — they surface problems before a PR is merged and make small changes low-cost. +Content regressions are visible and erode trust quickly. Tests keep the author flow fast — they surface problems before a PR is merged and make small changes low-cost. A lean test suite (link checks + frontmatter + minimal accessibility) catches most content problems and keeps CI green without slowing down contributors. -Integration tips +Actionable checklist for maintainers -- Run link checks after the build step (not before) -- Cache external checks where possible (rate limits) -- Allow a triage mode for flaky external links: mark as warning, not fail, if needed +- [ ] Add link check step after build +- [ ] Add frontmatter linting step +- [ ] Add a visual snapshot for header and article page +- [ ] Configure external-link policy (fail/warn) Takeaway -Treat your blog's content pipeline like code: minimal tests that run fast will save time and keep the site reliable. +Treat your blog's content pipeline like code: minimal, fast tests surface real problems early and protect the reader experience while preserving a rapid edit → review → merge loop. From 461302af6b3fed3ab766cd8ea76b3f3a61198675 Mon Sep 17 00:00:00 2001 From: Eric Dahl Date: Wed, 6 May 2026 13:33:31 -0400 Subject: [PATCH 08/11] chore(content): update src/content/posts/minimalist-dark-first-design.md --- src/content/posts/minimalist-dark-first-design.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/content/posts/minimalist-dark-first-design.md b/src/content/posts/minimalist-dark-first-design.md index 22d5ae6..da3e5f1 100644 --- a/src/content/posts/minimalist-dark-first-design.md +++ b/src/content/posts/minimalist-dark-first-design.md @@ -4,6 +4,7 @@ pubDate: "2026-05-06T17:17:00Z" description: "Design decisions for a dark-first, minimalist blog focused on readability and code." tags: [design, ux, accessibility] readingTime: "6 min" +draft: true slug: "minimalist-dark-first-design" --- From 474218e7b913e6df72fd271c43bc468bc9832aa9 Mon Sep 17 00:00:00 2001 From: Eric Dahl Date: Wed, 6 May 2026 16:32:56 -0400 Subject: [PATCH 09/11] ci: use root-relative blog links --- src/pages/index.astro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/index.astro b/src/pages/index.astro index 4af444f..bff5a58 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -9,7 +9,7 @@ const posts = await getCollection('blog'); From 9c51ba4cd2de857ddc3c6438a144119344dc8aee Mon Sep 17 00:00:00 2001 From: Eric Dahl Date: Wed, 6 May 2026 16:35:56 -0400 Subject: [PATCH 10/11] ci: use GitHub Pages deploy action --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6b87f1..f396a33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,10 +43,10 @@ jobs: - name: Build run: npm run build - - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 + - name: Upload pages artifact + uses: actions/upload-pages-artifact@v3 with: - github_token: \ - \ - ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./dist + path: ./dist + + - name: Deploy pages + uses: actions/deploy-pages@v4 From 0f43e6ec83b69314eaa7f82f56271a78486144ad Mon Sep 17 00:00:00 2001 From: Eric Dahl Date: Wed, 6 May 2026 16:36:17 -0400 Subject: [PATCH 11/11] ci: trigger workflow run --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8af6f97..273fd20 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ Updated Toggled REEADME +