<?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: 8Bit Enterprise</title>
    <description>The latest articles on DEV Community by 8Bit Enterprise (8bitenterprise).</description>
    <link>https://dev.to/8bitenterprise</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%2Forganization%2Fprofile_image%2F16036%2F4e411dd7-8fc8-47a4-88c1-918bd7a09bb1.png</url>
      <title>DEV Community: 8Bit Enterprise</title>
      <link>https://dev.to/8bitenterprise</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/8bitenterprise"/>
    <language>en</language>
    <item>
      <title>Stop Guessing Text Colors. Chrome DevTools Can Pick an Accessible One for You</title>
      <dc:creator>Hannan Shoukat</dc:creator>
      <pubDate>Wed, 11 Nov 2026 21:07:08 +0000</pubDate>
      <link>https://dev.to/8bitenterprise/stop-guessing-text-colors-chrome-devtools-can-pick-an-accessible-one-for-you-38b9</link>
      <guid>https://dev.to/8bitenterprise/stop-guessing-text-colors-chrome-devtools-can-pick-an-accessible-one-for-you-38b9</guid>
      <description>&lt;p&gt;Light grey text on white looks clean in a design file. It's also one of the most common accessibility failures on the web, and it's easy to miss because it looks fine on your own bright screen.&lt;/p&gt;

&lt;p&gt;Here's the thing: you don't need to guess a darker shade. Chrome DevTools will measure the contrast and suggest a color that passes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers you're aiming for
&lt;/h2&gt;

&lt;p&gt;WCAG, the standard most accessibility checks use, measures contrast as a ratio between text color and background. &lt;a href="https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html" rel="noopener noreferrer"&gt;WCAG's contrast guidance&lt;/a&gt; sets these levels:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Normal text&lt;/th&gt;
&lt;th&gt;Large text&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AA (the usual target)&lt;/td&gt;
&lt;td&gt;4.5:1&lt;/td&gt;
&lt;td&gt;3:1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AAA (enhanced)&lt;/td&gt;
&lt;td&gt;7:1&lt;/td&gt;
&lt;td&gt;4.5:1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To make this concrete, I calculated a few common greys on a white background:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Text color on #FFFFFF&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#9e9e9e&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2.68:1&lt;/td&gt;
&lt;td&gt;Fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#767676&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4.54:1&lt;/td&gt;
&lt;td&gt;Passes AA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#595959&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;7.00:1&lt;/td&gt;
&lt;td&gt;Passes AAA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That &lt;code&gt;#9e9e9e&lt;/code&gt; is a very popular "subtle" grey. It fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing it in DevTools
&lt;/h2&gt;

&lt;p&gt;These steps follow &lt;a href="https://developer.chrome.com/docs/devtools/accessibility/contrast" rel="noopener noreferrer"&gt;Chrome's contrast documentation&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Inspect the text.&lt;/strong&gt; Right-click it and choose &lt;strong&gt;Inspect&lt;/strong&gt;. Hovering the element in inspect mode shows its contrast ratio in the tooltip, with a warning sign if it's too low.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open the Color Picker.&lt;/strong&gt; In the &lt;strong&gt;Styles&lt;/strong&gt; pane, click the small color swatch next to the element's &lt;code&gt;color&lt;/code&gt; value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expand "Contrast ratio."&lt;/strong&gt; The picker shows the current ratio and whether it meets AA or AAA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use the lines.&lt;/strong&gt; The shade area shows two lines. Drag the circle below the top line to pass AA, or below the bottom line to pass AAA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Or let DevTools choose.&lt;/strong&gt; Click &lt;strong&gt;Use suggested color&lt;/strong&gt; next to the AAA level, and DevTools applies a text color that passes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then copy the new value into your CSS. DevTools changes are only temporary.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's new in this month's DevTools
&lt;/h2&gt;

&lt;p&gt;Chrome's &lt;a href="https://developer.chrome.com/blog/new-in-devtools-november-2026?hl=en" rel="noopener noreferrer"&gt;November 2026 DevTools update&lt;/a&gt;, covering Chrome 155 and 156, added two things to the color picker:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;APCA suggestions for light text on dark backgrounds&lt;/strong&gt;, which is exactly the case that trips up dark modes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Converting out-of-gamut colors to &lt;code&gt;oklab()&lt;/code&gt; and &lt;code&gt;oklch()&lt;/code&gt;&lt;/strong&gt; without producing &lt;code&gt;NaN&lt;/code&gt; values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;APCA is a newer contrast method that accounts for how we actually perceive light and dark text differently. Older guides say you need to switch it on under &lt;strong&gt;Settings &amp;gt; Experiments&lt;/strong&gt;, while newer ones show it by default. If your picker shows only AA and AAA, check Experiments in your version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dark mode needs its own check
&lt;/h2&gt;

&lt;p&gt;Contrast doesn't carry over automatically when you flip themes. Here's the same idea on a common dark background:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Text color on #121212&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#6b6b6b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3.52:1&lt;/td&gt;
&lt;td&gt;Fails for normal text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#949494&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;6.18:1&lt;/td&gt;
&lt;td&gt;Passes AA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;#b8b8b8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;9.44:1&lt;/td&gt;
&lt;td&gt;Passes AAA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Run the DevTools check in both themes. It takes a minute, and dark modes are where low-contrast text hides most often.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: check contrast in code
&lt;/h2&gt;

&lt;p&gt;If you generate colors or build a design system, you can test contrast automatically. This is the WCAG formula in a few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;luminance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mf"&gt;0.04045&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;12.92&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.055&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1.055&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mf"&gt;2.4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.2126&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.7152&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.0722&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;contrast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;luminance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;luminance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;contrast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#9e9e9e'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'#ffffff'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// "2.68"&lt;/span&gt;
&lt;span class="nf"&gt;contrast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#595959'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'#ffffff'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// "7.00"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop it into a unit test and you'll catch a failing color before it ever ships.&lt;/p&gt;

&lt;p&gt;What's your go-to for checking contrast? I'm curious how many people check dark mode separately, because I suspect it's not many.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>a11y</category>
      <category>design</category>
    </item>
    <item>
      <title>CSS text-overflow Now Takes Custom Text in Chrome 157. Here's What Works (and What Doesn't)</title>
      <dc:creator>Hannan Shoukat</dc:creator>
      <pubDate>Wed, 11 Nov 2026 21:06:53 +0000</pubDate>
      <link>https://dev.to/8bitenterprise/css-text-overflow-now-takes-custom-text-in-chrome-157-heres-what-works-and-what-doesnt-3e1b</link>
      <guid>https://dev.to/8bitenterprise/css-text-overflow-now-takes-custom-text-in-chrome-157-heres-what-works-and-what-doesnt-3e1b</guid>
      <description>&lt;p&gt;Chrome 157 went stable on November 3, and one small CSS change in it is surprisingly handy: &lt;code&gt;text-overflow&lt;/code&gt; now accepts a custom string, not just &lt;code&gt;ellipsis&lt;/code&gt; or &lt;code&gt;clip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That means the "…" at the end of truncated text can be anything you want: &lt;code&gt;" (more)"&lt;/code&gt;, &lt;code&gt;" →"&lt;/code&gt;, &lt;code&gt;" [...]"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's listed in the &lt;a href="https://chromestatus.com/release-notes/157" rel="noopener noreferrer"&gt;Chrome 157 release notes&lt;/a&gt;. Firefox has supported it for years, since Firefox 9. Safari doesn't support it yet, according to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/text-overflow" rel="noopener noreferrer"&gt;MDN's compatibility data&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I tried it out before writing this. Here's the recipe, plus two things that caught me out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basic recipe
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;text-overflow&lt;/code&gt; only works on text that's already overflowing on a single line. So you still need the usual two properties alongside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.truncate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="cm"&gt;/* keep it on one line */&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="cm"&gt;/* hide what doesn't fit */&lt;/span&gt;
  &lt;span class="nl"&gt;text-overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;" (more)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="cm"&gt;/* the new part */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The browser cuts the text where it overflows and adds your string at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a fallback in one line
&lt;/h2&gt;

&lt;p&gt;Browsers that don't understand the string value ignore the whole declaration. So declare &lt;code&gt;ellipsis&lt;/code&gt; first and let the newer value override it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.truncate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ellipsis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="cm"&gt;/* Safari and older Chrome use this */&lt;/span&gt;
  &lt;span class="nl"&gt;text-overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;" →"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="cm"&gt;/* Chrome 157+ and Firefox use this */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nobody gets a broken layout. They just get the classic "…" instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha 1: it cuts words in half
&lt;/h2&gt;

&lt;p&gt;This was the first surprise. The cut happens wherever the text runs out of room, even mid-word. In my test, &lt;code&gt;" (more)"&lt;/code&gt; produced:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Free shipping on every order over $50 unti (more)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The default ellipsis does the same thing, but it's easier to miss with "…". A word like "(more)" or "[...]" right after a broken word looks more like a bug.&lt;/p&gt;

&lt;p&gt;Short, symbol-style markers like &lt;code&gt;" →"&lt;/code&gt; or &lt;code&gt;" …"&lt;/code&gt; hide this best.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha 2: multi-line clamping ignores it
&lt;/h2&gt;

&lt;p&gt;The common way to show two or three lines and then cut off is &lt;code&gt;-webkit-line-clamp&lt;/code&gt;. I paired it with the custom string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.clamp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-webkit-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;-webkit-box-orient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vertical&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;-webkit-line-clamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;" (more)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Firefox, the custom string was ignored and the text ended with a normal "…". MDN's &lt;code&gt;text-overflow&lt;/code&gt; page also says the property only affects text overflowing in the inline direction, not text cut off at the bottom of a box.&lt;/p&gt;

&lt;p&gt;So this feature is for single-line truncation. I couldn't test Chrome 157's behaviour with line-clamp myself, so check it before relying on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it can't do: be clickable
&lt;/h2&gt;

&lt;p&gt;The string is just text drawn by the browser. You can't link it, focus it or attach a click handler.&lt;/p&gt;

&lt;p&gt;So if you want an actual "Read more" that expands the text, you still need a real &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;. Where the custom string helps is as a visual hint, for example in table cells, card titles or file lists where the full text sits in a tooltip or detail view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I'd actually use it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Card and list titles:&lt;/strong&gt; &lt;code&gt;" →"&lt;/code&gt; hints there's more to see.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Table cells:&lt;/strong&gt; &lt;code&gt;" [...]"&lt;/code&gt; reads more clearly than "…" in dense data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-English interfaces:&lt;/strong&gt; you can use a marker that suits the language.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The quick reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Does the custom string work?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Single line with &lt;code&gt;nowrap&lt;/code&gt; and &lt;code&gt;overflow: hidden&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes (Chrome 157+, Firefox)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safari&lt;/td&gt;
&lt;td&gt;No, falls back to your &lt;code&gt;ellipsis&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-line with &lt;code&gt;-webkit-line-clamp&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Firefox ignored it in my test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Making the marker clickable&lt;/td&gt;
&lt;td&gt;No, use a real link or button&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Small feature, but it removes a whole category of "add a custom suffix in JavaScript" code. Has anyone found a good use for the two-value form, where you set a different marker at the start and end of the line?&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>beginners</category>
    </item>
    <item>
      <title>13,352 Websites Already Let AI Agents Use Their Forms. Here's How WebMCP Works (With Code)</title>
      <dc:creator>Hannan Shoukat</dc:creator>
      <pubDate>Wed, 11 Nov 2026 21:06:39 +0000</pubDate>
      <link>https://dev.to/8bitenterprise/13352-websites-already-let-ai-agents-use-their-forms-heres-how-webmcp-works-with-code-5ak</link>
      <guid>https://dev.to/8bitenterprise/13352-websites-already-let-ai-agents-use-their-forms-heres-how-webmcp-works-with-code-5ak</guid>
      <description>&lt;p&gt;AI agents that browse the web on someone's behalf have a hard job. They look at a page, guess which box is the email field, guess which button submits, and hope nothing moved.&lt;/p&gt;

&lt;p&gt;WebMCP is Chrome's answer to that. Instead of guessing, an agent can ask the page: &lt;em&gt;what can you do?&lt;/em&gt; The page answers with a list of tools, each with a name, a description and a schema for its inputs.&lt;/p&gt;

&lt;p&gt;It's still experimental, but it's already in use on more sites than I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  How many sites already use it
&lt;/h2&gt;

&lt;p&gt;Browser Use ran a crawl to find out. According to &lt;a href="https://browser-use.com/webmcp" rel="noopener noreferrer"&gt;their WebMCP directory&lt;/a&gt;, they loaded 322,714 pages in Chrome and found &lt;strong&gt;13,352 sites&lt;/strong&gt; exposing &lt;strong&gt;45,763 distinct tools&lt;/strong&gt;. The scan finished on October 19, 2026.&lt;/p&gt;

&lt;p&gt;A few things stood out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shopping leads&lt;/strong&gt;, with 6,107 sites, ahead of business software and developer tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shopify accounts for about 5,190 of them&lt;/strong&gt;, adding tools like add-to-cart and catalog search across many stores at once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adoption is still small at the top:&lt;/strong&gt; 1,270 of the top 100,000 sites they could load, about 1.7%, had WebMCP tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if you run a store on Shopify, your site may already be talking to agents. For everyone else, it's early, which is the best time to learn it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The status, honestly
&lt;/h2&gt;

&lt;p&gt;WebMCP is an active origin trial. Chrome's &lt;a href="https://developer.chrome.com/docs/modern-web-guidance/guides/webmcp/webmcp" rel="noopener noreferrer"&gt;WebMCP guidance&lt;/a&gt; says local testing needs Chromium &lt;strong&gt;146.0.7672.0 or later&lt;/strong&gt; with the &lt;code&gt;chrome://flags/#enable-webmcp-testing&lt;/code&gt; flag turned on, and that the API is experimental and may change.&lt;/p&gt;

&lt;p&gt;Don't rebuild anything around it yet. But adding it to a form is cheap, and that's where to start.&lt;/p&gt;

&lt;h2&gt;
  
  
  The easy start: two attributes on a form
&lt;/h2&gt;

&lt;p&gt;The declarative API turns an ordinary HTML form into a tool. Add &lt;code&gt;toolname&lt;/code&gt; and &lt;code&gt;tooldescription&lt;/code&gt; to the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;, and optionally describe individual fields with &lt;code&gt;toolparamdescription&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is close to the example in Chrome's &lt;a href="https://developer.chrome.com/docs/ai/webmcp/declarative-api" rel="noopener noreferrer"&gt;declarative API docs&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;toolname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"newsletter_signup"&lt;/span&gt;
      &lt;span class="na"&gt;tooldescription&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Subscribes the user to the weekly newsletter"&lt;/span&gt;
      &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/subscribe"&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;
         &lt;span class="na"&gt;toolparamdescription&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"The user's email address"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign up&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few details from the docs worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Both attributes are required.&lt;/strong&gt; Remove either one and the tool is unregistered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labels count.&lt;/strong&gt; Without &lt;code&gt;toolparamdescription&lt;/code&gt;, the browser describes a field using its &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The user still submits by default.&lt;/strong&gt; The agent can fill the form, but a person clicks the button, unless you add &lt;code&gt;toolautosubmit&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Know when an agent sent the form
&lt;/h2&gt;

&lt;p&gt;The submit event gets an &lt;code&gt;agentInvoked&lt;/code&gt; flag, so you can treat agent submissions differently. You can also send a result straight back to the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'submit'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agentInvoked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nb"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'Subscribed.'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Subscription failed.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whatever the promise resolves to is returned to the model as the tool's output.&lt;/p&gt;

&lt;h2&gt;
  
  
  For app-like pages: register a tool in JavaScript
&lt;/h2&gt;

&lt;p&gt;When an action isn't a simple form, use the imperative API. Chrome's &lt;a href="https://developer.chrome.com/docs/ai/webmcp/imperative-api" rel="noopener noreferrer"&gt;imperative API docs&lt;/a&gt; show it living on &lt;code&gt;document.modelContext&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modelContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'check_order_status'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Returns the shipping status for an order number.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'object'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;orderNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'string'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'orderNumber'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;orderNumber&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`/api/orders/${encodeURIComponent(orderNumber)}`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sb"&gt;`Order ${orderNumber} is ${order.status}.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// later, to remove the tool:&lt;/span&gt;
&lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The order-status endpoint is my example. The &lt;code&gt;registerTool&lt;/code&gt; shape and the &lt;code&gt;AbortSignal&lt;/code&gt; cleanup come from the docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The safety settings that matter
&lt;/h2&gt;

&lt;p&gt;This is the part I'd read twice. Chrome's docs include several guards:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;consequentialHint: true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Marks a high-impact action, so agents and browsers can ask the user to confirm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;untrustedContentHint: true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Flags output that came from untrusted sources, such as user reviews, to help against prompt injection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;allow="tools"&lt;/code&gt; on an iframe&lt;/td&gt;
&lt;td&gt;Required before a cross-origin iframe can register tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exposedTo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tools are hidden from other origins unless you list them here&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Chrome's guidance also says not to auto-submit tools for destructive or irreversible actions, such as deleting data, unless the person has to confirm manually.&lt;/p&gt;

&lt;p&gt;My rule of thumb: newsletter signups and searches are fine to expose. Anything involving money, accounts or deletion should keep a human in the loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to test and debug it
&lt;/h2&gt;

