fix(w3c): clear per-page HTML validator findings (S1-S7, F11, F12) #24

Merged
michalvankodev merged 5 commits from w3c-html-fixes into main 2026-09-14 21:09:30 +02:00

Implements S1–S7 of specs/w3c-validation.md, plus two new findings (F11, F12) discovered during verification. S8/S9/S10 are deferred to a follow-up.

What changed

Fix Change
S1 sprite prolog just svgstore post-strips <?xml?>/<!DOCTYPE; recipe switched to npx --yes svgstore-cli (svgstore@3 dropped its bin — the old recipe no longer ran)
S2 invalid id dots-@-15pt → dots-at-15pt in json-feed.svg, sprite regenerated
S3 logo cleanup Inkscape metadata stripped from m-logo-animated.svg; every anime.js-targeted id kept (m-logo, border, m-letter, *-start, bg-gradient, bg-stop)
S3b inline style portfolio <style> in body → #logo-container svg rule in styles/input.css
S4 view-transition-name header/footer bare attribute → style="view-transition-name: …"
S5 RDFa CC license xmlns:cc/dct → prefix="cc: … dct: …"
S6 machine dates new machine_date filter (2020-05-11T05:38:18Z) used in all 3 datetime= attrs, +2 unit tests
S7 headings h2 "featured work"/"more work" on /showcase; h2 "what I do" above index talent cards; portfolio tagline h3 → styled p
F11 (new) blog-post page-bottom layout wrapper <footer> → <div> — it contains <header> elements (macro + preview cards) which must not descend from footer
F12 (new) _posts/blog/dev-2019-08-09-ide-to copy.md → …-copy.md — the space in the slug produced an illegal href (URL changes from …ide-to%20copy to …ide-to-copy)

Verification

  • cargo test: 48 passed (incl. 2 new machine_date tests)
  • just validate-html local: /, /blog, /showcase, /portfolio — 0 errors (was 16/8/20/…) ; blog post pages carry only the deferred F8 stray </p>
  • just validate-feed local: VALID; remaining warnings are exactly the deferred F8 ×21 / F9 ×3 / F10 ×1
  • Visual check (screenshots): logo renders + animation completes, new section headings styled consistently, no layout regressions

Notes

  • F12 renames a deliberately-published dev/test article (05fd17d) — its old URL already required %20 encoding, so the new slug is strictly an improvement; revert the rename if the URL matters.
  • static/svg/icons-sprite.svg is a stale unreferenced legacy copy (still has the old id) — left untouched; candidate for deletion separately.
Implements S1–S7 of `specs/w3c-validation.md`, plus two new findings (F11, F12) discovered during verification. S8/S9/S10 are deferred to a follow-up. ## What changed | Fix | Change | |---|---| | S1 sprite prolog | `just svgstore` post-strips `<?xml?>`/`<!DOCTYPE`; recipe switched to `npx --yes svgstore-cli` (svgstore@3 dropped its bin — the old recipe no longer ran) | | S2 invalid id | `dots-@-15pt` → `dots-at-15pt` in `json-feed.svg`, sprite regenerated | | S3 logo cleanup | Inkscape metadata stripped from `m-logo-animated.svg`; every anime.js-targeted id kept (`m-logo`, `border`, `m-letter`, `*-start`, `bg-gradient`, `bg-stop`) | | S3b inline style | portfolio `<style>` in body → `#logo-container svg` rule in `styles/input.css` | | S4 view-transition-name | header/footer bare attribute → `style="view-transition-name: …"` | | S5 RDFa | CC license `xmlns:cc/dct` → `prefix="cc: … dct: …"` | | S6 machine dates | new `machine_date` filter (`2020-05-11T05:38:18Z`) used in all 3 `datetime=` attrs, +2 unit tests | | S7 headings | `h2` "featured work"/"more work" on `/showcase`; `h2` "what I do" above index talent cards; portfolio tagline `h3` → styled `p` | | F11 (new) | blog-post page-bottom layout wrapper `<footer>` → `<div>` — it contains `<header>` elements (macro + preview cards) which must not descend from `footer` | | F12 (new) | `_posts/blog/dev-2019-08-09-ide-to copy.md` → `…-copy.md` — the space in the slug produced an illegal `href` (URL changes from `…ide-to%20copy` to `…ide-to-copy`) | ## Verification - `cargo test`: 48 passed (incl. 2 new `machine_date` tests) - `just validate-html local`: **`/`, `/blog`, `/showcase`, `/portfolio` — 0 errors** (was 16/8/20/…) ; blog post pages carry only the deferred F8 stray `</p>` - `just validate-feed local`: VALID; remaining warnings are exactly the deferred F8 ×21 / F9 ×3 / F10 ×1 - Visual check (screenshots): logo renders + animation completes, new section headings styled consistently, no layout regressions ## Notes - F12 renames a deliberately-published dev/test article (05fd17d) — its old URL already required `%20` encoding, so the new slug is strictly an improvement; revert the rename if the URL matters. - `static/svg/icons-sprite.svg` is a stale unreferenced legacy copy (still has the old id) — left untouched; candidate for deletion separately.
fix(w3c): clear per-page HTML validator findings (S1–S7, F11, F12)
All checks were successful
preview / preview hostname (push) Successful in 0s
preview / build + deploy preview (push) Has been skipped
preview / teardown preview (push) Has been skipped
test / cargo test (push) Successful in 1m26s
preview / preview hostname (pull_request) Successful in 0s
preview / teardown preview (pull_request) Has been skipped
preview / build + deploy preview (pull_request) Successful in 1m29s
625c9d27bd
Template/sprite/heading fixes per specs/w3c-validation.md:

