Skip to content

fix(nav): collapse a responsive button group, and give every nav a first entry - #2158

Merged
markdumay merged 3 commits into
mainfrom
fix/nav-buttons-responsive
Sep 6, 2026
Merged

fix(nav): collapse a responsive button group, and give every nav a first entry#2158
markdumay merged 3 commits into
mainfrom
fix/nav-buttons-responsive

Conversation

@markdumay

Copy link
Copy Markdown
Collaborator

Three defects in the nav component, found while tracking down why a tab-type="buttons" group's id never appeared in the rendered HTML.

1. A responsive button group lost its controls instead of collapsing them

responsive hides the nav controls below the breakpoint and renders a dropdown in their place, but the hide applied to every tab type while the dropdown was gated on the type not being a button group. A responsive tab-type="buttons" therefore had no controls at all at narrow widths — the panes still rendered, with no way to reach any but the active one. responsive defaults to true, so that was what a button group did unless its author opted out.

The companion now renders for every type, so the two gates agree. The button group announces it through data-companion the way the list already announces its own, and its toggle takes the button treatment assets/table.html gives the filter group it collapses.

Two identifier defects sat underneath, because the dropdown carries no behaviour of its own and works only by replaying a click on the control its data-link names:

  • id named no element. It is documented as the "unique identifier of the current element", but the list took nav-{id} and a button group took nothing, so #{id} resolved to nothing at all. The wrapper now carries the caller's value verbatim, and only when one was given — stamping the shared fallback there would repeat a single id down the page.
  • The two branches spelled {id}-btn-{index} differently, the button group from the argument's fallback and the list and the nav-show lookup from the raw argument. A nav rendered without an id emitted id="%!s(<nil>)-btn-0" while pointing its panes and its companion at 0-btn-0.

2. A nav whose items were all unmarked showed an empty content area

assets/nav.html has always marked the first control active when nav-show is unset, but a pane's active class came from the item's own show and from nothing else. An author who marked no item — which the shortcode never required and never warned about — got a highlighted tab above nothing, since .tab-pane is display:none until something activates it.

An item cannot settle this alone: it renders in document order and cannot know whether a later sibling will claim show. nav-item now hands its parent a description of its pane rather than rendered markup, and nav resolves the entry once — an explicit show if any item reported one, its first entry otherwise — before rendering the panes and passing the same value on as nav-show. Control, pane and companion then read one decision, and an author who did mark an item still wins.

The accordion rendering keeps its eager path: nothing in the theme reads alternative, but an override of the nav shortcode reaches it the way it reached inner, so that contract is left as it was.

3. fade on the group aborted the nav

nav-item reads fade off its parent and falls back to it, and the shared argument definition marks fade as cascading — but the nav structure never declared it. Writing it on the group did not cascade, it aborted: InitArgs rejected it as unsupported, $args.err was set, and assets/nav.html rendered nothing. Declaring it makes the cascade the item shortcode already implements reachable.

Verification

27 assertions added to tests/templates, across four sections — at the partial, and through the shortcodes where the identifier chain and the fallback only exist. Each was run against the unfixed templates:

Section fixed unfixed
NAV — companion gating, identifiers 12 PASS 8 FAIL, 4 guards pass
NAV CHAIN — wrapper → control → pane → data-link 6 PASS 4 FAIL, 2 guards pass
NAV START — the fallback entry 6 PASS 2 FAIL, 4 guards pass
NAV FADE — the cascade 3 PASS 3 FAIL (unsupported argument 'fade')

The guards are deliberate: they hold what a naive repair would break — an opted-out group gaining a dropdown it did not ask for, an id-less nav stamping the shared fallback on its wrapper, and the first-entry fallback overriding an author who did mark an item.

Also driven in a real browser (Chromium, hugo server), 21 checks across the three fixes:

  • 390px — button group hidden, companion shown, opening it and picking the second entry switches the pane and relabels the toggle.
  • 1280px — buttons shown, companion hidden, clicking a button keeps the companion in step and marks the same entry.
  • Unmarked nav — first pane visible and readable, first control active, switching still works.
  • fadetransitionProperty includes opacity, intermediate opacities sampled through the switch, ends opaque, outgoing pane gone. Not an instant swap and not a transparent ghost.

pnpm test and pnpm build both exit 0.