&lt;p&gt;Two Chrome tools help here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DevTools:&lt;/strong&gt; open &lt;strong&gt;Application&lt;/strong&gt;, then &lt;strong&gt;WebMCP&lt;/strong&gt;. It lists the tools on the current tab and logs each time an agent calls one, with inputs and outputs. See &lt;a href="https://developer.chrome.com/docs/devtools/application/webmcp" rel="noopener noreferrer"&gt;Debug WebMCP tools&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lighthouse:&lt;/strong&gt; it now has an audit that flags &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; elements missing &lt;code&gt;toolname&lt;/code&gt; or &lt;code&gt;tooldescription&lt;/code&gt;. Chrome's docs say it's informational for now and won't produce warnings.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Should you add it now?
&lt;/h2&gt;

&lt;p&gt;For a contact form, newsletter or site search, yes. It's a couple of attributes, and it costs nothing if the agent never shows up.&lt;/p&gt;

&lt;p&gt;For checkout, payments or account actions, I'd wait. The API is an origin trial, the details are still changing, and those are exactly the actions where a mistake costs real money.&lt;/p&gt;

&lt;p&gt;Has anyone added WebMCP to a production site yet? I'd like to know whether agents are actually calling your tools, or if they're mostly sitting idle so far.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Traffic Dropped During Google's November 2026 Core Update? Check These 6 Things First</title>
      <dc:creator>Hannan Shoukat</dc:creator>
      <pubDate>Wed, 11 Nov 2026 21:06:25 +0000</pubDate>
      <link>https://dev.to/8bitenterprise/traffic-dropped-during-googles-november-2026-core-update-check-these-6-things-first-373e</link>
      <guid>https://dev.to/8bitenterprise/traffic-dropped-during-googles-november-2026-core-update-check-these-6-things-first-373e</guid>
      <description>&lt;p&gt;Google started rolling out its November 2026 core update on November 10 at 10:06 a.m. Pacific. According to the &lt;a href="https://status.search.google.com/incidents/WLxcJyC6jMWMfryxsKMc" rel="noopener noreferrer"&gt;Google Search Status Dashboard&lt;/a&gt;, it applies globally, to all languages, and may take up to two weeks to finish.&lt;/p&gt;

&lt;p&gt;If your traffic dipped this week, the worst move is to start rewriting pages. Some drops come from the update. Others come from something you broke, and that kind you can fix today.&lt;/p&gt;

&lt;p&gt;Here's how to tell them apart, using Google's own guidance.&lt;/p&gt;

&lt;h2&gt;
  
  
  First: don't judge it mid-rollout
&lt;/h2&gt;

&lt;p&gt;Google's &lt;a href="https://developers.google.com/search/docs/appearance/core-updates" rel="noopener noreferrer"&gt;core updates documentation&lt;/a&gt; is specific about timing. Wait until the dashboard shows the rollout is complete, then wait at least a full week more before you analyze anything.&lt;/p&gt;

&lt;p&gt;When you do compare, Google recommends comparing the week after the update finished with a week before it started. Comparing yesterday with last Tuesday mostly measures noise.&lt;/p&gt;

&lt;p&gt;So for now, use the time to rule out the problems that &lt;em&gt;aren't&lt;/em&gt; the update.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Check whether the drop is site-wide or narrow
&lt;/h2&gt;

&lt;p&gt;Open Search Console, go to &lt;strong&gt;Performance&lt;/strong&gt;, and use the &lt;strong&gt;Compare&lt;/strong&gt; tab. Check whether the drop is limited to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;specific queries or pages&lt;/li&gt;
&lt;li&gt;one country&lt;/li&gt;
&lt;li&gt;one device type&lt;/li&gt;
&lt;li&gt;one search type (Web, Image, Video, News)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the most useful split you can make. A drop on one device or in one country rarely comes from a broad core update. A drop spread across your whole site might.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Look for a technical problem first
&lt;/h2&gt;

&lt;p&gt;Google's &lt;a href="https://developers.google.com/search/docs/monitor-debug/debugging-search-traffic-drops" rel="noopener noreferrer"&gt;guide to debugging traffic drops&lt;/a&gt; points to two reports: &lt;strong&gt;Page indexing&lt;/strong&gt; and &lt;strong&gt;Crawl stats&lt;/strong&gt;. Look for a spike in errors that lines up with your drop.&lt;/p&gt;

&lt;p&gt;Then run &lt;strong&gt;URL Inspection&lt;/strong&gt; on a few pages that lost traffic and use the live test. It shows whether Googlebot can actually fetch the page right now.&lt;/p&gt;