- S1 sprite: strip XML prolog+DOCTYPE after svgstore (invalid in HTML);
  recipe now uses svgstore-cli (svgstore@3 dropped its bin)
- S2 json-feed.svg: rename invalid id dots-@-15pt -> dots-at-15pt
- S3 animated logo: drop Inkscape editor metadata (PI, namespaces,
  namedview, nodetypes); all anime.js-targeted ids kept — animation
  verified rendering
- S3b portfolio: inline <style> in body -> rule in styles/input.css
- S4 header/footer: bare view-transition-name attr -> style property
- S5 CC license: xmlns:cc/dct -> RDFa prefix attribute
- S6 time datetimes: new machine_date filter (RFC 3339, Z) in 3 templates
  + unit tests
- S7 headings: h2 'featured work'/'more work' on /showcase, h2 'what I do'
  above index talent cards, portfolio tagline h3 -> styled p
- F11 blog_post: page-bottom layout wrapper footer -> div (headers must
  not descend from footer)
- F12 rename 'dev-2019-08-09-ide-to copy.md' -> '...ide-to-copy.md'
  (space in slug made href invalid; URL loses the %20)

Nu validator: / /blog /showcase /portfolio now 0 errors; blog post pages
carry only the deferred F8 (p>figure restructure, next PR). Feed stays
VALID with F8/F9/F10 warnings deferred.

🪦 Live preview

removed (PR closed)

<!-- preview-link --> ## 🪦 Live preview removed (PR closed)
fix(index): drop 'what I do' h2 — demote talent-card titles instead
All checks were successful
preview / preview hostname (push) Successful in 0s
preview / teardown preview (push) Has been skipped
test / cargo test (push) Successful in 1m14s
preview / preview hostname (pull_request) Successful in 0s
preview / teardown preview (pull_request) Has been skipped
preview / build + deploy preview (push) Successful in 1m22s
preview / build + deploy preview (pull_request) Successful in 1m11s
220250c877
Per review: the visible section heading was unwanted. The talent-card
component (h3 -> h2, index + portfolio) fixes the h1->h3 skip without
adding text. Outline stays valid on both pages (portfolio cards become
siblings of the Skills h2); no visual change — the cards' utility
classes carry all styling and the .article-body h2/h3 size rules don't
apply outside articles.
Author
Owner

Revision (220250c): per review, the visible h2 "what I do" on the index is dropped — the talent-card component's titles are demoted h3 → h2 instead (fixes the same h1→h3 skip without adding text). The component is shared with /portfolio, where the outline stays valid too (cards become siblings of the "Skills" h2); visuals unchanged since the cards carry their own utility classes and the .article-body h2/h3 sizing rules don't apply there. Re-validated: / and /portfolio still 0 Nu errors.

