Skip to main content

Native HTML First

A practical Field Guide page about starting with built-in HTML elements before reaching for custom components or ARIA.

Close-up of wooden building blocks stacked into a simple structure.
Photo: Close-Up Photo of Wooden Building Blocks via Pexels — https://www.pexels.com/photo/close-up-photo-of-wooden-building-blocks-7269693/
View full screen Download image

Native HTML is not a beginner version of accessibility.

It is the starting point that keeps a lot of accessibility work from becoming harder than it needs to be.

A real button already behaves like a button. A real link already behaves like a link. A real heading gives the page structure. A real label can connect a field to the words people see. A real list tells the browser that a set of items belongs together.

When teams skip those basics, they often have to rebuild them later with JavaScript, ARIA, keyboard handling, focus management, and extra testing.

Sometimes custom components are needed. But the first question should usually be simpler:

Is there already a native HTML element that does this job?

The common project moment

A team is building a screen that looks normal in a design file:

  • a clickable card
  • a button that looks like a link
  • a link that opens a modal
  • a custom dropdown
  • a tabbed section
  • an accordion
  • a table-like layout made from generic containers
  • an icon-only action
  • a form field without a visible label

The screen may look polished. It may work with a mouse. It may even pass a quick visual review.

But underneath, the browser may not know what the pieces are.

That is when accessibility gets expensive. The team is no longer just styling a screen. They are trying to recreate behavior the browser would have provided for free if the right element had been used first.

What native HTML gives you

Native elements give the browser, assistive technology, and users a shared starting point.

They do not solve every accessibility problem by themselves. A native button can still have unclear text. A form can still have confusing instructions. A heading structure can still be messy.

But native elements usually give you a safer baseline:

  • the right role
  • expected keyboard behavior
  • focusability where appropriate
  • familiar browser behavior
  • better support across assistive technologies
  • less custom code to maintain
  • fewer things for QA to rediscover late

That baseline matters because accessibility is not only about what appears on screen. It is also about what the page communicates to the browser.

What to use first

Use a button for actions

If the control does something on the current page, start with a button.

Examples:

  • submit a form
  • open a menu
  • close a dialog
  • expand a section
  • save changes
  • remove an item
  • start a search

A real <button> can receive keyboard focus. It can be activated with expected keys. It communicates that it is a button.

A clickable <div> or <span> does not give you that behavior for free.

If a team uses a non-button element as a button, they may have to add focus behavior, keyboard activation, role, state, disabled behavior, and testing coverage manually. That is usually a sign the wrong element is doing the job.

If the control takes the user somewhere, start with a link.

Examples:

  • open another page
  • move to a different route
  • download a file
  • jump to a section
  • open an external reference

A real <a href="..."> tells the browser and the user that this is navigation.

A button that only changes window.location can work visually, but it blurs the meaning. Users lose familiar link behavior, such as opening in a new tab, copying the link, or seeing the destination in browser status areas.

A useful rule:

Buttons do things. Links go places.

Use real headings for structure

Headings are not just large text.

They create the outline of the page.

People who use screen readers often navigate by headings. People who skim visually also use headings to understand the page quickly. Search, browser tools, and other assistive features can all benefit from a clear heading structure.

A styled <div> that looks like a heading is still just a generic container unless the markup gives it heading meaning.

Use headings to show the real structure:

  • one clear H1 for the page
  • H2s for major sections
  • H3s for subsections under those sections
  • no skipped levels just because a size looks better

Style should follow structure, not replace it.

Use labels for form fields

A form input should have a label that explains what the user is supposed to enter or choose.

Visible labels help everyone. Programmatic labels help assistive technology connect the field to its name.

Placeholder text is not a reliable replacement. It disappears when people type, can be hard to review, and often creates avoidable confusion.

Start with the native pattern:

<label for="email">Email address</label>
<input id="email" name="email" type="email">

The specific implementation may vary by framework, but the relationship should still be there: the field has a clear name, and the user can see it.

Use fieldsets and legends for grouped choices

Some questions are made of several controls.

Radio buttons and checkboxes often need a group label that explains the larger question.

For example:

<fieldset>
  <legend>Preferred contact method</legend>
  <label><input type="radio" name="contact" value="email"> Email</label>
  <label><input type="radio" name="contact" value="phone"> Phone</label>
  <label><input type="radio" name="contact" value="mail"> Mail</label>
</fieldset>

Without the group label, the individual options may be present but unclear. A user may hear Email, Phone, and Mail without knowing what question those options answer.

Use lists for lists

If content is a list, mark it up as a list.

That includes:

  • steps
  • requirements
  • related resources
  • navigation groups
  • filter options
  • repeated warnings
  • sets of supporting links

A list is not only a visual pattern. It tells the user how many items are in the set and how those items relate.

