Skip to content

feat(site): enhance HTML with semantic elements and ARIA accessibility#37

Merged
spoorcc merged 3 commits intomainfrom
claude/fix-issue-28-otA3r
Mar 10, 2026
Merged

feat(site): enhance HTML with semantic elements and ARIA accessibility#37
spoorcc merged 3 commits intomainfrom
claude/fix-issue-28-otA3r

Conversation

@spoorcc
Copy link
Contributor

@spoorcc spoorcc commented Mar 10, 2026

Addresses issue #28 by improving the semantic markup and accessibility of
the single-page app while maintaining zero behavioral or visual changes.

Semantic HTML5 improvements:

  • Topbar <div> converted to <nav aria-label="External links">
  • Hero <div> converted to <section>
  • Results list area <main> (nested incorrectly) converted to <section>
  • All view pages wrapped in a top-level <main id="main-content">
  • Breadcrumb <div> converted to <nav aria-label="Breadcrumb">
  • Heading hierarchy added: <h1> for hero title and package name,
    <h2> for sidebar filter groups and aside card titles
  • CSS heading reset added so h1h6 inherit font size/weight from
    their parent class styles

ARIA accessibility improvements:

  • aria-label added to both search inputs, navbar, sort selects,
    manifest button, modal close button, and copy-snippet button
  • Tab bar: role="tablist", role="tab", aria-selected, and
    aria-controls on buttons; role="tabpanel" + aria-labelledby
    on panels; switchTab() JS updated to sync aria-selected
  • Snippet card header: role="button", tabindex="0",
    aria-expanded, and keyboard handler added; toggleSnip() and
    renderDetail() updated to keep aria-expanded in sync
  • Modal: role="dialog", aria-modal="true", aria-labelledby
  • Results count: aria-live="polite" and aria-atomic="true"
  • Toast notification: role="status" and aria-live="polite"
  • Decorative chevron spans marked aria-hidden="true"
  • Filter aside given aria-label="Filter by"
  • aria-label added to main navbar and package information tabs

https://claude.ai/code/session_01LmU2X5jmgRZ1LHXc9S7bW1

Summary by CodeRabbit

  • New Features

    • Multi-view layout (Home / Results / Detail)
    • Manifest builder modal with live count, copy and download
    • Toast notifications and snippet/manifest clipboard support
    • Detail view tabs for README and Versions; hierarchical version tree
    • Enhanced search, filtering, and paging controls
  • Improvements

    • Better accessibility with ARIA/semantic updates
    • Improved navigation, UI interactions, and error guards

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 10, 2026

Warning

Rate limit exceeded

@spoorcc has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 47 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 46c5b88c-6d30-409f-aaa7-7552cb6d5a0a

📥 Commits

Reviewing files that changed from the base of the PR and between 6db290e and 7709aa0.

📒 Files selected for processing (2)
  • scripts/check_css.py
  • scripts/check_js.py
📝 Walkthrough

Walkthrough

Adds pre-commit djlint hooks and two new validation scripts for embedded CSS and JS, updates pyproject with tinycss2 and py-mini-racer, and substantially rewrites the site HTML/JS to a multi-view layout with accessibility attributes, a manifest-builder modal, and new global modal/snippet/manifest functions.

Changes

Cohort / File(s) Summary
Pre-commit & Project Config
\.pre-commit-config\.yaml, pyproject\.toml
Added three djlint hooks (HTML format check, embedded CSS check, embedded JS check) and two optional dev dependencies (tinycss2==1.5.1, py-mini-racer==0.6.0); updated djlint profile/format settings.
Validation Scripts
scripts/check_css.py, scripts/check_js.py
New CLI scripts: check_css.py extracts <style> blocks and validates with tinycss2, reporting block/line diagnostics; check_js.py extracts inline <script> blocks and checks syntax using V8 via py-mini-racer, reporting per-block errors and exit codes.
Site HTML & Frontend Logic
dfetch_hub/site/index.html
Large rewrite to multi-section layout (HOME/RESULTS/DETAIL), added ARIA/semantic updates, manifest-builder modal and toast UI, snippet/manifest copy & download flows, detailed version rendering (hierarchical trees), and new global functions (openModal, closeModal, renderModal, copySnip, copyMf, dlMf).

