Skip to content

Defer tabs until a native accessible HTML primitive is available #61

Closed as not planned
Closed as not planned

Description

@ricpastori

Source

Upstream: picocss/pico discussion #608 (11 upvotes, one of the higher-engagement Ideas threads), found during a sweep of picocss/pico's Discussions for items not yet covered by cirth's M5 audit.

Problem

Tabs are a common UI pattern with no equivalent in cirth today. The upstream proposal combines <details> with role="tablist"/role="tab"/role="tabpanel":

<div role="tablist">
  <details role="tab" name="my-tabs" open>
    <summary>First</summary>
    <div role="tabpanel">First tab content</div>
  </details>
  ...
</div>

Real accessibility risk flagged in the same thread, not to be glossed over: <details>'s only permitted implicit/explicit ARIA role is group — role="tab" on a <details> element is not sanctioned by the HTML-ARIA mapping spec, so this pattern may fail ARIA validation and could confuse assistive tech despite "working" visually. This needs a real decision, not a straight port of the upstream proposal.

Ask

Evaluate whether a tabs component is worth shipping, and if so, find a spec-clean implementation:

  • Is there a construction using native/permitted roles that still avoids JS (e.g., radio-button-driven tabs with appearance: none, which several other CSS-only tab implementations use, at the cost of "nastier" markup per the same thread)?
  • Or does this only become clean once a future platform primitive lands (native <tabs>/<tab-group> proposals exist in various stages)?
  • If no clean JS-free/spec-clean path exists today, document that explicitly as the outcome rather than leaving the idea open indefinitely.

Acceptance

  • A written decision: ship (with the spec-clean markup used), or explicitly deferred/rejected with the reasoning recorded.
  • If shipped: docs page, live demo, a11y-audited (axe) like every other component.

