What is the accessibility tree, and how does an element get its 'accessible name'?
Understanding that assistive tech reads a parallel tree, and how name computation works.
The browser builds a second tree alongside the DOM — the accessibility tree — where each node is reduced to what assistive technology needs: a role (what it is), a name (what to call it), a value/description, and states (checked, expanded, disabled). Screen readers, voice control, and testing tools read THIS tree, not your CSS. So an element that looks like a button on screen but is a bare <div> is, to a screen reader, an unnamed generic node it cannot describe or operate. The 'accessible name' is computed by a defined algorithm (accname) that walks a priority order: aria-labelledby (point at other elements' text) wins, then aria-label (a string you supply), then the element's own content or native labelling — a <label> for a form control, alt for an image, the text between a button's tags. If none of those yield text, the element is nameless and a screen reader announces just its role ('button') or nothing useful. The practical rules that fall out: give every control a name via its visible text where possible (so the name matches what a voice-control user says), reserve aria-label for icon-only controls, and remember content-based naming means <button>Delete</button> is already named — no ARIA required. display:none and aria-hidden prune a node from the tree entirely.
Debugging why a control is announced wrong, and deciding between visible text, aria-label, and aria-labelledby.
<!-- Name from content (best: visible text = accessible name) -->
<button>Delete</button>
<!-- Icon-only: no text content, so supply a name with aria-label -->
<button aria-label="Delete"><svg aria-hidden="true">…</svg></button>
<!-- Name from another element's text (labelledby wins over label/content) -->
<h2 id="sec-title">Billing</h2>
<section aria-labelledby="sec-title">…</section>