Use tables for data, not layout

Tables are useful when the relationship between rows and columns matters.

They are not a layout tool.

Use a table when the user needs to compare information across columns and rows. Use headings, lists, cards, or sections when the content is really a page layout.

The practical question is:

Does the row-and-column relationship help someone understand the information?

If yes, a table may be right. If no, another structure is probably better.

Use native inputs before custom widgets

Some custom components are necessary. But every custom component should have a reason to exist.

Before building one, ask whether the native control is enough:

  • Can a regular checkbox work?
  • Can a radio group work?
  • Can a select work?
  • Can a button plus a short section work?
  • Can a details/summary pattern work?
  • Can plain links work better than a scripted card?

Custom controls often need extra decisions:

  • What role does it expose?
  • What is its accessible name?
  • What keyboard keys operate it?
  • What states need to be announced?
  • Where does focus go after opening, closing, selecting, or canceling?
  • How does it behave at zoom or on small screens?

If the team cannot answer those questions yet, native HTML is probably the safer first move.

Where ARIA fits

ARIA can be useful.

ARIA can name things, describe things, expose states, and help custom widgets communicate with assistive technology.

But ARIA does not automatically add behavior.

Adding role="button" to a <div> does not make it behave like a real button. It does not automatically add keyboard activation. It does not automatically make disabled behavior work. It does not guarantee focus will be managed correctly.

That is why the safest ARIA rule is still the old one:

If you can use a native HTML element or attribute with the semantics and behavior you need, do that first.

ARIA should support the structure. It should not be used as duct tape over avoidable markup problems.

Questions teams can ask

During intake or requirements

  • Does this request include custom controls, cards, widgets, menus, dialogs, accordions, tabs, or complex forms?
  • Are there places where the design assumes something is clickable without saying what element it should be?
  • Are we asking for behavior that a native HTML element already provides?
  • Do requirements describe the user action clearly enough to choose the right element?
  • Are third-party widgets or design-system components involved?

During design

  • Is this control an action or navigation?
  • Is the heading structure clear, or are text styles doing the work of headings?
  • Are form labels visible and persistent?
  • Are grouped choices shown as one larger question with related options?
  • Are interactive states designed for focus, not only hover?
  • Is a custom component necessary, or would a native pattern be clearer?

During development

  • Is the markup using the native element that matches the job?
  • Are buttons real buttons and links real links?
  • Are labels programmatically connected to fields?
  • Are grouped controls represented as groups, not just visual clusters?
  • If ARIA is used, is it adding missing information rather than contradicting native semantics?
  • If a custom component is used, have keyboard behavior and focus management been built and tested?

During QA

  • Can keyboard users reach and operate every interactive control?
  • Do screen-reader names match visible labels and button/link text?
  • Does the heading list make sense as a page outline?
  • Do form fields announce useful names and instructions?
  • Does the custom component behave like the pattern it visually imitates?
  • If JavaScript fails or loads slowly, does the basic page structure still make sense?

Common trail hazards

  • Clickable <div> or <span> elements used instead of buttons or links.
  • Buttons used for navigation because they were easier to style.
  • Links used as fake buttons because they already looked right.
  • Heading-looking text that is not marked up as a heading.
  • Form inputs without visible or programmatic labels.
  • Radio buttons or checkboxes without a group question.
  • Custom dropdowns that do not support expected keyboard behavior.
  • ARIA roles added without matching keyboard and focus behavior.
  • Tables used only to arrange content visually.
  • Icon-only controls without an accessible name.

Small habit

Before building or reviewing a component, say what it is in plain language.

  • Is it a button?
  • Is it a link?
  • Is it a heading?
  • Is it a form field?
  • Is it a group of choices?
  • Is it a list?
  • Is it data that belongs in rows and columns?

Then check whether the markup says the same thing.

If the visual design says one thing and the HTML says another, accessibility problems are probably waiting downstream.

Use these as trail markers, not as the whole conversation:

  • Headings and Page Structure — for making page sections and relationships clear before adding custom behavior.
  • Accessible Names — for naming controls when native elements still need clear labels.
  • Name, Role, and Value — for checking what custom controls must expose when native HTML is not enough.
  • ARIA Is Not Duct Tape — for deciding whether ARIA is describing real behavior or hiding a component problem.
  • Accordions — for checking expandable sections, button headers, keyboard behavior, and expanded or collapsed state.
  • Button vs Link — for deciding whether a clickable thing should navigate or perform an action.
  • Link Text and Button Labels — for choosing words that match the user's action or destination.
  • Tables and Data Displays — for choosing real table/list/heading structure when relationships in data matter.
  • Accessibility Glossary — for plain-language definitions of common accessibility terms used across this guide.

What to do next