Leaving slide mode.
Fixing HTML element references (IDREFs) on the Web
Matthew Tylee Atkinson, Alice Boxhall, Lea Verou, Sarah Higley, Noam Rosenthal, Jeffrey Yasskin
TPAC 2025
Kobe, Japan & online
10–14 November 2025
Ground rules
- No
qq+
- We want problems, not solutions
- We are listening to the problems, and collecting them (not arguing with them)
Goals for this session
To find areas of consensus regarding
- the pain points of IDREFs
- expected costs and risks from an accessibility perspective
- the possibilities for conducting research to quantify the above
Non-goals
- Propose solution
- Fully flesh out the use case(s) and concerns
Agenda
- What (if anything) needs fixing?
- Prior art
- Main part: discussion
- Work towards consensus on the problem space
- Discuss constraints on solutions
- Concerns, questions, research needed
What (if anything) needs fixing?
Themes from developer feedback so far:
- Repeated content requires rewriting IDs for each repetition
- Code snippets may be copy-pasted multiple times; only IDs need to be edited each time
- Inline SVG using fragment identifiers regularly causes ID collisions
- Combining content from multiple sources (e.g. user-generated content) risks causing ID collisions
Themes from developer feedback so far:
- No platform support for generating/managing unique IDs
- Being forced to add a build step or use JS
- An ID may be generated in one component but used in another, creating a coordination problem
Themes from developer feedback so far:
General concerns
DX
- Reducing the pain points.
- Identifying the right pain points.
- Preserve developer flexibility.
UX/Accessibility
- Implementation and maintainance burden.
- Performance hit.
- Fragility of approach.
Prior art: Unique ID generation
useId() is a hook (function) provided by component frameworks Vue and React
- Generates a unique ID which can be used in templates
- IDs are stable across multiple runs (i.e. not randomly generated), as long as an element is in the same position in the tree
React useId()
export default function FormComponent() {
const inputId = useId();
return (
<form>
<label htmlFor={inputId}>Name:</label>
<input id={inputId} type="text" />
</form>
);
}
React useId()
The code on the previous slide my result in HTML something like this:
<form>
<label for=":r1:">Enter your name:</label>
<input id=":r1:" type="text">
</form>
Prior art: ID prefixing
- A unique string is added to IDs in repeated content
- The unique string may be generated or author-supplied
- There are build tools which do this specifically, and it can also be done using something like
useId()
ID prefixing using React useId()
export default function FormComponent() {
const prefix = useId();
return (
<form>
<label htmlFor={prefix + '-first'}>First name:</label>
<input id={prefix + '-first'} type="text" /><br />
<label htmlFor={prefix + '-last'}>Last name:</label>
<input id={prefix + '-last'} type="text" />
</form>
);
}
Prior art: ID prefixing
posthtml-rename-id is a plugin for PostHTML which specifically handles ID usage in SVG and CSS
fragment identifiers
- Fragment identifiers are used in in-page links (
<a href="#myId">),
CSS values (fill: url(#myId)) and SVG href attributes and
url() values
posthtml-rename-id
Input
<style>
.selector {
fill: url(#myId);
}
</style>
<div id="myId"></div>
<a href="#myId"></a>
Output
<style>
.selector {
fill: url(#prefix_myId);
}
</style>
<div id="prefix_myId"></div>
<a href="#prefix_myId"></a>
Prior art: Scoped ID rewriting
- Lets developers annotate a parent node to define a "group", within which all IDs are rewritten in a consistent
way by the framework
- In practice, this is similar to ID prefixing, but doesn't require developers to specify how to do the
rewriting
AlpineJS x-id and $id
<div x-id="['input']">
<label :for="$id('input')"> <!-- ➡ "input-1" -->
<input :id="$id('input')"> <!-- ➡ "input-1" -->
</div>
<div x-id="['input']">
<label :for="$id('input')"> <!-- ➡ "input-2" -->
<input :id="$id('input')"> <!-- ➡ "input-2" -->
</div>
Prior art: Using selectors to refer to elements
- This allows framework authors to specify an element as a target of some framework property via a selector, or
selector-like string
- This seems to be used less for platform features like label/for, and more for intra-framework operations
- Example code for this style of writing predominantly uses `#myId` selectors
htmx hx-target
<!-- A button that causes response-div to be updated -->
<button hx-post="/register"
hx-target="#response-div" hx-swap="beforeend">
Register!
</button>
<div id="response-div"></div>
htmx hx-target
<!-- A link that updates itself when clicked -->
<a hx-post="/new-link" hx-target="this"
hx-swap="outerHTML">
New link
</a>
Some questions…
(Just possibilities for the discussion, or to stimulate the queue.)
What are the pain points?
Is the problem generation of IDs, or communication accross component boundaries of
the IDs?
What other approaches have been used in libraries or tooling?
What are the (UA, AT) development costs?
Could devtools or offline tooling be improved to help the problem?
What are the performance costs?
How would any particular change benefit users?
What other constraints would you want on the potential solution space?
Research
We need to do research to identify the problems. That starts with you!
How can we ensure that the reseach is effective?
Can you help?
Themes from developer feedback were given above; more can be found as an appendix within this slide deck.
Discussion time!
Some points to consider…
- Our goal is to come to consensus on the problem space, which may need more research.
- If you have experience that expands or constrains the problem space, please let us know.
- If you have suggestions for research questions, or can help with research, please let us know.
Ground rules
- No
qq+
- We want problems, not solutions
- We are listening to the problems, and collecting them (not arguing with them)
Appendix: Quotes from developers
Theme: repeated content
- "labels use the ID, so you can't repeat similar forms and instead have to dynamically
generate unique IDs for every form"
- "The biggest pain point about IDs is the need for whole-page uniqueness, which conflicts
with component-based templates, especially when there are repeated components."
- "IDs don't work well with repeated content blocks, you have to uniquify them and refer to
that unique'd name"
- "[...] templates where I need to relate two elements together using an id [...] and the
template is usually repeated in the page."
Theme: unique ID generation
- "The platform doesn't help with basic needs like creating unique IDREFs."
- "Getting my developer community to care about generating unique IDs for every input is too
hard; they aren't doing it and our code is inaccessible."
- "having to come up with unique ids"
- "Html ul li counters as native performant unique ID generators would enable so much
functionality, it's hard to think of all the use cases."
- "I never found myself in a case where a random uuid or a framework provided tool didn't
solve the issue."
Theme: unique ID generation
Sub-theme: stable IDs
- "Guaranteeing uniqueness on a page and keeping that uniqueness consistent across page
renderings (e.g. if list elements have IDs, and an item gets inserted, do IDs remain
consistent?)
- "Random UUIDs not being ideal in static site generation as every page would be different
in every build, making selective syncing of actually changed pages to storage
impossible."
Theme: label/input
- "ids for everything for labels even though they're siblings"
- "Absolute ids for label input relations. Wish to have relative, form - scoped binding
- "settings ids and labels is very repetitive"
Theme: label/input
Sub-theme: label/input wrapping
- "Use to be if you nested an input within a label that was good enough to establish a
relationship but now we always need ids which seems like overkill."
- "That nesting inputs in labels have fallen out of favor and ids are wanted for nested
inputs"
- "the wrapping does not always make sense to the structure and we don't always want to
create ids just for the labels"
- "the fact that wrapping an input with
<label> is not recommended for
accessibility means ensuring unique ids per field"
Theme: SVG
- "state of SVG url references and canvas filter ids inside shadow DOMs"
- "SVG masks requiring unique IDs"
- "IDs in inline SVGs needing to be unique on the page"
- "In SVG, for example, defining a gradient requires creating a
<defs> block,
assigning it an id, and referencing it using url(#id) inside the
same SVG. When that SVG is duplicated (for example, multiple icons on a page), all those
references end up pointing to the first matching ID in the document."
Theme: unique ID generation
Sub-theme: getting generated IDs to both ends of a pair
- "Generating unique-but-matching for-ID pairs in component-based dev frameworks, especially
when the actual input is separated from its label and potentially seventeen levels deeper. "
- "Passing ids betwen files [in component libraries] is tricky."
- "whole page uniqueness [is] a bit tricky to satisfy in some rare situations because the
different elements linked by the IDREF are not in the same component."
Theme: JS/toolchain requirement
- "Frequently have to append a uuid or database ID to make it prefixed but unique, which
means JS required."
- "Most current solutions depend on external tools to handle [ID prefixing] automatically,
but that often makes things worse in real projects due to tool limitations or collisions in
the processing pipeline."
Please file issues on the repo to talk about gaps you've noticed!