Own the markup
Take behavior from a library, and write the markup yourself.
Adam Wathan@adamwathanIt's *so* much easier to build the right markup yourself than it is to try and hack your design into the wrong starting point. I end up rebuilding every simple UI component from scratch for every project; this approach finally gives me hope I won't have to ๐๐ป
Adam Wathan@adamwathan@kentcdodds Downshift's approach of handling all of the quirky logic for you while deferring all of the markup generation to the end user is so much more developer-friendly, even if it initially looks like more work.
Both panes share one select controller. Only the markup differs.
Assign an owner
Open both, use the arrow keys, then swap your markup.
Fixed markupBad
options: string[]HeadlessGood
renderOption(item)const select = useSelect({ items, onChange });
<button {...select.trigger}>
{select.value ? <Person p={select.value} /> : "Pick a person"}
</button>
<ul {...select.list}>
{items.map((p, i) => (
<li {...select.option(i)}><Person p={p} /></li> // your markup
))}
</ul>
Why it matters, pros and cons
Why it matters
Keyboard, focus and ARIA are the hard part. The markup is the part your design changes.
Pros
- Any layout is reachable: avatars, badges, tiles.
- Behavior is written and tested once.
- No overriding another library's class names.
Cons
- More code up front than a drop-in widget.
- You must wire the props onto the right elements.
- For a plain native select, use the native select.