Sequence Diagram(s)

sequenceDiagram
  participant Dev as Developer
  participant Pre as Pre-commit
  participant Hook as djlint Hooks
  participant CSS as check_css.py
  participant JS as check_js.py
  participant HTML as Repository HTML Files
  participant V8 as V8 (py-mini-racer)
  Dev->>Pre: git commit / pre-commit run
  Pre->>Hook: run djlint-check, check-embedded-css, check-embedded-js
  Hook->>HTML: read HTML files
  Hook->>CSS: invoke scripts/check_css.py (style extraction -> tinycss2)
  CSS->>HTML: parse <style> blocks and validate
  Hook->>JS: invoke scripts/check_js.py (script extraction -> V8)
  JS->>V8: evaluate syntax in inline scripts
  CSS-->>Hook: return diagnostics (ok|errors)
  JS-->>Hook: return diagnostics (ok|errors)
  Hook-->>Pre: aggregate exit code (0|1)
  Pre-->>Dev: block or allow commit based on exit code
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • Issue 28 — Implements the requested semantic, accessibility, and structural site changes (multi-view layout, ARIA roles, navigation/search updates).

Possibly related PRs

  • PR 34 — Overlaps in hierarchical version-tree rendering and tree-toggle/version selection logic in the site HTML.
  • PR 32 — Adds related version-tree rendering and selection handling functions that intersect with the changed frontend code.
  • PR 20 — Related to expanding djlint and pre-commit tooling; touches the same config and validation concerns.

Poem

🐰 I hopped through tags and script and style,
Found nested trees and ARIA all the while,
TinyCSS checked each curled-up rule,
V8 watched scripts so none act the fool,
A manifest blooms — a rabbit's web-dev smile! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title focuses on enhancing HTML with semantic elements and ARIA accessibility, but the changeset includes substantial additional work: new pre-commit hooks for CSS/JS validation, new validation scripts, and updates to project configuration. Revise the title to reflect all major changes, such as 'feat(site): add semantic HTML/ARIA and validation tools for CSS/JS linting' or split into separate PRs for accessibility changes and tooling additions.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/fix-issue-28-otA3r

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

@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: 2

Caution

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

⚠️ Outside diff range comments (3)
test/test_catalog_clib.py (1)

1-1: ⚠️ Potential issue | 🟡 Minor

Run black to fix formatting.

The pipeline indicates this file needs reformatting. Run black test/test_catalog_clib.py to apply consistent formatting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/test_catalog_clib.py` at line 1, The test file test_catalog_clib.py is
not formatted according to project standards; run the code formatter (black) on
this file to fix formatting issues—e.g., execute `black
test/test_catalog_clib.py` (or apply your repository's pre-commit formatter) and
commit the resulting changes so the tests for dfetch_hub.catalog.clib
(Packages.md parsing and limit handling) are properly formatted.
test/test_catalog_conan.py (1)

1-1: ⚠️ Potential issue | 🟡 Minor

Run black to fix formatting.

The pipeline indicates this file needs reformatting. Run black test/test_catalog_conan.py to apply consistent formatting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/test_catalog_conan.py` at line 1, The test file test_catalog_conan.py is
misformatted; run the code formatter and commit the change by executing "black
test/test_catalog_conan.py" (or your repo's pre-commit/format step) to apply
consistent formatting to the module containing the Conan catalog tests
(test/test_catalog_conan.py) and then stage and push the resulting formatted
file.
test/test_catalog_readme.py (1)

1-1: ⚠️ Potential issue | 🟡 Minor

Run black to fix formatting.

The pipeline indicates this file needs reformatting. Run black test/test_catalog_readme.py to apply consistent formatting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/test_catalog_readme.py` at line 1, Run the code formatter "black" on the
test file to fix formatting issues: apply black to test/test_catalog_readme.py
(the module with the top docstring """Tests for
dfetch_hub.catalog.sources.readme: README parsing and description
extraction."""), e.g., run `black test/test_catalog_readme.py` and commit the
resulting changes so the file conforms to project formatting rules.
🧹 Nitpick comments (1)
scripts/check_css.py (1)

27-28: Empty TYPE_CHECKING block can be removed.

The if TYPE_CHECKING: pass block serves no purpose. Consider removing lines 27-28.

🧹 Remove empty TYPE_CHECKING block
-if TYPE_CHECKING:
-    pass
-
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/check_css.py` around lines 27 - 28, Remove the no-op TYPE_CHECKING
guard: delete the "if TYPE_CHECKING: pass" block and, if that makes the
TYPE_CHECKING name unused, also remove its import from the top of the module so
there are no unused imports; locate the empty block by the TYPE_CHECKING symbol
in scripts/check_css.py (and adjust any related imports accordingly).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@dfetch_hub/site/index.html`:
- Around line 1175-1176: The _ESC map in index.html contains a malformed HTML
entity for '<' (currently '&lt ;' with an extra space) which breaks escaping;
fix the mapping in _ESC (referenced by esc(s)) by removing the stray space so
the value is '&lt;' and ensure esc(s) continues to use _ESC for replacements.

In `@scripts/check_css.py`:
- Line 1: The file scripts/check_css.py is failing CI due to formatting; run the
project's formatter (black) on scripts/check_css.py and re-commit the changes to
satisfy the pipeline. If there are additional lint/format rules, run the same
command used by CI (e.g., black . or the repo's pre-commit hooks) to ensure
consistent formatting for functions like main or any validate_* helpers in that
file before pushing.

---

Outside diff comments:
In `@test/test_catalog_clib.py`:
- Line 1: The test file test_catalog_clib.py is not formatted according to
project standards; run the code formatter (black) on this file to fix formatting
issues—e.g., execute `black test/test_catalog_clib.py` (or apply your
repository's pre-commit formatter) and commit the resulting changes so the tests
for dfetch_hub.catalog.clib (Packages.md parsing and limit handling) are
properly formatted.

In `@test/test_catalog_conan.py`:
- Line 1: The test file test_catalog_conan.py is misformatted; run the code
formatter and commit the change by executing "black test/test_catalog_conan.py"
(or your repo's pre-commit/format step) to apply consistent formatting to the
module containing the Conan catalog tests (test/test_catalog_conan.py) and then
stage and push the resulting formatted file.

In `@test/test_catalog_readme.py`:
- Line 1: Run the code formatter "black" on the test file to fix formatting
issues: apply black to test/test_catalog_readme.py (the module with the top
docstring """Tests for dfetch_hub.catalog.sources.readme: README parsing and
description extraction."""), e.g., run `black test/test_catalog_readme.py` and
commit the resulting changes so the file conforms to project formatting rules.

---

Nitpick comments:
In `@scripts/check_css.py`:
- Around line 27-28: Remove the no-op TYPE_CHECKING guard: delete the "if
TYPE_CHECKING: pass" block and, if that makes the TYPE_CHECKING name unused,
also remove its import from the top of the module so there are no unused
imports; locate the empty block by the TYPE_CHECKING symbol in
scripts/check_css.py (and adjust any related imports accordingly).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2c585597-6864-458a-a0c7-3a3ad970a39d

📥 Commits

Reviewing files that changed from the base of the PR and between 792e3cb and 68d89ff.

📒 Files selected for processing (8)
  • .pre-commit-config.yaml
  • dfetch_hub/site/index.html
  • pyproject.toml
  • scripts/check_css.py
  • scripts/check_js.py
  • test/test_catalog_clib.py
  • test/test_catalog_conan.py
  • test/test_catalog_readme.py

Addresses issue #28 by improving the semantic markup and accessibility of
the single-page app while maintaining zero behavioral or visual changes.

Semantic HTML5 improvements:
- Topbar `<div>` converted to `<nav aria-label="External links">`
- Hero `<div>` converted to `<section>`
- Results list area `<main>` (nested incorrectly) converted to `<section>`
- All view pages wrapped in a top-level `<main id="main-content">`
- Breadcrumb `<div>` converted to `<nav aria-label="Breadcrumb">`
- Heading hierarchy added: `<h1>` for hero title and package name,
  `<h2>` for sidebar filter groups and aside card titles
- CSS heading reset added so `h1`–`h6` inherit font size/weight from
  their parent class styles

ARIA accessibility improvements:
- `aria-label` added to both search inputs, navbar, sort selects,
  manifest button, modal close button, and copy-snippet button
- Tab bar: `role="tablist"`, `role="tab"`, `aria-selected`, and
  `aria-controls` on buttons; `role="tabpanel"` + `aria-labelledby`
  on panels; `switchTab()` JS updated to sync `aria-selected`
- Snippet card header: `role="button"`, `tabindex="0"`,
  `aria-expanded`, and keyboard handler added; `toggleSnip()` and
  `renderDetail()` updated to keep `aria-expanded` in sync
- Modal: `role="dialog"`, `aria-modal="true"`, `aria-labelledby`
- Results count: `aria-live="polite"` and `aria-atomic="true"`
- Toast notification: `role="status"` and `aria-live="polite"`
- Decorative chevron spans marked `aria-hidden="true"`
- Filter aside given `aria-label="Filter by"`
- `aria-label` added to main navbar and package information tabs

https://claude.ai/code/session_01LmU2X5jmgRZ1LHXc9S7bW1
@spoorcc spoorcc force-pushed the claude/fix-issue-28-otA3r branch from 68d89ff to 2603ef0 Compare March 10, 2026 21:36
Copy link
Contributor

@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 (2)
scripts/check_js.py (1)

77-87: Return type annotation is overly generic.

The return type object loses type information. Consider using a protocol or Any with a clearer comment, or import MiniRacer inside a TYPE_CHECKING block for annotation purposes.

♻️ Suggested improvement
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from py_mini_racer import MiniRacer

-def _make_ctx() -> object:
+def _make_ctx() -> "MiniRacer":
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/check_js.py` around lines 77 - 87, The return type for _make_ctx is
currently object and should preserve the MiniRacer type: import TYPE_CHECKING
from typing and inside an if TYPE_CHECKING block import MiniRacer from
py_mini_racer, then change the signature of _make_ctx to return the MiniRacer
type (or use Any from typing if you prefer a looser, no-runtime import). Update
the annotation on def _make_ctx() to use the imported MiniRacer (or Any) so
callers get proper type information while avoiding runtime import overhead.
scripts/check_css.py (1)

28-29: Remove empty TYPE_CHECKING block.

The block contains only pass and serves no purpose.

🧹 Remove dead code
-if TYPE_CHECKING:
-    pass
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/check_css.py` around lines 28 - 29, Remove the empty TYPE_CHECKING
block in scripts/check_css.py: delete the "if TYPE_CHECKING: pass" stanza (and
if the only use of TYPE_CHECKING is this block, also remove the corresponding
import of TYPE_CHECKING) so no dead code remains; locate the snippet by
searching for the "if TYPE_CHECKING" conditional in that file and remove it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/check_css.py`:
- Around line 46-56: The stored style-block line is taken from getpos() in
handle_endtag, so errors point to the closing </style> instead of the opening
<style>; fix by capturing the start line in handle_starttag (call
self.getpos()[0] there and save to self._start_line when tag == "style"),
initialize self._start_line = 0 in __init__, and then in handle_endtag append
(self._start_line, "".join(self._buf)) to self.blocks instead of calling
getpos() there; keep the attrs parameter on handle_starttag (required by
HTMLParser) despite the linter warning.
- Around line 126-136: The code is parsing nested at-rule content with
tinycss2.parse_stylesheet(rule.content) but should use
tinycss2.parse_blocks_contents(rule.content) (or parse_rule_list() for old
compatibility) because rule.content is a list of ComponentValue tokens; replace
the parse_stylesheet call with parse_blocks_contents(rule.content) and keep the
subsequent loop and branches (checking inner_rule.type == "error" and calling
_check_declarations(inner_rule.content, start_line, block_num)) unchanged so
nested `@-rules` are parsed correctly.

In `@scripts/check_js.py`:
- Around line 52-64: The start line for script blocks is being captured at
handle_endtag via getpos(), which records the closing tag position; modify
handle_starttag to save the opening position (use self._start_line =
self.getpos()[0]) when entering a script (where you already set self._in_script
and self._buf), then change handle_endtag to append (self._start_line,
"".join(self._buf)) instead of calling getpos() there; also add an initializer
self._start_line: int = 0 in __init__ so the attribute exists.

---

Nitpick comments:
In `@scripts/check_css.py`:
- Around line 28-29: Remove the empty TYPE_CHECKING block in
scripts/check_css.py: delete the "if TYPE_CHECKING: pass" stanza (and if the
only use of TYPE_CHECKING is this block, also remove the corresponding import of
TYPE_CHECKING) so no dead code remains; locate the snippet by searching for the
"if TYPE_CHECKING" conditional in that file and remove it.

In `@scripts/check_js.py`:
- Around line 77-87: The return type for _make_ctx is currently object and
should preserve the MiniRacer type: import TYPE_CHECKING from typing and inside
an if TYPE_CHECKING block import MiniRacer from py_mini_racer, then change the
signature of _make_ctx to return the MiniRacer type (or use Any from typing if
you prefer a looser, no-runtime import). Update the annotation on def
_make_ctx() to use the imported MiniRacer (or Any) so callers get proper type
information while avoiding runtime import overhead.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 72b204c6-c344-4082-afb1-518b6b17b1c5

📥 Commits

Reviewing files that changed from the base of the PR and between 68d89ff and 2603ef0.

📒 Files selected for processing (5)
  • .pre-commit-config.yaml
  • dfetch_hub/site/index.html
  • pyproject.toml
  • scripts/check_css.py
  • scripts/check_js.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • pyproject.toml

Three new pre-commit hooks (all PyPI-installable, language: python):

djlint-check — HTML formatter consistency
  Runs djlint --check so any future hand-edits that deviate from djlint's
  canonical HTML formatting are caught before commit.
  Configuration additions in pyproject.toml:
    profile = "html"   — enables the full HTML5 rule set
    indent = 2         — enforces 2-space indentation
    max_line_length = 120 — matches the Python project limit
    format_js = false  — prevents djlint from mangling JS regex tokens
    format_css = false — CSS is intentionally minified; keep it verbatim

check-embedded-css (scripts/check_css.py + tinycss2==1.5.1)
  Extracts every <style> block from HTML files and parses each with
  tinycss2.  Reports parse errors at the tokeniser level (malformed tokens,
  unexpected characters) and declaration-level errors (missing ':' separator,
  empty value lists).  Accepts modern CSS (custom properties, var(), clamp(),
  vendor prefixes) without false positives.

check-embedded-js (scripts/check_js.py + py-mini-racer==0.6.0)
  Extracts every inline <script> block (skipping src= external scripts) and
  passes each through V8's Function constructor via py-mini-racer.  This
  catches all ES2020+ syntax errors (including optional chaining, async/await,
  nullish coalescing) without executing the code.

Supporting changes:
  • esc() refactored from 5 sequential .replace() calls to a single-pass
    lookup-table approach (avoids djlint misreading the bare />/g regex token;
    also slightly faster at runtime).
  • index.html reformatted by djlint --reformat (HTML structure only; CSS/JS
    content preserved verbatim).
  • tinycss2==1.5.1 and py-mini-racer==0.6.0 added to [project.optional-
    dependencies].development in pyproject.toml.

https://claude.ai/code/session_01LmU2X5jmgRZ1LHXc9S7bW1
@spoorcc spoorcc force-pushed the claude/fix-issue-28-otA3r branch from 2603ef0 to 6db290e Compare March 10, 2026 21:53
@spoorcc spoorcc merged commit b96a229 into main Mar 10, 2026
1 check passed
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