Prepare for HTML interviews with questions grouped by experience level, from semantic markup to accessibility and Web Components.
Junior (0-2 years)
HTML, HyperText Markup Language, defines the structure and content of a web page, the headings, paragraphs, links, and other elements that make up what you actually see. It works alongside CSS, which handles visual styling, and JavaScript, which handles behavior and interactivity, together forming the three core building blocks of the web.
A doctype declaration at the top tells the browser which version of HTML to expect. An html element wraps everything else, containing a head section for metadata (title, linked stylesheets, meta tags) and a body section holding the actual visible content of the page.
A tag is the actual markup syntax, like <p> or </p>. An element is the tag plus its content, like <p>Hello</p> as a whole unit. An attribute provides extra information about an element, written inside the opening tag, like the href attribute on an anchor tag specifying where a link points.
<!DOCTYPE html> tells the browser to render the page in standards mode, following the modern HTML5 specification. Without it, some browsers fall back to quirks mode, which can produce inconsistent and unpredictable rendering behavior compared to what the actual specification defines.
A block-level element, like a div or a paragraph, takes up the full width available and always starts on a new line. An inline element, like a span or an anchor tag, only takes up as much width as its content needs and flows within the surrounding text rather than forcing a line break.
The head contains metadata about the page, the title, character encoding, linked stylesheets, meta tags, none of which is directly visible as page content itself. The body contains the actual visible content that renders on the page, everything the user actually sees and interacts with.
h1 through h6 represent headings of decreasing importance, with h1 typically reserved for a page's single main heading. They should be used in a logical, hierarchical order reflecting the actual structure of the content, not chosen based on how large or bold a heading happens to look, since visual styling is CSS's job, not HTML's.
<a href='https://example.com'>Visit</a> creates a clickable link. The href attribute specifies the destination URL the link points to, whether that's an external website, another page on the same site, or an anchor point within the current page.
<img src='photo.jpg' alt='A description of the image'> embeds an image. The alt attribute provides a text description used by screen readers for visually impaired users, and also displayed if the image itself fails to load, making it a genuine accessibility feature rather than an optional extra.
An ordered list (ol) numbers its items sequentially, appropriate when the actual order matters, like a set of instructions. An unordered list (ul) displays items with bullet points instead, appropriate when the order among items doesn't carry any particular meaning.
dl, paired with dt for a term and dd for its description, represents a list of term-description pairs, like a glossary or a set of key-value metadata. It's less commonly used than ordered or unordered lists, but it's the semantically correct choice specifically when content genuinely has that term-and-definition structure, rather than forcing it into a generic unordered list instead.
A div is a generic, non-semantic block-level container with no inherent meaning of its own, used purely for grouping and styling content. It's criticized when overused, sometimes called div soup, because a page built entirely from generic divs conveys no meaningful structural information to a screen reader or a search engine, unlike a page using proper semantic elements.
A span is an inline element, used for grouping a small piece of text or content within a line without breaking the surrounding flow. A div is a block-level element, used for grouping larger sections of content that should start on their own new line.
A form element groups together a collection of input controls, letting a user enter and submit data, like a login form or a search box. When submitted, the form sends its collected data to a server, either through a GET or POST request, depending on the form's configured method.
GET appends form data directly to the URL as query parameters, visible in the browser's address bar, generally suitable for a search or filter where the submitted data isn't sensitive. POST sends form data in the actual request body instead, not visible in the URL, and is the appropriate choice for anything sensitive or anything that actually changes data on the server, like a login form.
text for plain text entry, password for masked text entry, email for an email-formatted input with basic built-in validation, checkbox and radio for selecting one or more options, and submit for the button that actually triggers form submission. HTML5 added several more specific types too, like date, number, and tel.
A checkbox lets a user select any number of options independently, including none or all of them. A set of radio buttons sharing the same name attribute restricts selection to exactly one option among the group, since selecting a new one automatically deselects whichever was previously selected.
A label associates descriptive text with a specific form control, either by wrapping the control directly or by using a for attribute matching the control's id. Beyond the visual benefit, clicking anywhere on the label text also focuses or activates the associated input, and screen readers announce that label when the input receives focus, making it a genuine accessibility feature rather than mere decoration.
It prevents a form from being submitted if that field is left empty, triggering the browser's own built-in validation message. Its limitation is that it only handles basic presence validation, not more complex rules like a password meeting specific complexity requirements, which still needs custom validation logic, whether in HTML pattern attributes, JavaScript, or on the server itself.
Semantic HTML means using elements that convey actual meaning about the content they contain, like article, nav, or header, rather than generic elements like div for everything. It matters because it helps browsers, search engines, and assistive technologies like screen readers understand a page's actual structure and content, rather than seeing an undifferentiated pile of generic containers.
Visually, with the same CSS applied, they can indeed look completely identical. The real difference is the meaning conveyed to something reading the underlying markup rather than the rendered visual output, a search engine crawler, a screen reader, another developer reading the code. article explicitly signals self-contained content, while div signals nothing about content meaning at all.
header for introductory content or navigation at the top of a page or section. nav for a block of navigation links. main for a page's primary, unique content. article for a self-contained piece of content, like a blog post. section for a thematic grouping of content. footer for closing content, typically at the bottom of a page or section.
Search engines use semantic markup as a signal for understanding a page's actual structure and identifying its most important content, like distinguishing a main article from a sidebar or navigation. A page built entirely from generic divs gives a search engine far less structural information to work with when deciding how to interpret and rank that content.
main represents a page's single primary content area, and a page should have only one main element. article represents one specific, self-contained piece of content within a page, and a page can contain several article elements, like multiple blog post previews listed on the same page.
Heading tags convey a document's actual content hierarchy to a screen reader and a search engine, beyond simply a visual size. Choosing a heading level purely for its visual appearance rather than its actual place in the content's logical structure creates a confusing, misleading outline for anyone or anything relying on that structural information, even though the page might still look fine to a sighted user.
table wraps the whole table. tr defines a row. td defines a standard data cell within a row. th defines a header cell, typically rendered bold and centered by default, and importantly conveys header meaning to assistive technology, unlike a td styled to merely look like a header visually.
thead groups a table's header row or rows. tbody groups the table's main body content. tfoot groups footer content, like a summary row. These aren't strictly required for a table to work, but they add meaningful structure and can also help a browser render a very long table more efficiently.
<video src='movie.mp4' controls></video> embeds a video with visible playback controls. Other useful attributes include autoplay, muted (often required alongside autoplay for it to actually work in modern browsers), and poster to specify a preview image shown before playback starts.
<audio src='song.mp3' controls></audio> embeds an audio player with visible controls, following essentially the same pattern as the video element, just for audio content specifically.
Different browsers support different video and audio file formats. Providing multiple source elements, each specifying a different format, lets the browser pick and use the first format it actually supports, providing broader compatibility than relying on a single hardcoded format that might not work in every browser.
New semantic elements (header, nav, article, section, footer), native audio and video support without needing a plugin like Flash, the canvas element for drawing graphics directly, new form input types, and built-in client-side storage APIs like localStorage, all without needing third-party plugins that earlier HTML versions typically relied on for the same functionality.
canvas provides a blank drawing surface that JavaScript can draw onto directly, pixel by pixel, used for things like custom charts, simple games, or dynamic image manipulation, anything requiring programmatic, pixel-level graphics rather than static markup or CSS-driven visuals.
Both let a web page store simple key-value data directly in the browser. localStorage persists indefinitely, surviving even after the browser is closed and reopened. sessionStorage only persists for the duration of that specific browser tab's session, and is cleared once that tab is closed.
localStorage stores more data (typically several megabytes versus a cookie's few kilobytes) and is only accessible via JavaScript, never automatically sent to the server. A cookie is automatically included in every HTTP request to its matching domain, which makes it useful for server-side session tracking but adds overhead to every single request, even ones that don't actually need that data.
placeholder shows hint text inside an empty input that disappears once the user starts typing. It shouldn't replace a proper label because that hint text vanishes the moment the field has content, leaving no persistent, visible indication of what the field is actually for, and many screen readers don't announce placeholder text the same reliable way they announce an actual label.
An id must be unique within a page, identifying exactly one specific element, commonly used as a target for CSS styling, a JavaScript reference, or an internal page anchor link. A class can be applied to multiple elements at once, commonly used to apply the same shared styling or behavior to a group of related elements.
Yes. List multiple class names separated by spaces within the same class attribute, class='card highlighted featured'. This lets an element pick up styling or behavior from several independent classes at once, rather than being limited to exactly one class per element.
Mid-Level (3-6 years)
ARIA (Accessible Rich Internet Applications) provides attributes that add accessibility information to elements, particularly useful for custom, JavaScript-driven UI components that don't have a native semantic HTML equivalent, like a custom dropdown or a tab panel. The general guidance is to use a native semantic HTML element wherever one genuinely exists and fits, and reach for ARIA only when it genuinely doesn't.
Incorrectly applied ARIA attributes can actively make a page less accessible than having no ARIA at all, by giving a screen reader inaccurate or conflicting information about an element's role or state. Simply adding ARIA attributes without genuinely understanding their exact expected behavior can create a worse experience than just leaving well-structured, plain semantic HTML alone.
aria-label provides an accessible name directly as a string value on the attribute itself. aria-labelledby instead references the id of another element on the page whose text content is used as the accessible name, useful when the appropriate label text already exists visibly elsewhere on the page and shouldn't be duplicated.
Add appropriate ARIA roles and states (role, aria-expanded, aria-haspopup) to communicate the component's actual behavior to assistive technology, ensure it's fully operable using only the keyboard (arrow keys to navigate, Enter or Space to select, Escape to close), and manage focus properly as the dropdown opens and closes, moving focus logically rather than leaving it stuck somewhere unexpected.
A skip link, typically hidden visually until it receives keyboard focus, lets a keyboard or screen reader user jump directly past repetitive navigation content straight to a page's main content. Without one, someone navigating with a keyboard has to tab through an entire navigation menu on every single page before ever reaching the actual content they came for.
aria-label='Close' on the button provides an accessible name that a screen reader announces, since the icon itself, whether an image or an inline SVG, conveys no text meaning to assistive technology on its own. Alternatively, visually hidden text placed inside the button and styled off-screen, rather than fully hidden, achieves the same result without depending on ARIA specifically.
pattern accepts a regular expression that the input's value must match for the form to be considered valid, letting you enforce a specific format directly in HTML, like requiring a specific structure for a product code, without needing JavaScript for that particular validation rule.
Visually hide the native input (without using display: none, which would remove it from accessibility trees entirely) and pair it with a styled sibling element that represents the checkbox's visual appearance, keeping the actual native input functional underneath for keyboard interaction, form submission, and accessibility, while the styled sibling provides the custom visual look.
Client-side validation, using HTML attributes or JavaScript, gives immediate feedback to the user without a round trip to the server. Server-side validation is genuinely required for actual security and data integrity, since client-side validation can always be bypassed entirely by a user who disables JavaScript or sends a request directly, so relying on client-side validation alone leaves the actual data unprotected.
fieldset wraps the related group of controls, and legend, as its first child, provides a caption describing that group as a whole, which a screen reader announces when a user encounters any control within that group, giving important context that individual labels alone wouldn't convey.
It hints to the browser what kind of data a field expects, letting the browser correctly auto-fill it from previously saved information, like a saved address or payment card. Beyond convenience, correctly set autocomplete values are also considered an accessibility best practice, since they help users with certain cognitive or motor disabilities who benefit significantly from not needing to manually retype information they've already entered elsewhere.
<meta name='viewport' content='width=device-width, initial-scale=1'> tells a mobile browser to render the page at the device's actual width rather than a wider desktop-oriented default layout scaled down. Without it, a responsive site's CSS media queries won't correctly detect and adapt to an actual mobile screen size.
The title tag sets the text shown in a browser tab and in search engine results listings, but isn't visible directly on the rendered page itself. The h1 is the page's actual visible main heading. Search engines consider both as strong signals of a page's topic, and they should ideally be closely related, though not necessarily worded identically.
<meta name='description' content='...'> provides a short summary of the page's content that a search engine often, though not always, displays directly underneath the page's title in its search results listing. It doesn't directly affect search ranking itself, but a well-written one can meaningfully improve click-through rate from someone scanning search results.
Open Graph meta tags (og:title, og:description, og:image, and others) control how a page's content appears when it's shared on social media platforms like Facebook or LinkedIn, specifying the actual title, description, and preview image shown in that shared link's preview card, rather than leaving it up to a possibly inaccurate automatic guess by the platform.
The browser reads the HTML and builds the DOM (Document Object Model), a tree structure representing the page's actual elements and their nesting relationships. It similarly parses any linked CSS into the CSSOM. The two are then combined into a render tree, which the browser uses to actually calculate layout and paint pixels to the screen.
It's the specific sequence of steps a browser goes through, parsing HTML and CSS, building the DOM and CSSOM, laying out and finally painting the page, before a user actually sees meaningful content on screen. Understanding it matters because it reveals exactly where a page's load time is actually being spent, and which specific resources are genuinely blocking that first meaningful render from happening sooner.
A script tag placed in the head without defer or async blocks HTML parsing entirely while the browser downloads and executes that script, delaying when the rest of the page's content can even start rendering. Placing scripts at the bottom, or using defer to let the script download in the background while continuing to parse HTML and only execute once parsing finishes, avoids that render-blocking delay.
Both let the script download without blocking HTML parsing. defer additionally guarantees the script executes only after HTML parsing completes, and preserves the original order of multiple deferred scripts relative to each other. async executes the script the moment it finishes downloading, potentially before parsing completes and with no guaranteed order relative to other async scripts, which fits an independent script with no dependency on other scripts or the fully-parsed DOM.
A browser generally won't paint any content until it has processed the CSS needed to correctly style that content, so a large, render-blocking stylesheet delays a user seeing anything meaningful on screen at all. Inlining the small amount of critical CSS needed for above-the-fold content directly in the HTML, and loading the remaining, larger stylesheet asynchronously, is a common technique for reducing this specific delay.
navigator.geolocation.getCurrentPosition() requests the user's current location, prompting the browser to ask the user for explicit permission first, since location is genuinely sensitive data. The API returns coordinates through a callback function once permission is granted and the location is successfully determined.
The native HTML5 Drag and Drop API lets an element be dragged and dropped onto another. Key events include dragstart on the element being dragged, dragover on the potential drop target (which must call preventDefault to actually allow a drop there), and drop on the target when the dragged item is actually released onto it.
The Web Storage API, localStorage and sessionStorage, stores data directly in the browser with a simple key-value interface, offering more storage capacity than cookies and never being automatically sent to the server on every request the way a cookie is. It doesn't support setting an expiration time or a specific access scope the way cookies natively can, and everything stored is always accessible only through JavaScript, never directly by the server itself.
The History API, primarily pushState and replaceState, lets JavaScript modify the browser's URL and history stack without triggering an actual full-page reload from the server. This is what allows a single-page application to have distinct, shareable, bookmarkable URLs for different views while still behaving as a single continuously-loaded page underneath.
Senior (6-8 years)
The Web Content Accessibility Guidelines are the widely recognized standard for web accessibility, organized around four core principles known by the acronym POUR. Content must be Perceivable (available to the senses, like providing alt text for images), Operable (usable via keyboard and other input methods, not just a mouse), Understandable (predictable and clear), and Robust (working reliably across different browsers and assistive technologies).
Level A represents the most basic, minimum accessibility requirements. Level AA, the level most commonly targeted by legal requirements and organizational accessibility policies, represents a solidly usable standard for a genuinely broad range of users with disabilities. Level AAA is the strictest level, though it isn't always fully achievable for every single type of content, and isn't typically required or expected across an entire site.
Navigate the entire page using only the Tab key to move forward, Shift+Tab to move backward, and Enter or Space to activate elements, with the mouse deliberately set aside entirely. Check that every genuinely interactive element is reachable this way, that a visible focus indicator is always clearly present, and that the tab order actually follows a logical, sensible reading sequence through the page's content.
A focus trap keeps keyboard focus contained within a specific set of elements, most commonly used inside a modal dialog, so tabbing doesn't accidentally move focus to content behind the modal that a sighted user can't currently see or interact with anyway. It's appropriate specifically while a modal is genuinely open, and needs to be released properly, restoring focus to a sensible location, once the modal actually closes.
Provide the same underlying data in an accessible alternative form alongside the chart itself, like a properly structured, well-labeled data table, or a clear text summary describing the specific trend or key finding the chart is actually meant to convey. Relying purely on the visual chart, or a generic alt attribute alone, genuinely leaves a screen reader user without access to the real information the chart was created to communicate.
Automated tools reliably catch certain, well-defined categories of issues, like missing alt attributes or genuinely insufficient color contrast, but they typically catch only a fraction, often cited as somewhere around a third, of the total range of real accessibility issues a page might actually have. Manual testing with an actual keyboard, and ideally with real assistive technology like a screen reader, catches the more genuinely contextual, judgment-based issues that automated tools structurally can't evaluate on their own.
Lazy loading defers loading an image until it's actually about to scroll into the visible viewport, rather than loading every single image on the page immediately, all at once, on initial page load. loading='lazy' on an img tag enables this natively in modern browsers, with no JavaScript required at all for the basic behavior.
preload tells the browser to fetch a resource the current page will definitely need soon, prioritizing it accordingly ahead of other, lower-priority resources. prefetch fetches a resource likely to be needed for a future navigation, at a lower priority than the current page's own resources. preconnect establishes an early network connection, DNS lookup and handshake, to a domain the page will need shortly, without necessarily fetching a specific resource from it yet.
Serve appropriately sized images for the actual device and viewport using the srcset attribute, rather than sending one large, oversized image to every device regardless of its actual screen size. Use a modern, efficient format like WebP or AVIF where genuinely supported, and apply lazy loading for images that sit below the initial visible fold of the page.
srcset lets you specify multiple versions of an image at different resolutions, letting the browser choose the most appropriate one based on the actual device's screen size and pixel density. It solves the real problem of a single fixed image being either wastefully oversized for a small mobile screen, or blurry and low-quality when stretched onto a large, high-resolution desktop display.
LCP measures how long it takes for the largest, most visually significant content element to actually render on screen. Common ways to improve it include preloading the specific resource for that largest element (often a hero image), removing unnecessary render-blocking resources sitting ahead of it in the loading sequence, and ensuring the server itself responds quickly enough that the whole rendering process can even begin sooner.
CLS measures how much visible content unexpectedly shifts position as a page loads, a frustrating experience when a user is about to tap something and the layout suddenly jumps underneath them. HTML contributes to it when an image or an embedded ad has no explicitly reserved width and height, so the browser doesn't know how much space to allocate for it before it actually loads, causing surrounding content to shift once it finally does.
Lead (8-10 years)
Structured data adds explicit, machine-readable metadata about a page's content, like marking up a recipe's specific ingredients and cooking time, or a product's price and availability, in a standardized vocabulary search engines can reliably parse. This can enable rich search results, star ratings, prices, or availability shown directly in the search listing itself, beyond just a plain title and description snippet.
Web Components let you define genuinely custom, reusable HTML elements with their own encapsulated markup, styling, and behavior, using standardized native browser APIs rather than a specific JavaScript framework's own component system. They extend HTML's native semantics by letting you create a custom element, like <user-profile-card>, that behaves and can be styled much like any other native, built-in HTML element.
The Shadow DOM creates a genuinely isolated, encapsulated DOM subtree for a component, with its own separate styling scope that doesn't leak out to the rest of the page, and that isn't affected by the rest of the page's own styles leaking in either. This solves the real problem of CSS conflicts between a reusable component and whatever unpredictable page it eventually ends up being embedded into.
Deliberately choose semantic HTML elements within the framework's component templates rather than defaulting to generic divs everywhere out of pure convenience, add ARIA attributes explicitly and carefully where a genuinely custom interactive component has no native semantic equivalent, and specifically test the actual rendered DOM output rather than just the component source code, since a framework can sometimes render unexpected, non-obvious markup that differs from what the source templates might initially suggest.
Start from a single, semantically correct, logically ordered HTML structure that fundamentally makes sense read linearly, top to bottom, on its own, then layer CSS on top to handle purely visual, responsive rearrangement for different screen sizes. A page whose actual meaning and structure depends entirely on a specific visual layout to make sense at all is a strong sign the underlying HTML itself isn't genuinely semantic or accessible in the first place.
Progressive enhancement starts from a genuinely solid, functional HTML baseline that works even with no CSS or JavaScript at all, then layers on enhanced styling and behavior for browsers that fully support it. Graceful degradation instead starts from a fully-featured experience built assuming modern capabilities, then attempts to handle failure gracefully for older or more limited browsers. Progressive enhancement is generally considered the sturdier approach, since it guarantees a working baseline experience by actual construction, rather than by attempting to patch failures after the fact.
Check actual, current support data (a resource like caniuse.com) rather than assuming based on outdated or incomplete information, and use feature detection in JavaScript, checking whether a specific API or property genuinely exists before actually relying on it, rather than assuming universal support or relying purely on browser-version sniffing, which is generally considered a far less reliable approach.
A polyfill is a piece of code that implements a specific, newer feature's expected behavior for browsers that don't natively support it yet, letting you write code against the modern API while still functioning correctly on genuinely older browsers. You'd need one specifically when you need to support a browser that predates a specific feature you'd otherwise like to use, and the actual functionality itself, beyond just the syntax, can be reasonably implemented in plain JavaScript.
A polyfill specifically implements a standardized web platform feature so it behaves the same as the native version once available. A shim is a broader term for any code that intercepts and normalizes an API call, which could include a polyfill but could also just adjust behavior for compatibility reasons unrelated to matching a specific web standard. In everyday conversation among developers, the two terms are often used loosely as if they mean the same thing.
HTML was originally standardized through the W3C, but disagreements over the specification's actual direction led to the WHATWG (Web Hypertext Application Technology Working Group) developing HTML5 as a competing, living standard maintained by browser vendors themselves. The W3C and WHATWG eventually reached an agreement, with the WHATWG's continuously evolving living standard now serving as the single, authoritative source of truth for the specification going forward.
A versioned specification, like the older HTML 4.01, is fixed and doesn't change once formally published as a specific numbered version. A living standard, the current WHATWG approach for HTML, is continuously updated as new features are actively added and existing ones are refined, without waiting for a discrete, numbered version release the way older specifications historically did.
Check actual current browser support data against your application's genuinely real, current user base, not a generic assumption about browser usage in general. Consider whether a reasonable fallback or a polyfill is available for the small percentage of unsupported browsers, and weigh the real, concrete benefit the new feature provides against the real cost and effort of supporting that fallback path.
The simple <!DOCTYPE html> declaration ensures every modern browser renders the page in standards mode rather than a legacy quirks mode, which historically had significant, genuinely inconsistent rendering differences across different browsers. While standards mode itself is now essentially universal across current browsers, actual rendering differences in specific CSS or JavaScript feature support still exist and still require careful testing and consideration.
Staff (10+ years)
It shows up directly in decisions like whether a design system's components are built on genuinely accessible, semantic foundations from the very start, and whether a team's overall approach to markup will actually scale cleanly across dozens of pages and components built by many different developers over time, rather than each page being built ad hoc with its own inconsistent, one-off structural decisions.
Bake genuinely accessible markup directly into the design system's own shared components themselves, so a team consuming those pre-built components inherits good, accessible structure automatically by default, rather than relying on every single individual developer to remember and correctly apply every specific accessibility best practice manually on every single page they happen to build.
I check whether the semantic elements genuinely and accurately reflect the actual content's real meaning rather than visual convenience or habit, whether the structure would remain fully usable and understandable with CSS completely disabled, and whether it's genuinely, practically accessible via keyboard alone. A page that only makes real sense when it's fully styled and rendered visually is often a meaningful sign the underlying HTML itself isn't structured correctly to begin with.
I'd push back against treating accessibility as a purely optional nice-to-have that only gets addressed once time genuinely allows for it. Basic accessibility, semantic HTML, keyboard operability, sufficient color contrast, is achievable at minimal added cost when it's built in from the very start of a project, and it becomes dramatically more expensive and disruptive to properly retrofit later, after a genuinely inaccessible structure has already been built and shipped.
I'd look at where the actual pain is genuinely coming from, poor search engine visibility, real accessibility complaints or actual legal risk, or a specific area of the site that's become genuinely difficult and risky to maintain or safely extend, rather than modernizing purely because current best practices have simply moved on since it was originally built. Modernization is worth the real cost and effort once it's addressing a concrete, currently-felt problem, not as a purely cosmetic exercise for its own sake.
Check actual browser support data first for any specific HTML or CSS feature genuinely in use on that page, since the issue is very often a feature the affected browser simply doesn't fully support yet, rather than an actual, genuine bug in your own markup. Testing directly in the affected browser's own developer tools, rather than only guessing remotely from a bug report alone, usually reveals the actual, specific root cause fastest.
Automated accessibility testing, using a tool like axe-core, integrated directly into CI, catches a genuinely meaningful subset of common issues automatically and consistently on every single change. That should be paired with periodic, genuine manual testing, since automated tools alone, as noted earlier, only catch a fraction of the full range of real accessibility issues a page might actually have.
Treat the component's actual markup structure, beyond just its final visual appearance, as a contract, since other teams may have written CSS or JavaScript that depends on specific existing class names or the current element structure, even if that dependency was never actually intended or documented at the time. Changing the underlying structure needs the same care and advance communication as changing any other kind of shared, depended-upon API.
Roll back the change immediately if that's genuinely possible, prioritizing stopping active user impact over fully understanding the root cause first. Then investigate specifically whether it was a genuine cross-browser rendering issue, an accessibility regression that broke assistive technology users' ability to actually interact with the page, or something else entirely, since each of those requires a meaningfully different kind of fix going forward.
Invest early in a genuinely well-documented, semantic, accessible component library that most new work is actually built from by default, rather than each new page or feature reinventing its own markup patterns independently from scratch. Consistency at that shared component layer scales dramatically better across a large, growing team than relying purely on individual developer discipline and memory alone.
I'd walk through one of their actual pages together with a screen reader turned on, letting them directly hear firsthand exactly what a screen reader user actually experiences when navigating a page built entirely from generic, non-semantic markup. That direct, concrete experience tends to genuinely shift habits far more effectively than simply being told semantic HTML matters in the abstract.
I wouldn't lead with accessibility as a purely abstract compliance requirement or legal checkbox. I'd point to a concrete, real user impact, a specific complaint that's already come in, a real accessibility audit finding, or a genuine legal risk relevant to the actual product and its market, and let that already-felt, concrete cost make the case rather than arguing for it purely in the abstract.
I'd bring a concrete, working alternative that achieves most of the actual intended visual effect while remaining genuinely accessible, rather than simply pushing back on the original design with no real alternative offered in its place. Showing a real, working example that gets close to their genuine intent usually resolves the disagreement faster than an abstract technical objection alone ever could.
I'd translate it into terms leadership already tracks: the actual addressable market size that includes users with disabilities, a specific legal complaint or risk already on the table, and cases where accessible design improvements, like better keyboard support or clearer visual structure, measurably improved usability for every user, beyond just those relying on assistive technology. Framed as market reach and risk reduction rather than pure compliance cost, it competes far better for genuine investment.




