Accessible Link-Label Helpers for Repeated CTAs
Two Liquid snippets that resolve a real conflict between two link-naming rules — identical link text on different destinations versus an accessible name that must contain the visible label — by deriving a name from the link's href and emitting it only when it differs from what the shopper can see.
Two accessibility rules pull against each other: links to different destinations shouldn't share the same text, and a link's accessible name must contain its visible label. A slideshow of five "Shop Now" buttons breaks the first; fixing it with aria-label breaks the second. These two Liquid snippets derive a name from the href and substitute it only when it would actually differ from the visible text.
The Problem
WCAG's link-purpose criterion says a screen-reader user should be able to tell from a link's name where it goes. A homepage slideshow with five slides, each ending in a "Shop Now" button pointing at a different collection, fails it five times: in a links list, every entry reads "Shop Now."
The obvious fix is an aria-label — "Shop Now: Blush Balms" — and it runs straight into WCAG's label-in-name criterion, which requires that a control's accessible name contain the text a sighted user can see, so that someone using voice control can say what they read and have it work. An aria-label that replaces "Shop Now" with something else satisfies one rule by breaking the other.
Both criteria land on the same button here, and the design team wasn't willing to rewrite every CTA's visible copy to make the links distinct. The buttons had to keep saying "Shop Now."
The Constraint
At render time, the only thing that distinguishes one "Shop Now" from another is its destination. So the accessible name had to be derived from the href — and it had to be substituted only when it would actually differ from the visible text, because a derived name identical to the visible one is just the first rule violated again with more markup.
The helpers also had to be callable from any section, in Liquid, without JavaScript, without new settings and without a new snippet convention to learn: the theme is built on Dawn 6.0.2, its sections were authored by several hands, and the fix had to drop into a slideshow, a banner and a category grid without changing how those sections were written.
What We Built
anchor-aria-label.liquid turns an href into a human label. It takes the last path segment of the URL, replaces hyphens with spaces and capitalizes the result; a link to a blush-balms collection yields "Blush balms." Callers use its output as the aria-label value on a link whose visible text is a generic verb.
anchor-text.liquid goes further and encodes the actual rule. It handleizes both the href-derived label and the visible text and compares them. If they match — the visible text already names the destination — it renders the visible text plainly and adds nothing. Only if they differ does it emit a visually hidden href-derived name alongside the visible label marked aria-hidden, so the accessible name is the destination and the visible label is still on screen for a sighted user.
Both snippets carry a header comment that documents the two rules they reconcile and shows a usage example, so the next developer who reaches for an aria-label on a CTA finds the reasoning where the code is.
Why This Way
The handleize-and-compare in anchor-text is the whole point. Substituting the name unconditionally would double up the accessible name on every link whose visible text already describes its destination — "Blush Balms" would become "Blush balms Blush Balms" to a screen reader — and that's a regression on the links that were fine. Comparing normalized forms means case, spacing and punctuation don't produce a false difference, so "Shop now" and a handle of shop-now are treated as the same label.
Doing it in Liquid keeps the fix in the server-rendered HTML. The accessible name is present in the first response, with no script to run, which matters to a screen reader and to anything else that reads the delivered markup.
There's a limit built into the approach. What a screen-reader user hears is the merchandiser's URL handle turned into words. A handle chosen to read well — a product line, a collection name — reads well as a label; a handle chosen for internal bookkeeping doesn't, and nothing in the snippet can improve on the URL it's given. The first snippet is also the blunter tool: anchor-aria-label replaces the visible text outright; anchor-text exists for the cases where the visible text might already be right.
Implementation Notes
anchor-aria-label.liquidis used by four sections:custom-banner.liquid,index-header-slider2.liquid,featured-collections.liquidandshop-by-category2.liquid.anchor-text.liquidis used byshop-by-category2.liquid, where the visible text of a tile may or may not match the category it links to.- Both snippets document the rule conflict they resolve in a header comment, with a usage example.
- The comparison in
anchor-textruns on both strings after Liquid'shandleizefilter, so the match is insensitive to case, whitespace and punctuation.
Edge Cases
- Visible text that already matches the destination renders without duplication:
anchor-textemits the plain label and no hidden name. - A visible label that differs from the handle only in case or spacing is treated as a match, so "Shop now" against a
shop-nowdestination adds nothing.
Platform Primitives Used
liquid-snippets— both helpers are snippets rendered inline by the calling section, taking the href and the visible text as arguments and returning markup.
Where It Runs
On the home page through the header slider and the featured-collections and shop-by-category sections, on the collection listing and on landing pages wherever the custom banner section is placed. The snippets aren't tied to a template; they run wherever one of those four sections renders a CTA.
What This Demonstrates
- Design system and reusable component library — the primary capability. Two documented Liquid authoring primitives that any section can call, so the accessibility decision is made once and reused rather than re-derived per section.
How We Know
Two Liquid snippets of roughly 45 lines together, read from the theme, plus one client task-register record. The record documents an accessibility optimization task in early 2025 but doesn't itemize what was changed, so attributing these snippets to that pass is an inference from timing and subject; the snippets' own header comments are the clearest statement of why they exist. The mechanism is read from the code.
Related Builds
- Accessible play/pause web component for autoplaying hero video — the same storefront's other accessibility primitive, on the same homepage slider: a control whose state tracks the video rather than a toggled class.
- PDP content accordion that borrows the premium theme's collapsible contract — a different storefront's accessibility-conscious component, driving the theme's own disclosure runtime rather than shipping one.
- Detached mobile drawer with three-level native-details navigation — a different storefront's navigation built on native
<details>so the disclosure semantics come from the platform.
The Buy-vs-Build Question
A snippet library is the thing a theme either owns or re-derives in every section that needs it. Building these two helpers bought one place where the link-naming rule is written down, callable from any section without a new convention to learn — and cost a label that's only as good as the URL behind it. Whether a storefront should own a section and snippet library at all is the question on the theme section library and design system decision page.
Provenance & Evidence
- Client: Nudestix — nudestix.com
- Surfaces: Home page, collection listing, landing page
- Calling sections: four —
custom-banner.liquid,index-header-slider2.liquid,featured-collections.liquid,shop-by-category2.liquid - Complexity: Low
- Attribution: Deploi-authored. Both snippets and their header documentation are ours. They run in a theme based on Dawn 6.0.2 and are called from four of that theme's sections.
- Status: Live, verified 2026-09-06
- Evidence: Two Liquid snippets, plus one client task-register record
- Confidence: Strong — the code is read directly from the theme; the client record documents an accessibility pass in the same period without itemizing it
- Primary capability: Design system and reusable component library
Ready to Name Every Link Without Rewriting Every Button?
You dream it. We build it. If two link-naming rules on your storefront seem to contradict each other, Contact us today and we'll show you how a few lines of Liquid can satisfy both.