Revision (220250c): per review, the visible `h2` "what I do" on the index is dropped — the talent-card component's titles are demoted `h3` → `h2` instead (fixes the same h1→h3 skip without adding text). The component is shared with `/portfolio`, where the outline stays valid too (cards become siblings of the "Skills" `h2`); visuals unchanged since the cards carry their own utility classes and the `.article-body` h2/h3 sizing rules don't apply there. Re-validated: `/` and `/portfolio` still 0 Nu errors.
- S8 markdown restructure: BlockFigureParagraphGate keeps generated
  <figure> blocks out of the implicit <p> wrapper. Image-only paragraphs
  emit bare figures at block level; mid-paragraph figures close/reopen
  the paragraph; whitespace after a figure no longer materializes an
  empty <p>. Fenced code needed no gating (pulldown never nests CodeBlock
  in Paragraph events).
- F13 external-image fix (pre-existing, exposed by S8): bare-<img> path
  for non-/ URLs no longer emits the </figcaption></figure> closer or
  leaks alt text as visible duplicate content (in_bare_img state).
- F14 attribute escaping: escape_attr for hand-built markdown tags
  (external img src/alt); entity-aware escape_raw_ampersands for the
  feed link-card href (lol_html get_attribute returns serialized values,
  before() inserts raw HTML — raw & in ?video=1&parent=x was NotHtml x6).
- S9 feed embed policy: iframes replaced with host-labeled link cards
  (watch on Twitch/YouTube, listen on Spotify, ...); src absolutized;
  srcless shells dropped. Site keeps the live embeds.
- S10: CharacterData allowlisted in validate_feed.py (style preference,
  both escape forms well-formed XML).
- Ratchet: validate-feed strict by default (LAX=1 escape hatch).

Verification:
- cargo test: 60 passed (8 new markdown-shape tests, 3 iframe-policy
  tests, corpus audits: no <iframe> and no <p>-nested blocks in any
  rendered post)
- just validate-feed local: VALID, zero non-allowlisted warnings in
  strict mode
- just validate-html local: 0 errors on all 5 default pages (incl. the
  article page that carried the F8 stray </p>)
- article screenshots: figures render with proper spacing; devbreak
  page keeps its Twitch embed (feed-only policy)
The in_bare_img suppression misread author intent: the alt text showing
after external images WAS the desired caption behavior (same as local
images) — the workaround was only missing the <figure><figcaption>
opener. External images and SVGs now share one simple-figure arm:

  <figure><img src alt><figcaption>…alt text…</figcaption></figure>

End(Image) closer is unconditional again (every path opens a figure).
Verified live: first-weekly shows the beyerdynamic photo with its
centered caption; feed (strict) and all 5 Nu pages still clean.
fix(markdown): raw source <figure> blocks must not trip the paragraph gate
All checks were successful
preview / preview hostname (push) Successful in 0s
preview / teardown preview (push) Has been skipped
preview / build + deploy preview (push) Successful in 1m15s
preview / preview hostname (pull_request) Successful in 0s
preview / build + deploy preview (pull_request) Has been skipped
test / cargo test (push) Has been skipped
preview / teardown preview (pull_request) Successful in 0s
deploy / cargo test (push) Successful in 1m11s
deploy / build, export, deploy (push) Successful in 2m9s
ce70865cb1
The gate matched ANY Event::Html starting with <figure — including
hand-written figure blocks in post sources, which pulldown emits at
block level (outside Paragraph events). Those close with a plain
</figure> that never matches our </figcaption></figure> end matcher,
so the in_figure flag stuck and swallowed the <p> of every following
paragraph — text rendered as one unwrapped wall (seen on
/blog/2026-04-01-week-with-my-pi-agent, PR preview).

Inside a Paragraph span an Event::Html figure-start can only be one of
ours (source block HTML is never wrapped in Paragraph events; inline
raw HTML uses InlineHtml) — so the flag is now only set there, plus a
defensive reset on paragraph start.

Regression tests: the exact corpus shape (raw figure block followed by
paragraphs) and a corpus-wide guard in the feed audit (no long bare
text runs directly after </figure>).
michalvankodev deleted branch w3c-html-fixes 2026-09-14 21:09:30 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
michalvankodev/michalvankodev-site!24
No description provided.