&lt;p&gt;You can also do a quick status-and-robots check from your terminal. This doesn't prove what Googlebot sees, but it catches obvious mistakes like a stray &lt;code&gt;noindex&lt;/code&gt; after a deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r url&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$(&lt;/span&gt;curl -s -o /tmp/page.html -w &lt;span class="s2"&gt;"%{http_code}"&lt;/span&gt; &lt;span class="s2"&gt;"$url"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;header&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$(&lt;/span&gt;curl -sI &lt;span class="s2"&gt;"$url"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; grep -i &lt;span class="s1"&gt;'^x-robots-tag'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; tr -d &lt;span class="s1"&gt;'\r'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$(&lt;/span&gt;grep -io &lt;span class="s1"&gt;'&amp;lt;meta[^&amp;gt;]*name="robots"[^&amp;gt;]*&amp;gt;'&lt;/span&gt; /tmp/page.html &lt;span class="o"&gt;|&lt;/span&gt; head -1&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"$code | $url | ${header:-no x-robots-tag} | ${meta:-no meta robots}"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; urls.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put the URLs that lost the most clicks into &lt;code&gt;urls.txt&lt;/code&gt;, one per line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A trap worth knowing:&lt;/strong&gt; if you test with a bot's user agent, your CDN may answer &lt;code&gt;403&lt;/code&gt;. Many CDNs block requests that &lt;em&gt;claim&lt;/em&gt; to be Googlebot or Bingbot but don't come from their IP ranges. That's normal and proves nothing about the real crawler. Only the search engine's own live test can confirm that.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Check the Security Issues report
&lt;/h2&gt;

&lt;p&gt;If Google has detected malware or phishing on your site, it will show in &lt;strong&gt;Security issues&lt;/strong&gt; in Search Console. It's a one-minute check, and it's a very different problem from a core update.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Check Manual Actions
&lt;/h2&gt;

&lt;p&gt;A manual action means someone at Google decided part of your site breaks the spam policies. It shows under &lt;strong&gt;Manual actions&lt;/strong&gt;. Again, this isn't the core update, and it has its own fix and review process.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Rule out search interest and seasonality
&lt;/h2&gt;

&lt;p&gt;Sometimes nothing is wrong with your site. People are just searching less.&lt;/p&gt;

&lt;p&gt;In the Performance report, filter to the queries that lost the most clicks. Check each one in Google Trends. Then switch to the &lt;strong&gt;16-month&lt;/strong&gt; date range and see if the same dip happened last November.&lt;/p&gt;

&lt;p&gt;If demand fell everywhere, your rankings may be fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Think about what &lt;em&gt;you&lt;/em&gt; changed recently
&lt;/h2&gt;

&lt;p&gt;New theme, URL changes, a migration, a plugin update that touched SEO settings? Google's guide notes that a medium-sized site can take a few weeks for Google to process URL changes.&lt;/p&gt;

&lt;p&gt;Drops that line up with your own deploy date are far more likely to be yours than Google's.&lt;/p&gt;

&lt;h2&gt;
  
  
  If it really is the core update
&lt;/h2&gt;

&lt;p&gt;Google's documentation says a drop after a core update doesn't automatically mean something is wrong with your pages. Core updates are broad and don't target specific sites. Other pages may simply serve searchers better now.&lt;/p&gt;

&lt;p&gt;Its advice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Small drop:&lt;/strong&gt; no need for drastic changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Large drop:&lt;/strong&gt; do a deeper review of how helpful and reliable your content is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid quick fixes.&lt;/strong&gt; Make improvements that help readers long term.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recovery isn't instant either. Google says it can take several months for its systems to recognize improvements, and sometimes the change only shows after the next core update.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Where to look&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Drop on a few pages&lt;/td&gt;
&lt;td&gt;Technical issue on those pages&lt;/td&gt;
&lt;td&gt;URL Inspection live test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spike in indexing errors&lt;/td&gt;
&lt;td&gt;Technical issue&lt;/td&gt;
&lt;td&gt;Page indexing, Crawl stats&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warning in Search Console&lt;/td&gt;
&lt;td&gt;Security or spam&lt;/td&gt;
&lt;td&gt;Security issues, Manual actions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same dip last year&lt;/td&gt;
&lt;td&gt;Seasonality&lt;/td&gt;
&lt;td&gt;Performance, 16-month range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broad drop, nothing else wrong&lt;/td&gt;
&lt;td&gt;Core update&lt;/td&gt;
&lt;td&gt;Wait, then compare full weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Has anyone seen movement yet? I'm curious whether people are seeing site-wide shifts or drops limited to specific page types.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>seo</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