Note on the version marker

release: v3.24.0 on the new fade argument assumes this lands as the next minor, which the feat: commit produces. Happy to change it if the release lines up differently.

🤖 Generated with Claude Code

markdumay and others added 3 commits September 6, 2026 12:52
`responsive` hides the nav controls below the breakpoint and renders a
dropdown in their place, but the hide applied to every tab type while the
dropdown was gated on the type not being a button group. A responsive
`tab-type="buttons"` therefore had no controls at all at narrow widths --
the panes still rendered, with no way to reach any but the active one --
and since `responsive` defaults to true, that was what a button group did
unless its author opted out.

Render the companion for every type, so the two gates agree. The button
group announces it through `data-companion` the way the list already
announces its own, and its toggle takes the button treatment
assets/table.html gives the filter group it collapses: a control standing
in for buttons has to read as one rather than as body copy.

Two identifier defects sat underneath, because the dropdown carries no
behavior of its own and works only by replaying a click on the control its
`data-link` names. `id` is documented as the "unique identifier of the
current element" but named no element: the list took `nav-{id}` and a
button group took nothing, so `#{id}` resolved to nothing at all. The
wrapper now carries the caller's value verbatim, and only when one was
given -- stamping the shared fallback there would repeat a single id down
the page.

The two branches also spelled `{id}-btn-{index}` differently, the button
group from the argument's fallback and the list and the `nav-show` lookup
from the raw argument. A nav rendered without an id emitted
`id="%!s(<nil>)-btn-0"` while pointing its panes and its companion at
`0-btn-0`, so `nav-show` could never match and the companion could never
address a control. Both now read the same variable.

Assertions in tests/templates cover all three, at the partial and through
the shortcodes, along with guards for an opted-out group that must not
gain a dropdown it did not ask for and an id-less nav that must not stamp
the fallback on its wrapper.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
assets/nav.html has always marked the first control active when `nav-show`
is unset, but a pane's `active` class came from the item's own `show` and
from nothing else. An author who marked no item -- which the shortcode
never required and never warned about -- got a nav with a highlighted tab
above an empty content area, since `.tab-pane` is display:none until
something activates it.

An item cannot settle this alone. It renders in document order and cannot
know whether a later sibling will claim `show`, so the fallback can only
be resolved where the whole set is visible. The nav-item shortcode now
hands its parent a description of its pane rather than the rendered
markup, and the nav shortcode resolves the entry once -- an explicit
`show` if any item reported one, its first entry otherwise -- before
rendering the panes and passing the same value on as `nav-show`. Control,
pane and companion dropdown then read one decision instead of each
defaulting on its own, and an author who did mark an item still wins.

The accordion rendering keeps its eager path. Nothing in the theme reads
`alternative`, but an override of the nav shortcode reaches it the way it
reached `inner`, so the contract is left as it was.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The nav-item shortcode reads `fade` off its parent and falls back to it
when the item sets none, and the shared argument definition marks `fade`
as a cascading one. The nav structure never declared it, so the cascade
could not be reached: InitArgs rejected `fade` on the group as an
unsupported argument, `$args.err` was set, and assets/nav.html rendered
nothing at all -- an author reaching for the documented cascade lost the
whole nav rather than gaining an animation.

Declare it, scoped to the shortcode group, since the cascade lives in the
item shortcode and a partial caller renders its own panes. The behaviour
it unlocks is the item shortcode's existing one: the group's value is
or-ed with the item's, so a group that sets `fade` fades every pane and an
item cannot opt back out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@netlify

netlify Bot commented Sep 6, 2026

Copy link
Copy Markdown

Deploy Preview for gethinode-demo ready!

Name Link
🔨 Latest commit 2def440
🔍 Latest deploy log https://app.netlify.com/projects/gethinode-demo/deploys/6a9d4a410c117b0008fc67ab
😎 Deploy Preview https://deploy-preview-2158--gethinode-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@markdumay
markdumay enabled auto-merge September 6, 2026 11:16
@markdumay
markdumay merged commit 6a0c065 into main Sep 6, 2026
17 checks passed
@markdumay
markdumay deleted the fix/nav-buttons-responsive branch September 6, 2026 11:16
@markdumay

Copy link
Copy Markdown
Collaborator Author

🎉 This PR is included in version 3.24.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant