Repository navigation
Round 4: tabcordion, tooltip and responsive-table - #24
Merged
Merged
Conversation
…rrow
One set of panels, one DOM. A CSS container query on the element's own width sets
--tc-layout; the script reads it on every resize and makes the ARIA match
(tablist/tab/tabpanel, or heading buttons with aria-expanded). The selected tab's panel
is open in the accordion and the panel opened last is the tab when it widens; the
accordion holds several open unless multiple={false}. No-JS: every panel open under its
heading, the strip hidden.
scripts/check-structure.mjs (new, in npm run check) runs the shipped state block against
golden cases, pins the no-JS render and the one-breakpoint-in-CSS rule, and must catch 17
mutants of the render, the stylesheet and the logic.
Pattern from Rocketbelt (Pier 1 Imports, 2020, MIT); reimplemented, no code copied.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… on Escape
A real button (or link) whose aria-describedby names a role="tooltip" of plain text. It
shows on focus as well as hover, the pointer can move onto it, Escape dismisses it, taps
toggle it, and one shows at a time (WCAG 1.4.13). Placement top/bottom/left/right is
measured on show: it flips when the side has no room and slides to stay 8px inside the
window, in the Popover API's top layer where the browser has it. Icon-only triggers take
`label` and are 44px. No-JS: the text in brackets after the trigger, or its title with
inline={false}. Markup in `text` fails the build.
check-structure.mjs runs the shipped state and placement blocks against 22 + 13 golden
cases, pins the no-JS render, the focus and Escape wiring, the 44px target and the
fallback contrast (16.0:1), and must catch 24 mutants.
Pattern from Rocketbelt (Pier 1 Imports, 2020, MIT); reimplemented, no code copied.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts column is narrow Columns and rows in, a <table> out: caption, scope="col"/"row", explicit table roles (so cards keep table semantics in Chrome and Safari), and numbers right-aligned with tabular figures, detected at build time. mode="cards": below a breakpoint on the element's own width each row becomes a card, every value under its column's name from data-label, CSS only, the label drawn with empty alt text so it is not read twice. mode="scroll": a focusable region named by the caption, edge fades where there is more, a one-time hint, and no Tab stop when nothing overflows. Optional striped rows and a sticky header (inside the box with maxHeight). No-JS: the table. check-structure.mjs runs the numeric detection (frontmatter, types stripped) and the shipped edge maths (right-to-left included) against 14 + 9 golden cases, pins caption, scope, roles, data-label = header text, numeric alignment, the scroll region and hint, and the CSS-only cards at each size, and must catch 23 mutants. Pattern from Rocketbelt (Pier 1 Imports, 2020, MIT); reimplemented, no code copied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--tc-* and data-tc are testimonial-carousel's, already on main. Beyond
theming, the clash broke pages: tabcordion boots with
querySelectorAll('[data-tc]'), found the carousel's root, and threw on its
missing tab list inside forEach, so no tabcordion after a carousel on the
page mounted (the gallery index has both, carousel first). Following the
library's rule when two letters are taken (--anb-, --ofc-), tabcordion is
now tcd in its custom properties (--tcd-layout included), classes, the
runtime-built toggle class, data attributes and the demo, and
check-structure follows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Chip D of library round 4: three structure elements, reimplemented from Rocketbelt's patterns. One commit per element. Each has one self-contained
.astrofile, a demo, a catalogue entry, and a section in the newscripts/check-structure.mjs, which is now innpm run check.The check follows the business-hours pattern. Each element's rules live in one plain-JS block between
// <name>markers. The check takes that block from the built page, requires it to equal the source, and runs it against golden cases. It then pins the no-JS render of the built demo page. Finally it runs mutation tests: every check is run against deliberately broken copies (render, CSS and logic), and must fail each one.tabcordion
What it does. One set of panels. It is tabs when the element itself is wide and an accordion when it is narrow. A CSS container query on the element's own width decides, not the window. So the same element is tabs in a wide column and an accordion in a 17rem sidebar on a desktop page. The container query sets
--tcd-layout; the script reads it on resize and never measures a width itself, so the breakpoint lives only in CSS. State carries across the switch. Going to the accordion opens the selected tab's panel. Going to tabs selects the panel opened most recently that is still open. The accordion holds several panels open unlessmultiple={false}.When to pick something else.
notFornames both neighbours. Usetabswhen the set should stay tabs until the phone, or needs a vertical strip, icons, manual activation or a hash that follows the selection. Useaccordionfor questions, FAQ schema, or answers that find-in-page should reach while closed.Accessibility contract.
label, with tab and tabpanel,aria-selected,aria-controls,aria-labelledbyand a roving tabindex. ← → wrap; Home and End jump to the ends.aria-expandedandaria-controls; ↑ ↓ Home End move between headings. The strip is hidden and the panels lose the tabpanel role.No-JS render. The strip is hidden and every panel is open under its heading.
What the check pins.
aria-controlsand ids line up.roleand noaria-expandedbefore the script runs, and the runtime emitted once per page.@containerrules, with nomatchMediaorinnerWidthin the script.Search. Query "responsive accordion": 320/mo, difficulty 32, and rising (50/mo a year ago). alsoRanks: "tabs on mobile" (110/11), "responsive tabs" (40/35), "tabs to accordion" (10/34). The brief's "responsive tabs accordion" and "tabcordion" have no measurable volume. Source: SE Ranking US, 2026-09-26.
MIT. Pattern from Rocketbelt (Pier 1 Imports, 2020, MIT); reimplemented, no code copied.
tooltip
What it does. A real
<button>, or a link withhref, whosearia-describedbynames arole="tooltip"of plain text.overflow: hiddenancestor clips it.textfails the build. Anything interactive belongs in a popover, andnotForsays so.Accessibility contract (WCAG 1.4.13).
label, their icon isaria-hidden, and they are 44 × 44 px.No-JS render. The text shows in brackets after the trigger. With
inline={false}it is the trigger'stitleinstead, and the script removes thattitlewhen it runs.What the check pins.
aria-describedbyequals the tip's id,role="tooltip", no interactive content in the tip, and the inline and title variants.aria-labeland thearia-hiddenwrapper.Search. Query "tooltip html": 320/mo, difficulty 24. alsoRanks: "accessible tooltip" (50/14; the brief's query, kept as a variant of the bigger one), "tooltip ui" (260/36), "html tooltip" (10/36). Source: SE Ranking US, 2026-09-26.
MIT. Pattern from Rocketbelt (Pier 1 Imports, 2020, MIT); reimplemented, no code copied.
responsive-table
What it does. Columns and rows go in; a
<table>comes out, with a<caption>,scope="col"andscope="row". Numeric columns are detected at build time and right-aligned with tabular figures.data-labelin CSS only.maxHeightis set.notForsays so.Accessibility contract.
content: attr(data-label) / '', so a screen reader hears the column header once, not twice.aria-hidden.No-JS render. Cards mode has no script at all. A scroll-mode box scrolls without its fades or hint.
What the check pins.
data-labelequal to its header's text.aria-hidden.Search. Query "responsive tables": 140/mo, difficulty 8. alsoRanks: "responsive table html" (110/25; the brief's query), "responsive table css" (110/0), "responsive data table" (70/16), "sticky table header" (50/12). The whole cluster is down from about 260 a year ago. Source: SE Ranking US, 2026-09-26.
MIT. Pattern from Rocketbelt (Pier 1 Imports, 2020, MIT); reimplemented, no code copied.
Verification
npm run checkandnpm run buildare green.ecropolis/SuperheroTechmain, withELEMENTS_SOURCE=<this checkout> npm run check:types, gave 0 errors and 0 warnings. None of its hints are in the new files.npm run buildandnpm run check:elementsalso pass. All three pages mount with no console errors after its CSP step moves the inline scripts out.Notes
Elementfields, and nosource.--tcd-*,.tcd__*anddata-tcd. It first shipped as--tc-*anddata-tc, which testimonial-carousel already owns, so its boot picked up carousel roots and threw (renamed in 2de7f40).tt(tooltip) andrt(responsive-table) collide with nothing in the library.catalog.ts,demos/index.tsand thenpm run checkchain inpackage.json, so expect trivial conflicts there. Both sides are kept.superhero-ui-librarysections are in a separate agent-skills PR.🤖 Generated with Claude Code