Activity

  1. added
    ♿ accessibilityAccessibility, assistive technology, keyboard use, or WCAG compliance.
    🧭 decision neededNeeds a product, API, or maintainership decision before implementation.
    ⬆️ upstreamInherited from or coordinated with the upstream Pico CSS project.
    on Aug 10, 2026
  2. moved this from 📝 Todo to 📥 Backlog in Cirth roadmapon Aug 10, 2026
  3. moved this from 📥 Backlog to 📝 Todo in Cirth roadmapon Aug 10, 2026
  4. ricpastori commented on Aug 15, 2026

    @ricpastori
    CollaboratorAuthor

    Proposed decision

    I recommend deferring this component until HTML provides a native tabs primitive with interoperable browser and accessibility support.

    At the moment, Cirth cannot implement a true tabs component that is simultaneously:

    • CSS-only;
    • semantically conforming;
    • keyboard accessible;
    • synchronized with ARIA state;
    • progressively enhanced;
    • consistent across browsers.

    This is not primarily a styling limitation. The missing part is native interaction and state management.

    Why the <details> proposal cannot be used

    The proposed markup assigns role="tab" to <details>:

    <div role="tablist">
      <details role="tab" name="my-tabs" open>
        <summary>First</summary>
        <div role="tabpanel">First content</div>
      </details>
    </div>

    This is not conforming HTML/ARIA.

    <details> has an implicit group role and cannot be changed to tab. A <summary> that belongs to its parent <details> cannot be assigned an alternative role either.

    Reference: ARIA in HTML
    The native interaction also remains that of a disclosure:

    • focus lands on each <summary>;
    • Enter or Space toggles the disclosure;
    • the active disclosure can normally be closed;
    • browser accessibility APIs expose disclosure state, not tab selection;
    • the expected tabs arrow-key model is not provided.

    Preventing the active summary from closing with:

    pointer-events: none;

    would only block pointer activation. Keyboard activation could still behave differently, creating inconsistent input behaviour.

    Even if the elements were visually arranged like tabs, assistive technology would still encounter a group of disclosures. Presenting one interaction model visually while exposing another programmatically would be misleading.

    <details name> remains useful for an exclusive disclosure or accordion pattern, but it should not be presented as tabs.

    Why a radio-based implementation is not a replacement

    Radio buttons can maintain a mutually exclusive checked state without JavaScript, and browsers provide native keyboard interaction for a radio group.

    However, that produces a radio-based view selector, not an ARIA tabs widget.

    A CSS-only implementation cannot keep all required tabs state synchronized:

    • aria-selected on the active tab;
    • tabindex="0" on the active tab;
    • tabindex="-1" on inactive tabs;
    • the visible tabpanel;
    • focus movement between tabs;
    • panel activation;
    • Home and End behaviour;
    • horizontal or vertical arrow-key behaviour.

    Keeping the controls exposed as radio buttons would be semantically honest, but it would be a different component. Reassigning tab roles would promise an interaction model that is not actually implemented.

    Hiding the radio inputs also introduces further risks. If they are removed from the accessibility tree or keyboard navigation, the remaining labels do not become accessible tab controls automatically.

    Why :target is not a replacement

    A link-based implementation using fragments and :target would provide same-page navigation:

    <a href="#panel-one">First</a>

    This can be an accessible navigation pattern, but it is not a tabs widget:

    • controls are links rather than tabs;
    • activation changes the URL and browser history;
    • focus does not follow the tabs interaction model;
    • ARIA selection state is not maintained;
    • the browser may scroll to the target panel.

    It should therefore remain a same-page navigation pattern rather than being described as tabs.

    Requirements of a real tabs component

    The established tabs pattern requires:

    • a labelled tablist;
    • one tab for each tabpanel;
    • aria-controls and aria-labelledby associations;
    • exactly one selected tab;
    • synchronized aria-selected;
    • roving tabindex;
    • predictable focus management;
    • arrow-key navigation;
    • Enter or Space activation when manual activation is used;
    • correct visibility and focusability of inactive panels.

    Reference: WAI-ARIA Tabs Pattern

    ARIA roles only expose semantics. Browsers do not add the required keyboard behaviour when role="tab" is declared. Authors must implement that behaviour.

    Reference: WAI-ARIA — Developing a Keyboard Interface

    CSS can select and present a state, but it cannot manage focus or update the corresponding ARIA attributes. Therefore, correct ARIA tabs currently require script.

    Could Cirth provide styles for externally scripted tabs?

    Technically, Cirth could style conforming application-provided markup:

    <div role="tablist" aria-label="Account">
      <button role="tab" aria-selected="true" aria-controls="profile">
        Profile
      </button>
      <button role="tab" aria-selected="false" aria-controls="security" tabindex="-1">
        Security
      </button>
    </div>

    This would keep the Cirth package itself CSS-only, while requiring the consuming application to provide the behaviour.

    However, it would not satisfy the original goal of a component that works without JavaScript. It would also make the component appear functional based on its styling even when the required script is missing or incomplete.

    Unless Cirth explicitly decides that “CSS-only” means only “Cirth does not bundle JavaScript”, rather than “the documented component works without JavaScript”, I do not think this should be shipped as a core component.

    Progressive enhancement considerations

    A safely enhanced tabs implementation should start with every section available in document order. JavaScript would then:

    1. add or initialize the tab roles;
    2. associate tabs and panels;
    3. initialize the selected state;
    4. hide inactive panels;
    5. enable keyboard and focus management.

    If the script fails, all content remains visible.

    This is a valid application architecture, but it cannot be completed by Cirth CSS alone.

    Native platform status

    Open UI has been researching a native tabs control, including semantics, focus management, keyboard behaviour and a progressive fallback model.

    Reference: Open UI Tabs research

    Until such a primitive is standardized, implemented and sufficiently interoperable, every CSS-only workaround would either expose incorrect semantics or implement only part of the required behaviour.

    Recommended resolution

    Defer the tabs component.

    The issue can document the decision as follows:

    • <details name> is a disclosure/accordion pattern, not tabs;
    • radio controls may implement a separate view selector, but not ARIA tabs;
    • :target implements navigation, not tabs;
    • conforming tabs currently require application-level JavaScript;
    • Cirth will reconsider the component when a suitable native HTML primitive becomes available.

    This avoids shipping a component that looks correct while exposing incomplete or misleading behaviour to keyboard and assistive-technology users.

  5. moved this from 📝 Todo to ⛔ Blocked in Cirth roadmapon Aug 15, 2026
  6. moved this from ⛔ Blocked to 📥 Backlog in Cirth roadmapon Aug 15, 2026
  7. changed the title [-]Evaluate a tabs component (upstream pico discussion #608)[/-] [+]Defer tabs until a native accessible HTML primitive is available[/+] on Aug 20, 2026
  8. added
    🧊 wontfixValid request that is intentionally not planned for implementation.
    and removed
    🧭 decision neededNeeds a product, API, or maintainership decision before implementation.
    on Aug 20, 2026
  9. moved this from 📥 Backlog to ⛔ Blocked in Cirth roadmapon Aug 21, 2026
  10. ricpastori commented on Oct 14, 2026

    @ricpastori
    CollaboratorAuthor

    Audit del 14 ottobre 2026: chiudo questa issue come non pianificata. Il titolo e la decisione già registrata stabiliscono il rinvio finché HTML non offrirà una primitiva tabs nativa, interoperabile e accessibile. Il sorgente corrente non espone una API tabs; l’issue potrà essere riaperta se cambia lo stato della piattaforma.

  11. moved this from ⛔ Blocked to ✅ Done in Cirth roadmapon Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ♿ accessibilityAccessibility, assistive technology, keyboard use, or WCAG compliance.⬆️ upstreamInherited from or coordinated with the upstream Pico CSS project.🧊 wontfixValid request that is intentionally not planned for implementation.

    Fields

    Priority

    🌱 P3 — Low

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions