<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jim Smith</title>
    <description>The latest articles on DEV Community by Jim Smith (@jim_smith_2acac60d656d462).</description>
    <link>https://dev.to/jim_smith_2acac60d656d462</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4118559%2F1490b8c6-988e-40d5-837e-acb2d15dec9e.png</url>
      <title>DEV Community: Jim Smith</title>
      <link>https://dev.to/jim_smith_2acac60d656d462</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jim_smith_2acac60d656d462"/>
    <language>en</language>
    <item>
      <title>Your HTML Is an API Surface: 7 Patterns That Make Web Apps Easier to Automate</title>
      <dc:creator>Jim Smith</dc:creator>
      <pubDate>Thu, 10 Sep 2026 07:09:47 +0000</pubDate>
      <link>https://dev.to/jim_smith_2acac60d656d462/your-html-is-an-api-surface-7-patterns-that-make-web-apps-easier-to-automate-11p2</link>
      <guid>https://dev.to/jim_smith_2acac60d656d462/your-html-is-an-api-surface-7-patterns-that-make-web-apps-easier-to-automate-11p2</guid>
      <description>&lt;p&gt;Modern frontend development usually treats HTML as the final output of a much larger system.&lt;/p&gt;

&lt;p&gt;We think about:&lt;/p&gt;

&lt;p&gt;React components&lt;br&gt;
state management&lt;br&gt;
APIs&lt;br&gt;
design systems&lt;br&gt;
JavaScript bundles&lt;br&gt;
server rendering&lt;br&gt;
caching&lt;br&gt;
performance&lt;/p&gt;

&lt;p&gt;Then somewhere at the end, all of that becomes HTML.&lt;/p&gt;

&lt;p&gt;That makes it easy to think of markup as implementation detail.&lt;/p&gt;

&lt;p&gt;But consider everything that may need to understand your interface without looking at it the way a human does:&lt;/p&gt;

&lt;p&gt;screen readers&lt;br&gt;
browser automation&lt;br&gt;
end-to-end tests&lt;br&gt;
search crawlers&lt;br&gt;
extensions&lt;br&gt;
monitoring tools&lt;br&gt;
AI-powered browser agents&lt;/p&gt;

&lt;p&gt;For all of them, the DOM is effectively an interface.&lt;/p&gt;

&lt;p&gt;That means your HTML is not merely presentation.&lt;/p&gt;

&lt;p&gt;It is an API surface.&lt;/p&gt;

&lt;p&gt;And like any API, it becomes significantly more reliable when its meaning is explicit.&lt;/p&gt;

&lt;p&gt;Here are seven practical patterns that make web interfaces easier for both humans and software to understand.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use Real Buttons for Actions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of the most common frontend shortcuts looks like this:&lt;/p&gt;


  Save


&lt;p&gt;Visually, there may be nothing wrong with it.&lt;/p&gt;

&lt;p&gt;Add CSS:&lt;/p&gt;

&lt;p&gt;.button {&lt;br&gt;
  padding: 12px 20px;&lt;br&gt;
  background: #2563eb;&lt;br&gt;
  color: white;&lt;br&gt;
  cursor: pointer;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Now it looks exactly like a button.&lt;/p&gt;

&lt;p&gt;But appearance does not define behavior.&lt;/p&gt;

&lt;p&gt;A machine inspecting the DOM sees a generic container with a click handler.&lt;/p&gt;

&lt;p&gt;Compare it with:&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  Save changes&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;The second version communicates several things automatically.&lt;/p&gt;

&lt;p&gt;It is interactive.&lt;/p&gt;

&lt;p&gt;It can receive keyboard focus.&lt;/p&gt;

&lt;p&gt;It has button semantics.&lt;/p&gt;

&lt;p&gt;It exposes a recognizable control to accessibility tools.&lt;/p&gt;

&lt;p&gt;Automation frameworks can identify it more reliably.&lt;/p&gt;

&lt;p&gt;And developers reading the source immediately understand what it does.&lt;/p&gt;

&lt;p&gt;React Example&lt;/p&gt;

&lt;p&gt;Avoid:&lt;/p&gt;

&lt;p&gt;&amp;lt;div&lt;br&gt;
  className="primaryButton"&lt;br&gt;
  onClick={handleCheckout}&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Checkout&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;Prefer:&lt;/p&gt;

&lt;p&gt;&amp;lt;button&lt;br&gt;
  type="button"&lt;br&gt;
  className="primaryButton"&lt;br&gt;
  onClick={handleCheckout}&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Checkout&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;CSS can make both elements look identical.&lt;/p&gt;

&lt;p&gt;Their semantics are not identical.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give Forms Explicit Relationships&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Forms are another place where visual interfaces can hide structural ambiguity.&lt;/p&gt;

&lt;p&gt;This looks reasonable in a browser:&lt;/p&gt;

&lt;p&gt;
  type="email"&lt;br&gt;
  placeholder="Enter your email"&lt;br&gt;
/&amp;gt;&lt;/p&gt;

&lt;p&gt;But placeholder text is doing several jobs at once.&lt;/p&gt;

&lt;p&gt;It is acting as:&lt;/p&gt;

&lt;p&gt;instruction&lt;br&gt;
label&lt;br&gt;
example&lt;br&gt;
contextual hint&lt;/p&gt;

&lt;p&gt;A stronger implementation separates those responsibilities.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  Email address&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  id="email"&lt;br&gt;
  name="email"&lt;br&gt;
  type="email"&lt;br&gt;
  autocomplete="email"&lt;br&gt;
  required&lt;br&gt;
/&amp;gt;&lt;/p&gt;

&lt;p&gt;Now the relationship is explicit.&lt;/p&gt;

&lt;p&gt;The browser knows the field is an email input.&lt;/p&gt;

&lt;p&gt;The label belongs to that input.&lt;/p&gt;

&lt;p&gt;The field is required.&lt;/p&gt;

&lt;p&gt;Autocomplete behavior is defined.&lt;/p&gt;

&lt;p&gt;Software interacting with the form has considerably less guessing to do.&lt;/p&gt;

&lt;p&gt;Add Helpful Error Context&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;p&gt;
  Invalid value
&lt;/p&gt;

&lt;p&gt;connect the error to the field:&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  Email address&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  id="email"&lt;br&gt;
  name="email"&lt;br&gt;
  type="email"&lt;br&gt;
  aria-describedby="email-error"&lt;br&gt;
  aria-invalid="true"&lt;br&gt;
/&amp;gt;&lt;/p&gt;

&lt;p id="email-error"&gt;
  Enter a valid email address.
&lt;/p&gt;

&lt;p&gt;The difference seems small.&lt;/p&gt;

&lt;p&gt;Structurally, it is significant.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stop Encoding State Only in CSS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consider this button:&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  Place order&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;p&gt;.disabled {&lt;br&gt;
  opacity: 0.5;&lt;br&gt;
  pointer-events: none;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;A human sees a faded button and assumes it is unavailable.&lt;/p&gt;

&lt;p&gt;But the state exists only visually.&lt;/p&gt;

&lt;p&gt;A better implementation exposes the state directly:&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  Place order&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Or, when native disabled behavior is not appropriate:&lt;/p&gt;

&lt;p&gt;&amp;lt;button&lt;br&gt;
  aria-disabled="true"&lt;br&gt;
  type="button"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Place order&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;The same principle applies elsewhere.&lt;/p&gt;

&lt;p&gt;Instead of showing selection only with a different background:&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  Monthly&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;make the state explicit:&lt;/p&gt;

&lt;p&gt;&amp;lt;button&lt;br&gt;
  aria-pressed="true"&lt;br&gt;
  type="button"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Monthly&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;For expandable content:&lt;/p&gt;

&lt;p&gt;&amp;lt;button&lt;br&gt;
  aria-expanded="false"&lt;br&gt;
  aria-controls="pricing-details"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Show pricing details&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;&amp;lt;div&lt;br&gt;
  id="pricing-details"&lt;br&gt;
  hidden&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;...&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;Good interfaces expose state programmatically.&lt;/p&gt;

&lt;p&gt;CSS should communicate state visually.&lt;/p&gt;

&lt;p&gt;It should not be the only place where that state exists.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write Links That Explain Their Destination&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consider a dashboard containing several links:&lt;/p&gt;

&lt;p&gt;&lt;a href="/billing"&gt;Learn more&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="/security"&gt;Learn more&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="/integrations"&gt;Learn more&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Humans can probably infer the destinations from surrounding cards.&lt;/p&gt;

&lt;p&gt;Software gets three controls with essentially identical names.&lt;/p&gt;

&lt;p&gt;More descriptive links are better:&lt;/p&gt;

&lt;p&gt;&lt;a href="/billing"&gt;&lt;br&gt;
  Learn more about billing&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="/security"&gt;&lt;br&gt;
  Learn more about security&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="/integrations"&gt;&lt;br&gt;
  Explore integrations&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This also improves maintainability.&lt;/p&gt;

&lt;p&gt;A test can target:&lt;/p&gt;

&lt;p&gt;page.getByRole('link', {&lt;br&gt;
  name: 'Explore integrations'&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;instead of relying on something fragile like:&lt;/p&gt;

&lt;p&gt;page.locator(&lt;br&gt;
  '.card:nth-child(3) .footer a'&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;That leads to an important idea.&lt;/p&gt;

&lt;p&gt;Semantic interfaces can produce better tests.&lt;/p&gt;

&lt;p&gt;When tests locate controls by meaningful roles and names, they resemble the way actual users understand the page.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make Dynamic Content Announce Its State&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Modern applications frequently update content asynchronously.&lt;/p&gt;

&lt;p&gt;A user clicks:&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  Check availability&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Then JavaScript fetches data.&lt;/p&gt;

&lt;p&gt;The interface changes from:&lt;/p&gt;

&lt;p&gt;Checking...&lt;/p&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;p&gt;Available tomorrow&lt;/p&gt;

&lt;p&gt;The visual update may be obvious.&lt;/p&gt;

&lt;p&gt;The structural update may not be.&lt;/p&gt;

&lt;p&gt;One approach is to expose the status:&lt;/p&gt;

&lt;p&gt;&amp;lt;div&lt;br&gt;
  role="status"&lt;br&gt;
  aria-live="polite"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Available tomorrow&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;For loading states:&lt;/p&gt;

&lt;p&gt;&amp;lt;section&lt;br&gt;
  aria-busy="true"&lt;br&gt;
  aria-labelledby="results-heading"&lt;/p&gt;

&lt;blockquote&gt;

&lt;h2 id="results-heading"&gt;
&lt;br&gt;
    Search results&lt;br&gt;
  &lt;/h2&gt;

&lt;/blockquote&gt;

&lt;p&gt;Loading results...&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Then update:&lt;/p&gt;

&lt;p&gt;&amp;lt;section&lt;br&gt;
  aria-busy="false"&lt;br&gt;
  aria-labelledby="results-heading"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;...&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;This communicates something important:&lt;/p&gt;

&lt;p&gt;the state of the interface changed.&lt;/p&gt;

&lt;p&gt;Dynamic applications become easier to automate when state transitions are observable rather than implied.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use Stable Identifiers Where Stability Matters&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Automation frequently breaks because developers use selectors based on styling.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;document.querySelector(&lt;br&gt;
  '.flex.items-center.mt-4 &amp;gt; div:nth-child(2)'&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;That selector describes layout.&lt;/p&gt;

&lt;p&gt;It does not describe meaning.&lt;/p&gt;

&lt;p&gt;A harmless redesign can break it instantly.&lt;/p&gt;

&lt;p&gt;For testing or integration points, sometimes an explicit identifier is appropriate:&lt;/p&gt;

&lt;p&gt;&amp;lt;button&lt;br&gt;
  data-testid="checkout-submit"&lt;br&gt;
  type="submit"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Place order&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;page.getByTestId('checkout-submit');&lt;/p&gt;

&lt;p&gt;But there is an important distinction.&lt;/p&gt;

&lt;p&gt;Don't add data-testid to everything simply because you can.&lt;/p&gt;

&lt;p&gt;Whenever possible, prefer semantic queries:&lt;/p&gt;

&lt;p&gt;page.getByRole('button', {&lt;br&gt;
  name: 'Place order'&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;Use dedicated stable identifiers when:&lt;/p&gt;

&lt;p&gt;several controls have legitimately similar names&lt;br&gt;
third-party automation depends on them&lt;br&gt;
dynamic interfaces make semantic selection ambiguous&lt;br&gt;
a component represents a contractual integration point&lt;/p&gt;

&lt;p&gt;The hierarchy should generally be:&lt;/p&gt;

&lt;p&gt;Meaningful role/name&lt;br&gt;
        ↓&lt;br&gt;
Stable business identifier&lt;br&gt;
        ↓&lt;br&gt;
Implementation-specific selector&lt;/p&gt;

&lt;p&gt;Avoid making CSS class names part of your application's external contract.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Treat Important Data as Data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Imagine a pricing component:&lt;/p&gt;

&lt;p&gt;Only $49!&lt;/p&gt;

&lt;p&gt;A person knows that 49 is probably the price.&lt;/p&gt;

&lt;p&gt;But what does the value actually represent?&lt;/p&gt;

&lt;p&gt;$49 per month?&lt;br&gt;
$49 per year?&lt;br&gt;
$49 setup fee?&lt;br&gt;
starting from $49?&lt;br&gt;
discounted from another price?&lt;/p&gt;

&lt;p&gt;Now consider:&lt;/p&gt;


&lt;h2 id="starter-plan"&gt;
&lt;br&gt;
    Starter Plan&lt;br&gt;
  &lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $49 per month
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The information becomes more explicit.&lt;/p&gt;

&lt;p&gt;For ecommerce, structured data can go further:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "&lt;a class="mentioned-user" href="https://dev.to/context"&gt;@context&lt;/a&gt;": "&lt;a href="https://schema.org" rel="noopener noreferrer"&gt;https://schema.org&lt;/a&gt;",&lt;br&gt;
  "@type": "Product",&lt;br&gt;
  "name": "Developer Keyboard",&lt;br&gt;
  "offers": {&lt;br&gt;
    "@type": "Offer",&lt;br&gt;
    "price": "129.00",&lt;br&gt;
    "priceCurrency": "USD",&lt;br&gt;
    "availability":&lt;br&gt;
      "&lt;a href="https://schema.org/InStock" rel="noopener noreferrer"&gt;https://schema.org/InStock&lt;/a&gt;"&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;This should match the information users actually see.&lt;/p&gt;

&lt;p&gt;Structured data should clarify the interface, not create a second version of reality.&lt;/p&gt;

&lt;p&gt;Why This Matters for Testing&lt;/p&gt;

&lt;p&gt;There is a useful side effect to all of these patterns.&lt;/p&gt;

&lt;p&gt;They make browser tests more resilient.&lt;/p&gt;

&lt;p&gt;Consider a Playwright test.&lt;/p&gt;

&lt;p&gt;Fragile&lt;br&gt;
await page.click(&lt;br&gt;
  '.pricing-card:nth-child(2) .btn-primary'&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;A designer changes the card order.&lt;/p&gt;

&lt;p&gt;The test breaks.&lt;/p&gt;

&lt;p&gt;Better&lt;br&gt;
await page&lt;br&gt;
  .getByRole('button', {&lt;br&gt;
    name: 'Start Professional plan'&lt;br&gt;
  })&lt;br&gt;
  .click();&lt;/p&gt;

&lt;p&gt;The second test expresses intent.&lt;/p&gt;

&lt;p&gt;It describes what the user is trying to do rather than where the element happens to be positioned.&lt;/p&gt;

&lt;p&gt;The difference is similar to API design.&lt;/p&gt;

&lt;p&gt;Bad API:&lt;/p&gt;

&lt;p&gt;GET /thing/3/value/2&lt;/p&gt;

&lt;p&gt;Better API:&lt;/p&gt;

&lt;p&gt;GET /users/42/subscriptions&lt;/p&gt;

&lt;p&gt;Meaningful interfaces produce meaningful integrations.&lt;/p&gt;

&lt;p&gt;Your Accessibility Tree Is Worth Inspecting&lt;/p&gt;

&lt;p&gt;Most developers regularly inspect:&lt;/p&gt;

&lt;p&gt;DOM&lt;br&gt;
network requests&lt;br&gt;
console&lt;br&gt;
performance&lt;br&gt;
storage&lt;/p&gt;

&lt;p&gt;Fewer inspect the accessibility tree.&lt;/p&gt;

&lt;p&gt;Chrome DevTools can expose how browsers interpret elements programmatically.&lt;/p&gt;

&lt;p&gt;A visually obvious checkout button might effectively become:&lt;/p&gt;

&lt;p&gt;button&lt;br&gt;
  name: "Checkout"&lt;/p&gt;

&lt;p&gt;That is useful.&lt;/p&gt;

&lt;p&gt;A clickable &lt;/p&gt; containing an icon might expose far less meaningful information.

&lt;p&gt;The gap between what you see visually and what the browser understands structurally is worth investigating.&lt;/p&gt;

&lt;p&gt;When the accessibility representation is confusing, automated interaction may also become harder.&lt;/p&gt;

&lt;p&gt;React Doesn't Prevent Semantic HTML&lt;/p&gt;

&lt;p&gt;Component frameworks are sometimes blamed for poor markup.&lt;/p&gt;

&lt;p&gt;The framework is rarely the fundamental problem.&lt;/p&gt;

&lt;p&gt;This component:&lt;/p&gt;

&lt;p&gt;function Button({ children, onClick }) {&lt;br&gt;
  return (&lt;br&gt;
    
      className="button"&lt;br&gt;
      onClick={onClick}&lt;br&gt;
    &amp;gt;&lt;br&gt;
      {children}&lt;br&gt;
    &lt;/p&gt;
&lt;br&gt;
  );&lt;br&gt;
}

&lt;p&gt;produces weak semantics because we chose weak semantics.&lt;/p&gt;

&lt;p&gt;This works just as easily:&lt;/p&gt;

&lt;p&gt;function Button({&lt;br&gt;
  children,&lt;br&gt;
  onClick,&lt;br&gt;
  type = 'button'&lt;br&gt;
}) {&lt;br&gt;
  return (&lt;br&gt;
    
      className="button"&lt;br&gt;
      type={type}&lt;br&gt;
      onClick={onClick}&lt;br&gt;
    &amp;gt;&lt;br&gt;
      {children}&lt;br&gt;
    &lt;br&gt;
  );&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;The same applies to Vue, Svelte, Angular and server-rendered templates.&lt;/p&gt;

&lt;p&gt;Framework abstractions do not remove the need to understand the platform underneath them.&lt;/p&gt;

&lt;p&gt;A Quick Audit You Can Run Today&lt;/p&gt;

&lt;p&gt;Open one of your application's important workflows.&lt;/p&gt;

&lt;p&gt;Try signup, checkout, search or account settings.&lt;/p&gt;

&lt;p&gt;Then ask:&lt;/p&gt;

&lt;p&gt;Navigation&lt;/p&gt;

&lt;p&gt;Can I tell which links lead where without relying entirely on surrounding visual context?&lt;/p&gt;

&lt;p&gt;Headings&lt;/p&gt;

&lt;p&gt;Does the heading hierarchy represent the actual information hierarchy?&lt;/p&gt;

&lt;p&gt;Forms&lt;/p&gt;

&lt;p&gt;Does every input have a real label?&lt;/p&gt;

&lt;p&gt;Controls&lt;/p&gt;

&lt;p&gt;Are actions implemented as buttons and navigation as links?&lt;/p&gt;

&lt;p&gt;State&lt;/p&gt;

&lt;p&gt;Are disabled, expanded, selected and loading states exposed programmatically?&lt;/p&gt;

&lt;p&gt;Dynamic Updates&lt;/p&gt;

&lt;p&gt;Can software detect when important content changes?&lt;/p&gt;

&lt;p&gt;Selectors&lt;/p&gt;

&lt;p&gt;Would an automation script survive a CSS redesign?&lt;/p&gt;

&lt;p&gt;Data&lt;/p&gt;

&lt;p&gt;Are prices, dates, availability and identifiers unambiguous?&lt;/p&gt;

&lt;p&gt;If several answers are "no," the application may look polished while exposing a surprisingly weak machine interface.&lt;/p&gt;

&lt;p&gt;Don't Build a Second Website for Machines&lt;/p&gt;

&lt;p&gt;The solution is not necessarily to create:&lt;/p&gt;

&lt;p&gt;website-for-humans.com&lt;/p&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;p&gt;website-for-agents.com&lt;/p&gt;

&lt;p&gt;That creates another synchronization problem.&lt;/p&gt;

&lt;p&gt;Instead, expose meaning through the same interface wherever possible.&lt;/p&gt;

&lt;p&gt;Good markup can serve:&lt;/p&gt;

&lt;p&gt;humans&lt;br&gt;
keyboards&lt;br&gt;
assistive technology&lt;br&gt;
automated tests&lt;br&gt;
crawlers&lt;br&gt;
browser agents&lt;/p&gt;

&lt;p&gt;That is a much cleaner architectural outcome.&lt;/p&gt;

&lt;p&gt;The Best Automation Optimization Is Often Better HTML&lt;/p&gt;

&lt;p&gt;New protocols and AI-specific interfaces will continue to appear.&lt;/p&gt;

&lt;p&gt;Some will become valuable.&lt;/p&gt;

&lt;p&gt;Some will disappear.&lt;/p&gt;

&lt;p&gt;But semantic HTML has one major advantage.&lt;/p&gt;

&lt;p&gt;It already works.&lt;/p&gt;

&lt;p&gt;A  already communicates an action.&lt;/p&gt;


&lt;p&gt;A &lt;/p&gt; already identifies navigation.

&lt;p&gt;A  already describes an input.&lt;/p&gt;

&lt;p&gt;A &lt;/p&gt; already represents tabular relationships.
&lt;p&gt;A &lt;/p&gt; already identifies primary content.

&lt;p&gt;These are small implementation choices, but together they create a much more predictable interface.&lt;/p&gt;

&lt;p&gt;Frontend teams usually think about APIs as something happening between servers.&lt;/p&gt;

&lt;p&gt;That definition is becoming too narrow.&lt;/p&gt;

&lt;p&gt;Any interface consumed programmatically behaves like an API.&lt;/p&gt;

&lt;p&gt;And increasingly, your HTML is one of them.&lt;/p&gt;

&lt;p&gt;Before building another machine-readable layer on top of your application, inspect the one you already ship.&lt;/p&gt;

&lt;p&gt;Sometimes the most effective automation improvement isn't another JavaScript library.&lt;/p&gt;

&lt;p&gt;It's better markup.&lt;/p&gt;

&lt;p&gt;Suggested DEV Description&lt;/p&gt;

&lt;p&gt;Your HTML is more than presentation. These seven semantic patterns can make modern web apps easier to test, automate, access, crawl, and understand.&lt;/p&gt;

&lt;p&gt;Recommended DEV Tags&lt;/p&gt;

&lt;h1&gt;
  
  
  webdev #html #javascript #accessibility
&lt;/h1&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;


&lt;/table&gt;&lt;/div&gt;

</description>
      <category>html</category>
      <category>api</category>
      <category>apps</category>
    </item>
  </channel>
</rss>
