fix(shortcodes): migrate .Ordinal ids to the UniqueID helper#2026
Merged
Conversation
Twelve shortcodes build a DOM id from .Ordinal, which resets to 0 inside the example shortcode's embedded RenderString, so two accordions in two example blocks collide on accordion-0. Migrate the 11 real id sites to UniqueID: self-contained shortcodes swap directly; accordion/nav parent-child share the id lazily through the parent's Scratch, since Hugo renders children before the parent body. img is excluded (its ordinal is a parent-scoped slide index). Folds in an unrelated preview.html fix: a dangling aria-labelledby in the fallback path, where panes reference tab controls the fallback never renders.
Seven tasks across hinode, mod-lottie, mod-leaflet and mod-blocks. Each branch is independent (mod-utils v6.5.0 already released); merge in any order.
carousel, navbar, toast, file and docs built their id from .Ordinal, which resets to 0 inside the example shortcode's embedded RenderString - so two of a component in two example blocks collided. Use the UniqueID helper, which counts from a page Store counter. The explicit id argument still overrides.
The item rebuilt its parent's id from .Parent.Ordinal, which collides inside the example shortcode. A UniqueID counter cannot be recomputed by the child, and Hugo renders children before the parent body so the parent cannot push its id down. Instead the first renderer mints the id and stores it in the parent's Scratch; the parent and its other items read it back. The explicit id argument still wins.
nav-item rebuilt its parent's id from .Parent.Ordinal, which collides inside the example shortcode. Resolve it the same way as the accordion: the first renderer mints a UniqueID and stores it in the parent's Scratch; the nav container and its items read it back. The explicit id argument still wins.
The carousel indicator buttons targeted #carousel-{id}, doubling the
"carousel" prefix already present in $id (e.g. #carousel-carousel-UID-1).
This id never existed, so clicking an indicator did nothing on any
multi-slide carousel. The prev/next controls already targeted #{id}
correctly; align the indicators to match.
Give the id-demo fixture carousels two slides each so indicators
actually render and the fix is verified end to end.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
✅ Deploy Preview for gethinode-demo ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Collaborator
Author
|
🎉 This PR is included in version 3.2.3 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
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.
Shortcodes built their DOM id from Hugo's
.Ordinal, which resets to 0 inside theexampleshortcode's embeddedRenderString. So two of a component in two{{< example >}}blocks both becameaccordion-0, and Bootstrap'squerySelector-baseddata-bs-parentdrove the wrong element — the second accordion's toggle opened the first's panel. This hit the component-documentation pages hardest, where every component is wrapped in{{< example >}}.Every
.Ordinal-derived id now uses mod-utils'UniqueIDhelper (<prefix>-UID-<n>, from a per-pageStorecounter).Two mechanisms
idoverride..Parent.Ordinal. A counter can't be recomputed by the child, and Hugo renders children before the parent body — so the parent can't push its id down. The fix is a lazy shared id: whichever renders first mints the id and stores it in the parent's.Scratch; the parent and its other items read it back. The explicitidstill wins, and the child keeps its own.Ordinalas the parent-scoped item index for the-item-<n>/-btn-<n>suffix.imgis untouched — its.Ordinalis a parent-scoped active-slide test, not an id.Also here
A pre-existing carousel bug the migration surfaced: the indicators targeted
#carousel-{{ $id }}(a doubled prefix → a non-existent id) while prev/next used#{{ $id }}, so indicator clicks were dead on multi-slide carousels. Corrected to#{{ $id }}, and the fixture strengthened to two-slide carousels that browser-verify each carousel's indicators advance only its own.Note on generated ids
Every auto-generated id changes shape (
accordion-0becomesaccordion-UID-1). These ids are auto-generated and undocumented, and every affected shortcode still honours an explicitidargument — the escape hatch for a stable anchor. This is an implementation-detail change, cut as afix:patch; the caveat belongs in the release note. (Deliberately not flagged as a semver-major change — the ids are not a public contract.)Verification
exampleSite/content/en/id-demo.mdis a permanent regression fixture: each component twice, inside separate{{< example >}}blocks (escaped-nesting idiom, so they render live through the example shortcode'sRenderString— exactly the collision path). Whole-site scan confirms no bare<prefix>-<n>id remains and the fixture's components get distinct-UID-ids. In a browser: the second accordion's item opens only its own panel (the core regression), the second nav's tab switches only its own pane, carousels advance independently, and the explicit-idoverride flows through parent and child — zero uncaught console exceptions.Companion PRs (independent — mod-utils v6.5.0 already released)
animationmapEach carries a
fix:commit; merge in any order. Hinode's exampleSite renders bare-Nlottie/leaflet ids until those two modules release andexampleSite/go.modis bumped — a known post-merge follow-up.🤖 Generated with Claude Code