Frontend audit and hardening
The working report from auditing and hardening mager.co with Codex and Impeccable: findings, fixes, an Astro 7 migration, and the checks behind them.
October 1, 2026. Scope: the shared publication shell, homepage feed, search, article layout, cooking interactions, and Life theme. This is a code and browser audit of the local production build, not a WCAG conformance certification or a full security assessment.
The core publication is distinctive and readable. The most consequential defects were the search overlay's keyboard and resize behavior, inaccessible recipe checkboxes, fragile browser storage handling, and insufficient metadata contrast. These are fixed. The follow-up completes the Astro 7 migration, dependency remediation, control sizing, blockquote cleanup, and expanded responsive/accessibility checks. npm now reports zero vulnerabilities.
Anti-pattern verdict
Pass for the audited surfaces. The personal newspaper structure fits a literal publication and uses real authored content and photographs. Preserving its existing fonts is appropriate. The old search overlay did contain decorative blur, glowing results, and thick colored side borders. Its replacement uses the publication palette, plain separators, and a native dialog. Blockquotes now use full borders or horizontal rules. The About page also loses its decorative blur and panel stripes.
Health score
Scores are reviewer judgments on a 0–4 scale, based on the sampled surfaces. A score of 3 does not mean every page or assistive technology was tested.
| Dimension | Before | After | Finding |
|---|---|---|---|
| Accessibility | 2 | 3 | Search semantics and focus, recipe labels, and measured contrast corrected. |
| Performance | 3 | 3 | Static delivery and lazy supporting images are sound; unnecessary shared font requests removed. No lab performance trace captured. |
| Responsive design | 2 | 3 | Sampled pages fit 320px, including doubled text checks. About sizing and homepage shelf wrapping corrected. |
| Theming | 2 | 3 | Search follows publication themes, selection contrast fixed, cooking uses the shared saved theme. Category systems still differ. |
| Anti-patterns | 2 | 3 | Search ornament and legacy blockquote side borders removed. |
| Total | 11/20 | 15/20 | Good: sampled implementation verified. |
Prioritized findings
All fourteen original grouped findings are resolved (five P1, seven P2, two P3). Expanded checks also corrected archive metadata contrast, inline-link underlines, recipe keyboard-hint contrast, About overflow, and homepage shelf wrapping. No P0 blocker found. Locations refer to the revised source; descriptions record the original defect.
| Priority | Location | Category and impact | Resolution and suggested command |
|---|---|---|---|
| P1 | src/components/SecretSearch.astro:6 |
Accessibility: the input lacked a label, the overlay lacked dialog semantics, and focus could reach background controls. Relevant to WCAG 2.4.3 and 4.1.2. | Fixed with a named native dialog, labeled input, Tab containment, Escape/close controls, focus restoration, and result announcements. impeccable harden. |
| P1 | src/components/SecretSearch.astro:87 |
Responsive: opening search at desktop width then resizing below 1024px hid it while retaining the body scroll lock. | Fixed: dialog remains visible and scrollable across viewport sizes; closing restores the previous overflow state. impeccable harden. |
| P1 | src/styles/cooking.css, src/styles/life.css, src/layouts/LifePost.astro, src/styles/newspaper.css:43 |
Accessibility/theming: muted text, colored badges, and selected text fell below readable contrast. Normal text relates to WCAG 1.4.3. | Fixed foreground tokens and badge colors. See measured pairs below. impeccable harden. |
| P1 | src/scripts/recipe.ts:14, src/layouts/CookingPost.astro:952 |
Accessibility: custom ingredient checkboxes had no accessible name; the recipe page lacked a main landmark and skip link. Relevant to WCAG 4.1.2 and bypass navigation. | Fixed with native inputs wrapped in labels, full-row labels, a main landmark, and Skip to recipe. impeccable harden. |
| P1 | package-lock.json |
Dependency resilience: npm reported ten affected packages. Package severity alone does not demonstrate an exploitable site vulnerability. | Fixed: Astro 7.3.5, MDX 8.0.2, updated sitemap, and a regenerated lockfile. Clean install and npm audit report zero vulnerabilities. impeccable harden, followed by impeccable audit. |
| P2 | src/components/SecretSearch.astro:16 |
Input handling: interpolating titles into result HTML could interpret authored markup instead of displaying literal text. | Fixed with escaped JSON serialization and DOM textContent; slugs are encoded by path segment. impeccable harden. |
| P2 | src/scripts/recipe.ts:1 |
Error handling: blocked storage or malformed saved JSON aborted recipe initialization. | Fixed with guarded reads/writes and validation of object keys and boolean values. Controls retain in-memory behavior. Removed the obsolete separate cooking-theme restore. impeccable harden. |
| P2 | src/scripts/recipe.ts:49 |
State consistency: clearing ingredient checks removed storage but left the saved object populated, so checking another ingredient could resurrect old checks after reload. | Fixed by resetting the in-memory object and persisted state together. impeccable harden. |
| P2 | src/scripts/recipe.ts:38 |
Error handling: missing or denied clipboard access produced no useful feedback. | Fixed with a visible polite status, manual-copy guidance, and a disabled button during the request. impeccable harden. |
| P2 | src/scripts/recipe.ts:72 |
Resource/motion resilience: wake locks were never explicitly released when cook mode ended; scrolling always animated. | Fixed with tracked acquisition/release, late-resolution handling, visibility handling, reduced-motion scrolling, and exposed pressed state. impeccable harden. |
| P2 | src/scripts/recipe.ts:120, src/components/ThemeToggle.astro:1 |
Interaction semantics: shortcuts could intercept modified keys and target the wrong adjacent recipe when only one existed; the theme control's accessible name disagreed with its displayed action. | Fixed shortcut guards and explicit rel targets; the theme button now announces its current action. impeccable harden. |
| P2 | src/components/BaseHead.astro:44 |
Performance: the shared head requested unused Syne and globally duplicated the category-specific Playfair stylesheet; it preloaded Atkinson even on pages that do not use it. | Removed those shared requests. Cooking retains its own Playfair request; Atkinson remains available through its font-face declarations. No quantitative speedup claimed. impeccable optimize. |
| P3 | src/styles/reading.css:54 |
Responsive comfort: the code-copy control is 36px high, below the project's 44px comfort target. This alone is not a WCAG 2.2 AA target-size failure. | Fixed: code-copy controls measure 44px high; contents and source links also receive a 44px minimum. impeccable adapt. |
| P3 | src/styles/reading.css:43, src/styles/global.css:252, src/styles/cooking.css:263, src/layouts/LifePost.astro |
Anti-pattern: thick colored blockquote side borders persist across legacy styles, reducing visual consistency with the shared publication. | Fixed with full borders or horizontal rules using existing theme tokens. Verified in impeccable polish. |
Contrast evidence
Calculated from the actual foreground/background tokens using relative luminance. These are explicit color pairs, not a full-page automated contrast scan.
| Pair | Before | After |
|---|---|---|
| Life metadata on white cards | 3.68:1 | 6.77:1 |
| Cooking muted text on dark surface | 2.77:1 | 6.40:1 |
| Cooking muted text on light surface | 4.02:1 | 5.55:1 |
| Life orange badge text | 2.80:1 | 5.63:1 |
| Dark publication text selection | 1.40:1 | 10.22:1 |
Systemic patterns and positive findings
Browser APIs were treated as always available in cooking interactions. Storage, clipboard access, and wake locks are now optional enhancements rather than prerequisites. Separate category palettes also accumulated foreground/background mismatches; the token-level corrections apply across each affected category.
Keep the full static feed and ordinary links as the baseline. Feed filters enhance complete server-rendered content, supporting images are lazy loaded, broken feed images retain descriptive links, and benchmark loading validates its response and preserves a labeled build snapshot on failure. Publication dates use explicit time zones. Existing reduced-motion support and visible focus styles provide a useful base.
Migration notes
Astro 7 uses the new Markdown processor. The configuration explicitly preserves disabled smart punctuation and HTML compression, and Zod now imports from astro/zod, following the official Astro 6 and Astro 7 migration guides. Node must be at least 22.19.0 because of a transitive dependency; .nvmrc pins the tested 22.23.3 patch.
The generated route set retains all 180 HTML files (174 pages plus redirects). Normalized body-text comparisons passed for 98 tech/note article bodies. The new loader exposed previously unspecified ordering for entries with identical dates. Tech and Notes archives and their adjacent-post links now use explicit ID tie breakers; the Tech lead also prefers the latest publication date when update dates tie. Some same-day adjacent links consequently change; authored article text does not.
Verification
- Clean
npm ci,npm test,npm run build,npm audit, andgit diff --check: passed on Node 22.23.3. Build generated 174 pages; npm reports zero vulnerabilities. - Eight automated DOM regression groups are retained in
tests/hardening.test.mjs: malformed/wrong-shape storage; blocked storage and missing clipboard; clear/recheck persistence; rejected clipboard writes; late wake-lock acquisition; ordinary wake-lock release; modifier shortcuts; long CJK/Arabic/emoji/HTML-shaped search titles and empty results. - Automated axe WCAG A/AA rules returned zero violations after corrections for 11 routes at 320px: homepage, Tech, Cooking, Life, Notes, Seen, Links, About, and representative tech, cooking, and life posts. Eight of these were also checked at 1280px.
- Dark-theme checks passed for homepage, Cooking archive, About, tech article, and recipe. Legacy About remains its own light surface. Recipe desktop checks passed in both themes.
- Doubled computed font sizes and line heights at 320px passed for homepage, Cooking archive, About, tech article, and recipe, with no detected viewport overflow. This stress test supplements narrow-viewport reflow; it is not a native browser text-zoom certification. Scrollable code and tables are excluded from viewport overflow assertions.
- Code-copy controls measured 44px high in the browser. Search was checked through the browser accessibility tree after migration: named dialog, labeled search input, readable result names/status, Escape dismissal, and restored scrolling. Earlier checks also exercised focus cycling, light/dark search, and resizing while open.
- Feed filters and Load older entries checked; pagination revealed 30 entries and focused the first newly revealed article. Recipe native checkboxes and clear action checked in the browser.
- Added
npm run audit:preview: a localhost-only server that injects axe into the built pages when?audit=1is present. Optional&theme=darkand&textScale=2support repeatable manual checks. Production files are unchanged by the audit harness.
Axe reports some contrast cases for manual review, particularly image-backed content; zero automated violations does not establish complete conformance. Third-party iframe contents are excluded. Not verified: a full VoiceOver/NVDA speech session, Windows forced colors, every legacy page, full internationalization, or lab performance metrics. These checks describe the local build at audit completion, before publication.