MX Learn ยท Lesson 3 of 6

Explicit over implicit

The hands-on lesson: real before/after markup for every state a machine needs declared. Reading time about six minutes; the full lesson text is on this page even with scripting off.

Humans excel at inference. Machines do not.

A greyed-out button signals "unavailable" to a human. An AI agent sees a fully clickable button unless you explicitly mark it disabled.

A red asterisk next to a form field means "required" to a human. An agent sees decoration unless you add the required attribute.

The MX principle: if something matters, declare it explicitly in markup. Every pattern in this lesson answers one question: if you removed all CSS from the page, could a machine still understand what each element does?

State must be declared

Visual styling encodes state for eyes only. Open each pattern to compare the visual-only markup with its explicit replacement:

Disabled elements open

Visual only

<button class="btn-disabled" style="opacity: 0.5">Submit</button>

Explicit

<button disabled aria-disabled="true">Submit</button>

The opacity and the class name are invisible to an agent that never renders CSS. The disabled attribute is a declaration every parser reads.

Loading states open

Visual only

<div class="spinner"></div>

Explicit

<div role="status" aria-busy="true" aria-live="polite">
  <span aria-hidden="true" class="spinner"></span>
  <span class="sr-only">Loading, please wait</span>
</div>

An empty div with a class name means nothing. role="status" and aria-busy="true" tell every machine the content is on its way.

Open both patterns to continue.

Required fields and the current page

The same rule holds for forms and navigation. Open both patterns:

Required form fields open

Visual only

<label>Email <span class="red">*</span></label>
<input type="email" name="email">

Explicit

<label for="email">Email <abbr title="required">*</abbr></label>
<input type="email" id="email" required aria-required="true">

The red asterisk is decoration to a machine. The required attribute, backed by aria-required and a proper for/id label association, is a contract.

Current page in navigation open

Ambiguous

<a href="/" class="active">Home</a>

Explicit

<a href="/" aria-current="page">Home</a>

A class named "active" is a private convention your CSS understands. aria-current="page" is a standard every agent and screen reader understands.

Open both patterns to continue.

Error states

Validation feedback is the state that costs users most when a machine misses it. Open the pattern:

Validation feedback open

Visual only

<input type="email" class="error">
<span class="error-text">Invalid email</span>

Explicit

<input type="email" aria-invalid="true" aria-describedby="email-error">
<span id="email-error" role="alert">Please enter a valid email address</span>

Three declarations do the work: aria-invalid="true" marks the field as failed, aria-describedby binds the message to the field it describes, and role="alert" announces the message to every consumer.

Open the pattern to continue.

Why this matters for agents

AI agents fall into five categories, each with different capabilities, and none of them reads your CSS the way a human eye does. When state is encoded visually, through colour, opacity, position, or animation, it is invisible to every agent that does not render the page. View each group:

Server-side agents fetch raw HTML

Agents like ChatGPT and Claude fetch raw HTML without executing JavaScript or rendering CSS. They see the DOM as plain text. A greyed-out button looks disabled to a human eye; to these agents it is an active, clickable button unless the disabled attribute is present.

Browser automation agents still read attributes

Agents like Perplexity use headless Chrome, so they do render the page - but they still rely on semantic attributes to determine state. Rendering pixels does not tell an automation script whether a button may be pressed; disabled and aria-invalid do.

Local on-device agents count every byte

Local agents running on device have limited context windows and need every byte to count. An agent that meets ambiguous state makes one of two choices: it guesses (and sometimes guesses wrong, triggering unintended actions) or it skips the element entirely (and the user loses functionality). Neither outcome is acceptable.

View all three groups to continue.

Quick check

A button is styled with opacity 0.5 and a class of "btn-disabled" but has no disabled attribute. What does a server-side AI agent conclude?

Right: a server-side agent never renders CSS, so the opacity and the class name are invisible to it. With no disabled attribute in the markup, the button reads as active and clickable.
Not quite. Remember: a server-side agent fetches raw HTML and never renders CSS, so styling carries no meaning for it. Try again.

Answer the question to finish the lesson.

What you now know

  • Machines do not infer. Humans read a greyed-out button as unavailable; an agent reads only what the markup declares.
  • State must be declared in markup: disabled, loading, required, current page, and error states all have explicit attributes.
  • Native HTML beats ARIA retrofits: required, disabled, and semantic elements first, ARIA where HTML has no native attribute.
  • If something matters, declare it explicitly. The test: remove all CSS - could a machine still understand what each element does?
Next: Lesson 4: Key principles - the full principle set that explicit-over-implicit belongs to. Prefer the article form? The companion page is Explicit over implicit.