Humans excel at inference. Machines do not.
A grayed-out button signals “unavailable” to humans. 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 humans. An agent sees decoration unless you add the required attribute.
MX principle: If something matters, declare it explicitly in markup.
State Must Be Declared
Disabled Elements
<!-- Visual only -->
<button class="btn-disabled" style="opacity: 0.5">Submit</button>
<!-- Explicit -->
<button disabled aria-disabled="true">Submit</button>
Loading States
<!-- 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>
Current Page/Selection
<!-- Ambiguous -->
<a href="/" class="active">Home</a>
<!-- Explicit -->
<a href="/" aria-current="page">Home</a>
Required vs Optional
Form Fields
<!-- 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">
Optional Fields
<label for="phone">Phone <span class="optional">(optional)</span></label>
<input type="tel" id="phone" aria-required="false">
Navigation Structure
Clear Hierarchy
<!-- Ambiguous -->
<div class="menu">
<a href="/">Home</a>
<a href="/about">About</a>
</div>
<!-- Explicit -->
<nav aria-label="Main navigation">
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
Error States
Validation Feedback
<!-- 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>
Why This Matters for AI Agents
AI agents fall into five categories, each with different capabilities. Server-side agents like ChatGPT and Claude fetch raw HTML without executing JavaScript or rendering CSS. They see the DOM as plain text. Browser automation agents like Perplexity use headless Chrome but still rely on semantic attributes to determine state. Local agents running on device have limited context windows and need every byte to count.
When state is encoded visually — through colour, opacity, position, or animation — it is invisible to every agent that does not render the page. A greyed-out button looks disabled to a human eye. To an AI agent parsing served HTML, it is an active, clickable button unless the disabled attribute is present.
This is not a hypothetical risk. Agents that encounter ambiguous state make one of two choices: they guess (and sometimes guess wrong, triggering unintended actions) or they skip the element entirely (and the user loses functionality). Neither outcome is acceptable for enterprise digital platforms.
The solution is always the same: declare state, intent, and meaning in the markup itself. Use the attributes that HTML and ARIA provide. They exist precisely for this purpose — making meaning machine-readable without relying on visual presentation.
Every pattern on this page follows the same principle: if you removed all CSS from the page, could a machine still understand what each element does? If the answer is yes, your markup is explicit. If the answer is no, you have an implicit dependency that will break for at least one agent type.
The Pattern
Ask: "If I removed all CSS, would the state, intent, and meaning still be clear?"
If no, make it explicit through:
- Semantic HTML elements (
<nav>,<main>,<button>rather than styled<div>elements) - ARIA attributes (
aria-current,aria-invalid,aria-busy,aria-disabled) - Native HTML attributes (
required,disabled,hidden) - Proper form labels with
for/idassociation - Clear text descriptions visible to all consumers
Humans infer from visual design. Machines cannot. Declare explicitly.