<?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: Vishal Singh</title>
    <description>The latest articles on DEV Community by Vishal Singh (@vishal_singh_0610).</description>
    <link>https://dev.to/vishal_singh_0610</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%2F4022537%2F35f46679-58b2-4d87-9d1c-c62efd848869.png</url>
      <title>DEV Community: Vishal Singh</title>
      <link>https://dev.to/vishal_singh_0610</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vishal_singh_0610"/>
    <language>en</language>
    <item>
      <title>CSS field-sizing Is Baseline 2026: Auto-Growing Inputs and Textareas Without JavaScript</title>
      <dc:creator>Vishal Singh</dc:creator>
      <pubDate>Tue, 01 Sep 2026 12:10:29 +0000</pubDate>
      <link>https://dev.to/vishal_singh_0610/css-field-sizing-is-baseline-2026-auto-growing-inputs-and-textareas-without-javascript-dfb</link>
      <guid>https://dev.to/vishal_singh_0610/css-field-sizing-is-baseline-2026-auto-growing-inputs-and-textareas-without-javascript-dfb</guid>
      <description>&lt;p&gt;Forms often start with a sizing compromise.&lt;/p&gt;

&lt;p&gt;An input is wide enough for the average value but wastes space for short values. A textarea begins at a fixed height and either shows a scrollbar too early or needs JavaScript to measure &lt;code&gt;scrollHeight&lt;/code&gt; after every edit.&lt;/p&gt;

&lt;p&gt;CSS &lt;code&gt;field-sizing&lt;/code&gt; gives form controls a content-based sizing mode. With one declaration, an input can grow with its value and a textarea can expand as the user types.&lt;/p&gt;

&lt;p&gt;MDN marks &lt;code&gt;field-sizing&lt;/code&gt; as Baseline 2026 Newly available since June 2026. That means the latest core browser versions support it, but older browsers and devices still exist. The practical approach is progressive enhancement: keep a usable default, add content sizing where supported, and set defensive minimum and maximum sizes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two sizing modes
&lt;/h2&gt;

&lt;p&gt;The property has two keyword values:&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="nt"&gt;field-sizing&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;fixed&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;field-sizing&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;fixed&lt;/code&gt; is the initial value. The control uses its normal preferred size, along with CSS dimensions and HTML attributes such as &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;rows&lt;/code&gt;, and &lt;code&gt;cols&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;content&lt;/code&gt; tells the control to adjust its preferred size around its current content.&lt;/p&gt;

&lt;p&gt;The smallest useful enhancement is:&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="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;textarea&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;field-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&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;That line changes the sizing model, but it is not enough for production. An empty text field can collapse close to the width of its caret, while a long value can push the layout much farther than intended. Content-based sizing needs boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build an auto-growing textarea
&lt;/h2&gt;

&lt;p&gt;Start with ordinary, accessible HTML:&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;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"reply"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Reply&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt;
  &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"reply"&lt;/span&gt;
  &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"reply"&lt;/span&gt;
  &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Write your reply"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then keep a dependable fallback and enhance 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="nt"&gt;textarea&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;min-block-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;max-block-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;resize&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@supports&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;textarea&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;field-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&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;In an unsupported browser, the textarea remains a normal six-rem-high control that the user can resize vertically.&lt;/p&gt;

&lt;p&gt;In a supporting browser, its block size grows with the entered content until it reaches &lt;code&gt;max-block-size&lt;/code&gt;. After that limit, the control can scroll instead of expanding forever.&lt;/p&gt;

&lt;p&gt;This replaces a common JavaScript pattern:&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="nx"&gt;textarea&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&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;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&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;Removing that measurement loop means less event code and fewer layout reads. It also lets CSS remain responsible for presentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let a short input grow with its value
&lt;/h2&gt;

&lt;p&gt;Content sizing is also useful for compact fields such as a coupon code, quantity, slug, or inline editable label.&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;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"project-slug"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Project slug&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="s"&gt;"project-slug"&lt;/span&gt;
  &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"project-slug"&lt;/span&gt;
  &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
  &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"docs"&lt;/span&gt;
  &lt;span class="na"&gt;maxlength=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@supports&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;#project-slug&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;field-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;min-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;max-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;32ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100%&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 input starts at a useful minimum, grows with the value, and stops before it can break its container. &lt;code&gt;maxlength&lt;/code&gt; also limits the amount of accepted text, although layout limits should still exist independently.&lt;/p&gt;

&lt;p&gt;Use logical properties such as &lt;code&gt;inline-size&lt;/code&gt; and &lt;code&gt;block-size&lt;/code&gt; when the component may appear in different writing modes. They describe the text flow rather than assuming that width is always horizontal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand the textarea growth order
&lt;/h2&gt;

&lt;p&gt;A textarea with &lt;code&gt;field-sizing: content&lt;/code&gt; can grow in both directions.&lt;/p&gt;

&lt;p&gt;If its inline size is unconstrained, it may first grow wider with the content. Once it reaches an inline constraint, text wraps and the textarea begins growing in the block direction. When the block-size limit is reached, scrolling becomes necessary.&lt;/p&gt;

&lt;p&gt;For most form layouts, the intended behaviour is simpler: keep the textarea as wide as its container and let only its height respond to content.&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="k"&gt;@supports&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.message-field&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;field-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;min-block-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="n"&gt;lh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;max-block-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="n"&gt;lh&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 &lt;code&gt;lh&lt;/code&gt; unit follows the element's line height, so the limits describe an approximate number of text lines instead of unrelated pixels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Placeholders participate in sizing
&lt;/h2&gt;

&lt;p&gt;A placeholder is content for intrinsic sizing. A long placeholder can make an otherwise empty control much wider.&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;input&lt;/span&gt;
  &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
  &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"name@company.example"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;field-sizing: content&lt;/code&gt;, that placeholder influences the initial preferred width. When the user starts typing, the field can resize around the actual value.&lt;/p&gt;

&lt;p&gt;Do not use a long placeholder as a substitute for instructions. Keep a visible label, place supporting guidance beside the control, and set &lt;code&gt;min-inline-size&lt;/code&gt; and &lt;code&gt;max-inline-size&lt;/code&gt; so placeholder copy cannot dictate the entire layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML sizing attributes change meaning
&lt;/h2&gt;

&lt;p&gt;The HTML &lt;code&gt;size&lt;/code&gt; attribute normally influences the preferred width of an input. &lt;code&gt;rows&lt;/code&gt; and &lt;code&gt;cols&lt;/code&gt; normally influence a textarea's starting size.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;field-sizing: content&lt;/code&gt; controls the preferred size, those attributes no longer provide the same sizing behaviour. Put the production boundaries in CSS instead:&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;.profile-bio&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;min-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;max-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;min-block-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="n"&gt;lh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;max-block-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="n"&gt;lh&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;Keep semantic HTML attributes that have independent value, such as &lt;code&gt;maxlength&lt;/code&gt;, &lt;code&gt;required&lt;/code&gt;, &lt;code&gt;autocomplete&lt;/code&gt;, and the correct input &lt;code&gt;type&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Select controls can shrink to the chosen option
&lt;/h2&gt;

&lt;p&gt;For a regular &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;, the browser normally reserves enough width for the longest option. With content sizing, the closed control can adjust to the currently selected option.&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="k"&gt;@supports&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nc"&gt;.compact&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;field-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;min-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;max-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&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;This can help compact toolbars, but changing width after every selection can also make surrounding controls move. Use it only where that movement is acceptable, and test long localized option labels.&lt;/p&gt;

&lt;p&gt;Multi-select list boxes behave differently: content sizing can make them large enough to display their options without the usual scrolling. That may be useful for a short fixed list and harmful for a long dynamic one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixed dimensions can defeat the feature
&lt;/h2&gt;

&lt;p&gt;If you set a fixed &lt;code&gt;width&lt;/code&gt; or &lt;code&gt;height&lt;/code&gt;, you are reintroducing a fixed size. Prefer minimums and maximums around content sizing.&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="c"&gt;/* Flexible */&lt;/span&gt;
&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;field-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;min-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;max-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;28ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* This prevents inline growth */&lt;/span&gt;
&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="nc"&gt;.fixed-example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18rem&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 property is not magic layout containment. The parent grid or flex layout, available space, padding, borders, fonts, placeholder, and overflow rules all affect the final result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility checks still matter
&lt;/h2&gt;

&lt;p&gt;Auto-sizing can remove JavaScript, but it does not remove form-design responsibilities.&lt;/p&gt;

&lt;p&gt;Test the component with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keyboard-only navigation&lt;/li&gt;
&lt;li&gt;browser zoom at 200% and 400%&lt;/li&gt;
&lt;li&gt;increased text size and spacing&lt;/li&gt;
&lt;li&gt;long unbroken strings&lt;/li&gt;
&lt;li&gt;autofilled values&lt;/li&gt;
&lt;li&gt;validation messages&lt;/li&gt;
&lt;li&gt;localized labels and placeholders&lt;/li&gt;
&lt;li&gt;right-to-left and vertical writing modes where relevant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep an associated &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;. Do not hide overflow merely to preserve a compact design. Make sure focus indicators remain visible as the control changes size. For textareas, retaining &lt;code&gt;resize: vertical&lt;/code&gt; gives users a manual option when your automatic limits do not match their needs.&lt;/p&gt;

&lt;p&gt;Also watch for layout shift. A field that grows inside a toolbar or card can move buttons and surrounding content. Content-based sizing should improve the interaction, not surprise the user after every character.&lt;/p&gt;

&lt;h2&gt;
  
  
  A safe rollout plan
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Choose one field currently using a JavaScript auto-resize handler.&lt;/li&gt;
&lt;li&gt;Keep the existing usable dimensions as the default.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;field-sizing: content&lt;/code&gt; inside &lt;code&gt;@supports&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Define minimum and maximum inline and block sizes.&lt;/li&gt;
&lt;li&gt;Test empty, placeholder, typical, maximum, and autofilled values.&lt;/li&gt;
&lt;li&gt;Test zoom, localization, keyboard use, and small containers.&lt;/li&gt;
&lt;li&gt;Remove the JavaScript measurement loop only after the CSS path meets your browser-support policy.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;field-sizing&lt;/code&gt; turns a common scripted behaviour into a native sizing option.&lt;/p&gt;

&lt;p&gt;The property is small, but the implementation decision is still a design decision. An input should not collapse to a caret or expand across the page. A textarea should not grow without limit. A select should not shift an entire toolbar unexpectedly.&lt;/p&gt;

&lt;p&gt;Use content sizing as progressive enhancement, surround it with sensible constraints, and test the states that real form controls experience. When those boundaries are in place, one line of CSS can replace a surprising amount of measurement code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/field-sizing" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/field-sizing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/css-ui/css-field-sizing" rel="noopener noreferrer"&gt;https://developer.chrome.com/docs/css-ui/css-field-sizing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/baseline/2026" rel="noopener noreferrer"&gt;https://web.dev/baseline/2026&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>a11y</category>
    </item>
    <item>
      <title>CSS :open Is Baseline 2026: Style Open UI States with One Selector</title>
      <dc:creator>Vishal Singh</dc:creator>
      <pubDate>Tue, 01 Sep 2026 11:55:56 +0000</pubDate>
      <link>https://dev.to/vishal_singh_0610/css-open-is-baseline-2026-style-open-ui-states-with-one-selector-4i09</link>
      <guid>https://dev.to/vishal_singh_0610/css-open-is-baseline-2026-style-open-ui-states-with-one-selector-4i09</guid>
      <description>&lt;p&gt;Web interfaces contain several elements that can be open or closed: disclosure widgets, dialogs, select pickers, date inputs, and color pickers.&lt;/p&gt;

&lt;p&gt;Historically, styling those states required different selectors. A disclosure could use &lt;code&gt;details[open]&lt;/code&gt;, while native picker state often had no equivalent general selector.&lt;/p&gt;

&lt;p&gt;The CSS &lt;code&gt;:open&lt;/code&gt; pseudo-class provides one semantic selector for elements that are currently in an open state. MDN marks it Baseline 2026 Newly available since May 2026 across the latest core browser versions.&lt;/p&gt;

&lt;p&gt;Older browsers still exist, so production use should include fallbacks where the open styling is essential.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with details
&lt;/h2&gt;

&lt;p&gt;The familiar approach is:&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="nt"&gt;details&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;open&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eef2ff&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;With the pseudo-class, the selector expresses the state directly:&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="nt"&gt;details&lt;/span&gt;&lt;span class="nd"&gt;:open&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eef2ff&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;Both can coexist during migration:&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="nt"&gt;details&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;open&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;summary&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;details&lt;/span&gt;&lt;span class="nd"&gt;:open&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eef2ff&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 attribute selector is a dependable fallback for details and dialog because those elements reflect their state through the &lt;code&gt;open&lt;/code&gt; attribute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Style an open select
&lt;/h2&gt;

&lt;p&gt;One of the more interesting uses is a native select while its picker is displayed.&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="nt"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#94a3b8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.625rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.65rem&lt;/span&gt; &lt;span class="m"&gt;0.8rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;:open&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4f46e5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;79&lt;/span&gt; &lt;span class="m"&gt;70&lt;/span&gt; &lt;span class="m"&gt;229&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;20%&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;That state was difficult to target consistently with older CSS alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Style the parent with :has()
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;:open&lt;/code&gt; becomes more useful when combined with &lt;code&gt;:has()&lt;/code&gt;.&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;.field&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;:open&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f8fafc&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 field wrapper can now react when its child select is open without JavaScript adding and removing a class.&lt;/p&gt;

&lt;p&gt;Keep the visual change helpful and subtle. Opening a picker should not cause nearby content to jump.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inputs with picker interfaces
&lt;/h2&gt;

&lt;p&gt;The selector can also match inputs while a browser-provided picker is open, including controls such as date or color inputs where supported.&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="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"date"&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"color"&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;&lt;span class="nd"&gt;:open&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt; &lt;span class="m"&gt;165&lt;/span&gt; &lt;span class="m"&gt;233&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;30%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;outline-offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&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 exact picker presentation varies by platform, so test real operating systems rather than relying only on desktop screenshots.&lt;/p&gt;

&lt;h2&gt;
  
  
  :open is not :popover-open
&lt;/h2&gt;

&lt;p&gt;Popover elements have their own showing state and selector.&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="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;popover&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:popover-open&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&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;Use &lt;code&gt;:open&lt;/code&gt; for elements with semantic open and closed states. Use &lt;code&gt;:popover-open&lt;/code&gt; for a popover that is currently showing.&lt;/p&gt;

&lt;p&gt;These states can coexist in the platform, so choosing the correct selector makes the CSS easier to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open does not necessarily mean visible
&lt;/h2&gt;

&lt;p&gt;Semantic state and visual rendering are different.&lt;/p&gt;

&lt;p&gt;A details element can have its open state even if an ancestor hides it. &lt;code&gt;:open&lt;/code&gt; reports the state; it does not guarantee that a user can currently see or interact with the element.&lt;/p&gt;

&lt;p&gt;Avoid using it as a substitute for visibility or layout checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature detection
&lt;/h2&gt;

&lt;p&gt;Use selector detection when the enhancement has no simple fallback.&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="k"&gt;@supports&lt;/span&gt; &lt;span class="n"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;(:&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4f46e5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For details and dialog, the &lt;code&gt;[open]&lt;/code&gt; attribute remains a practical fallback. For native picker states, make sure the closed appearance is already complete and accessible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Animation considerations
&lt;/h2&gt;

&lt;p&gt;An open-state selector can trigger a transition, but not every element's hidden-to-visible behaviour is automatically animatable.&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="nt"&gt;details&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="m"&gt;180ms&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;details&lt;/span&gt;&lt;span class="nd"&gt;:open&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-reduced-motion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;details&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&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;Test closing as well as opening. Some elements stop rendering content immediately when they close, which can prevent the reverse transition from appearing as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility rules still apply
&lt;/h2&gt;

&lt;p&gt;CSS state selectors do not replace semantics.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; as the interactive label for &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Give dialogs an accessible name.&lt;/li&gt;
&lt;li&gt;Keep focus visible.&lt;/li&gt;
&lt;li&gt;Do not communicate open state through color alone.&lt;/li&gt;
&lt;li&gt;Ensure controls remain operable by keyboard.&lt;/li&gt;
&lt;li&gt;Respect reduced-motion and forced-colors preferences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser supplies a state; your design still has to communicate it clearly.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical migration
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Keep &lt;code&gt;[open]&lt;/code&gt; fallbacks for existing details and dialogs.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;:open&lt;/code&gt; for select and native picker enhancements.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;@supports selector(:open)&lt;/code&gt; for isolated new styling.&lt;/li&gt;
&lt;li&gt;Test keyboard interaction, zoom, and multiple operating systems.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;:popover-open&lt;/code&gt; instead when the component is a popover.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;:open&lt;/code&gt; is a small CSS feature with a useful effect: it gives several native controls a shared vocabulary for their active state.&lt;/p&gt;

&lt;p&gt;It can remove JavaScript classes from simple visual changes, improve styling for native pickers, and make selectors read more like the interface behaviour they represent.&lt;/p&gt;

&lt;p&gt;Use it as an enhancement, keep sensible fallbacks, and remember that state selection is only one part of an accessible component.&lt;/p&gt;

&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/%3Aopen" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/%3Aopen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/details" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/details&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:popover-open" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/:popover-open&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/baseline/2026" rel="noopener noreferrer"&gt;https://web.dev/baseline/2026&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Customizable Select: Style Native Dropdowns Without Rebuilding Accessibility</title>
      <dc:creator>Vishal Singh</dc:creator>
      <pubDate>Tue, 01 Sep 2026 11:55:38 +0000</pubDate>
      <link>https://dev.to/vishal_singh_0610/customizable-select-style-native-dropdowns-without-rebuilding-accessibility-1bp0</link>
      <guid>https://dev.to/vishal_singh_0610/customizable-select-style-native-dropdowns-without-rebuilding-accessibility-1bp0</guid>
      <description>&lt;p&gt;Native &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; elements provide keyboard interaction, form submission, focus behaviour, and platform integration. They are also famously difficult to style.&lt;/p&gt;

&lt;p&gt;That tension has pushed many teams to replace a real select with divs, ARIA roles, and a large JavaScript component. The result may match the design system, but it also inherits responsibility for every keyboard, focus, scrolling, and assistive-technology detail.&lt;/p&gt;

&lt;p&gt;Customizable select features aim to provide a better middle ground: keep the native element and behaviour while opting into new styling hooks.&lt;/p&gt;

&lt;p&gt;Support is still limited. MDN does not classify the complete feature set as Baseline, and framework or server-rendering interactions need testing. Use progressive enhancement rather than replacing every production dropdown immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Opt into the base-select appearance
&lt;/h2&gt;

&lt;p&gt;The central CSS opt-in is &lt;code&gt;appearance: base-select&lt;/code&gt; on both the select and its picker.&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="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nd"&gt;::picker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base-select&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;This gives the control browser-defined base behaviour that can be styled more directly.&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="nt"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;min-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#cbd5e1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0f172a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;::picker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#cbd5e1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt; &lt;span class="m"&gt;23&lt;/span&gt; &lt;span class="m"&gt;42&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;18%&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;h2&gt;
  
  
  Style the open state
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;:open&lt;/code&gt; pseudo-class can target the select while its picker is displayed.&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="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;:open&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4f46e5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;79&lt;/span&gt; &lt;span class="m"&gt;70&lt;/span&gt; &lt;span class="m"&gt;229&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;20%&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 picker icon also has a dedicated pseudo-element:&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="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;::picker-icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#64748b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt; &lt;span class="m"&gt;160ms&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="nd"&gt;:open::picker-icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;180deg&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;Remember to respect reduced-motion preferences if an animation is not essential.&lt;/p&gt;

&lt;h2&gt;
  
  
  Style options without losing their meaning
&lt;/h2&gt;

&lt;p&gt;Options can receive richer visual treatment in supporting browsers.&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="nt"&gt;option&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eef2ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e0e7ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700&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;&lt;code&gt;::checkmark&lt;/code&gt; can style the visual indicator for the selected option, while &lt;code&gt;::picker-icon&lt;/code&gt; targets the closed control's disclosure icon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Selected content can be richer
&lt;/h2&gt;

&lt;p&gt;The evolving customizable-select model permits a button as the first child of the select and introduces &lt;code&gt;&amp;lt;selectedcontent&amp;gt;&lt;/code&gt; to display a clone of the chosen option's content.&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;select&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"plan"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;selectedcontent&amp;gt;&amp;lt;/selectedcontent&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"starter"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🌱&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    Starter
  &lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"growth"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🚀&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    Growth
  &lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep option labels concise and meaningful without their decorative icons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Progressive enhancement is the safest rollout
&lt;/h2&gt;

&lt;p&gt;Start with valid, ordinary select markup. Browsers without the new rendering path should retain a usable classic control.&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="k"&gt;@supports&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base-select&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nd"&gt;::picker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base-select&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;/* Enhanced styling lives here. */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not hide the select and replace it with an unsupported picker. The fallback is a feature, not a failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framework and hydration risks
&lt;/h2&gt;

&lt;p&gt;Modern parsing rules allow richer children inside a select, but some framework versions may still assume the older content model. Server-rendered markup can also differ from the client-side tree and cause hydration warnings.&lt;/p&gt;

&lt;p&gt;Before adopting the richer markup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;test the exact framework and version&lt;/li&gt;
&lt;li&gt;test server rendering and hydration&lt;/li&gt;
&lt;li&gt;inspect the DOM produced in supporting and non-supporting browsers&lt;/li&gt;
&lt;li&gt;keep a classic markup variant if your framework cannot preserve the structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CSS-only enhancement of a normal select is a smaller first step than immediately using every new element and pseudo-element.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility testing remains necessary
&lt;/h2&gt;

&lt;p&gt;Native semantics reduce risk, but visual customization can still create problems.&lt;/p&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;visible focus indication&lt;/li&gt;
&lt;li&gt;keyboard selection and dismissal&lt;/li&gt;
&lt;li&gt;high-contrast and forced-colors modes&lt;/li&gt;
&lt;li&gt;zoom and text resizing&lt;/li&gt;
&lt;li&gt;long labels and localization&lt;/li&gt;
&lt;li&gt;disabled options and optgroups&lt;/li&gt;
&lt;li&gt;screen-reader announcements&lt;/li&gt;
&lt;li&gt;touch target size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not remove outlines without providing an equally strong focus indicator.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to keep a custom component
&lt;/h2&gt;

&lt;p&gt;A select is appropriate when a user chooses from a list of options. It is not automatically a replacement for every combobox, searchable command palette, tree picker, or multi-step menu.&lt;/p&gt;

&lt;p&gt;If your interface needs asynchronous search, arbitrary text input, virtualized results, or complex option actions, a purpose-built component may still be necessary. Use the semantic pattern that matches the interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  A safe migration plan
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Choose one simple single-select field.&lt;/li&gt;
&lt;li&gt;Keep the current native markup.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;base-select&lt;/code&gt; styling inside &lt;code&gt;@supports&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Test all required browsers and assistive technologies.&lt;/li&gt;
&lt;li&gt;Introduce richer option content only after framework and hydration checks pass.&lt;/li&gt;
&lt;li&gt;Preserve the ordinary native control as the fallback.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Customizable select is exciting because it may finally reduce the trade-off between design control and native behaviour.&lt;/p&gt;

&lt;p&gt;The best adoption strategy is incremental. Improve a real select where support exists, keep the classic control everywhere else, and let accessibility and compatibility tests—not a screenshot—decide whether the result is ready.&lt;/p&gt;

&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Customizable_select" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Customizable_select&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/appearance" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/appearance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/select" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/select&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webkit.org/blog/17967/news-from-wwdc26-webkit-in-safari-27-beta/" rel="noopener noreferrer"&gt;https://webkit.org/blog/17967/news-from-wwdc26-webkit-in-safari-27-beta/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>a11y</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CSS text-fit: Responsive Headlines Without JavaScript Font-Sizing Loops</title>
      <dc:creator>Vishal Singh</dc:creator>
      <pubDate>Tue, 01 Sep 2026 11:55:14 +0000</pubDate>
      <link>https://dev.to/vishal_singh_0610/css-text-fit-responsive-headlines-without-javascript-font-sizing-loops-23ib</link>
      <guid>https://dev.to/vishal_singh_0610/css-text-fit-responsive-headlines-without-javascript-font-sizing-loops-23ib</guid>
      <description>&lt;p&gt;Designers often want a headline to fill its container regardless of whether it contains twelve characters or forty.&lt;/p&gt;

&lt;p&gt;Developers usually solve that with breakpoint-specific font sizes, container queries, or JavaScript that measures the text and repeatedly adjusts &lt;code&gt;font-size&lt;/code&gt;. Each option works, but none directly expresses the real intent: make this text fit the available inline space.&lt;/p&gt;

&lt;p&gt;Chrome 150 introduced the experimental CSS &lt;code&gt;text-fit&lt;/code&gt; property to target that problem.&lt;/p&gt;

&lt;p&gt;This is currently a new and limited-support feature. Treat it as an enhancement, verify the exact syntax against current specifications, and keep a readable fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with viewport-only typography
&lt;/h2&gt;

&lt;p&gt;A common responsive rule looks like this:&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;.hero-title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7vw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;6rem&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;&lt;code&gt;clamp()&lt;/code&gt; is excellent when text size should respond to the viewport. But the same viewport can contain cards, sidebars, split layouts, and translated text with very different available widths.&lt;/p&gt;

&lt;p&gt;The container and content length matter too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea behind text-fit
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;text-fit&lt;/code&gt; lets the browser scale text so it fits the width of its containing box. Chrome's examples include syntax such as:&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;.headline&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;text-fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grow&lt;/span&gt; &lt;span class="n"&gt;per-line-all&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 browser performs the fitting as part of layout. You do not need a resize observer and a loop that repeatedly measures text until it reaches a target width.&lt;/p&gt;

&lt;p&gt;Because the feature is experimental, confirm the supported values in your target Chrome release before shipping a demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Always start with a fallback
&lt;/h2&gt;

&lt;p&gt;The unsupported declaration will be ignored, so the base style must remain usable.&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;.headline&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;max-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="n"&gt;cqi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5.5rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@supports&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text-fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.headline&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;text-fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grow&lt;/span&gt; &lt;span class="n"&gt;per-line-all&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;Here, &lt;code&gt;clamp()&lt;/code&gt; and container-query units provide a dependable baseline. Supporting browsers receive the fitting enhancement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it can help
&lt;/h2&gt;

&lt;p&gt;Good candidates include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;short editorial headlines&lt;/li&gt;
&lt;li&gt;dashboard numbers&lt;/li&gt;
&lt;li&gt;poster-style cards&lt;/li&gt;
&lt;li&gt;campaign banners with controlled copy&lt;/li&gt;
&lt;li&gt;data visualizations with bounded labels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is less suitable for long body text. Consistent reading size and line length matter more than filling every pixel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content still controls the design
&lt;/h2&gt;

&lt;p&gt;Automatic fitting does not make every string readable.&lt;/p&gt;

&lt;p&gt;A long translation may be technically fitted but become too small. A short word may become uncomfortably large. User-generated text can contain one unbreakable token. Browser zoom and custom fonts can change the result.&lt;/p&gt;

&lt;p&gt;Use sensible bounds in the component design:&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;.metric&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;min-block-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;anywhere&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;Also consider a content-length limit when the interface owns the copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid layout instability
&lt;/h2&gt;

&lt;p&gt;Web fonts can change glyph widths after they load. That can cause fitted text to recalculate.&lt;/p&gt;

&lt;p&gt;Reduce surprises by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;preloading only genuinely critical fonts&lt;/li&gt;
&lt;li&gt;using accurate font fallback metrics&lt;/li&gt;
&lt;li&gt;reserving enough block space&lt;/li&gt;
&lt;li&gt;testing with a cold cache&lt;/li&gt;
&lt;li&gt;checking layout shifts in real devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not trade a JavaScript sizing loop for a visible font jump.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility checks
&lt;/h2&gt;

&lt;p&gt;Fitted text must still respond to user needs.&lt;/p&gt;

&lt;p&gt;Test at 200% and 400% zoom. Check browser text-size settings where available. Make sure the text is not clipped when the user increases spacing. Confirm that fitting does not reduce important copy below your product's minimum readable size.&lt;/p&gt;

&lt;p&gt;Never put critical information into a fixed-height box that hides overflow merely to preserve a visual composition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compare it with existing tools
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;clamp()&lt;/code&gt; controls a value between minimum and maximum sizes, usually based on the viewport.&lt;/p&gt;

&lt;p&gt;Container-query units such as &lt;code&gt;cqi&lt;/code&gt; respond to component size.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;text-wrap: balance&lt;/code&gt; improves line distribution but does not change font size.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;text-fit&lt;/code&gt; responds to the rendered text and the available box. These tools can complement one another; they do not need to compete.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical experiment plan
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Choose one non-critical marketing headline.&lt;/li&gt;
&lt;li&gt;Keep the current typography as the fallback.&lt;/li&gt;
&lt;li&gt;Add the enhancement inside &lt;code&gt;@supports&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Test the shortest and longest approved copy.&lt;/li&gt;
&lt;li&gt;Test localized strings, browser zoom, font loading, and narrow containers.&lt;/li&gt;
&lt;li&gt;Measure layout shift and compare screenshots across supported browsers.&lt;/li&gt;
&lt;li&gt;Keep it experimental until the syntax and compatibility are stable enough for your policy.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;text-fit&lt;/code&gt; expresses a real design goal that CSS has historically handled only indirectly. Letting the layout engine fit display text could remove a surprising amount of measurement code.&lt;/p&gt;

&lt;p&gt;The feature is new, so the responsible approach is simple: use it on controlled display text, keep a strong fallback, and test readability rather than judging only whether the text fills the box.&lt;/p&gt;

&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/blog/new-in-chrome-150" rel="noopener noreferrer"&gt;https://developer.chrome.com/blog/new-in-chrome-150&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/release-notes/150" rel="noopener noreferrer"&gt;https://developer.chrome.com/release-notes/150&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/blog/new-in-web-ui-io26" rel="noopener noreferrer"&gt;https://developer.chrome.com/blog/new-in-web-ui-io26&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@supports" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/@supports&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>design</category>
    </item>
    <item>
      <title>HTML focusgroup: Arrow-Key Navigation Without Roving Tabindex</title>
      <dc:creator>Vishal Singh</dc:creator>
      <pubDate>Tue, 01 Sep 2026 11:53:59 +0000</pubDate>
      <link>https://dev.to/vishal_singh_0610/html-focusgroup-arrow-key-navigation-without-roving-tabindex-2hhl</link>
      <guid>https://dev.to/vishal_singh_0610/html-focusgroup-arrow-key-navigation-without-roving-tabindex-2hhl</guid>
      <description>&lt;p&gt;Toolbars, tab lists, menus, and other composite widgets usually need two keyboard behaviours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one Tab stop for the entire widget&lt;/li&gt;
&lt;li&gt;arrow keys to move between items inside it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Today, developers normally implement that pattern with JavaScript and a “roving tabindex.” One item has &lt;code&gt;tabindex="0"&lt;/code&gt;, every peer has &lt;code&gt;tabindex="-1"&lt;/code&gt;, and keyboard handlers constantly move those values.&lt;/p&gt;

&lt;p&gt;The proposed HTML &lt;code&gt;focusgroup&lt;/code&gt; attribute moves much of that repeated work into the browser.&lt;/p&gt;

&lt;p&gt;It is important to be precise about availability: &lt;code&gt;focusgroup&lt;/code&gt; is still an evolving web-platform feature. Chrome announced it for developer feedback and later included it in Chrome 150, but you should verify your required browsers and keep a fallback before using it in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  The roving tabindex problem
&lt;/h2&gt;

&lt;p&gt;A simple toolbar often starts like this:&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;div&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"toolbar"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Formatting"&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;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Bold&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Italic&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Underline&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript must then listen for arrow keys, find the next enabled item, update tabindex values, and move focus.&lt;/p&gt;

&lt;p&gt;A robust implementation also handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;right-to-left layouts&lt;/li&gt;
&lt;li&gt;vertical writing modes&lt;/li&gt;
&lt;li&gt;wrapping at the first and last items&lt;/li&gt;
&lt;li&gt;disabled or hidden controls&lt;/li&gt;
&lt;li&gt;dynamically inserted items&lt;/li&gt;
&lt;li&gt;restoring the last-focused item&lt;/li&gt;
&lt;li&gt;nested widgets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is a lot of duplicated code for a common interaction pattern.&lt;/p&gt;

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

&lt;p&gt;With the proposed attribute, the toolbar becomes:&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;div&lt;/span&gt; &lt;span class="na"&gt;focusgroup=&lt;/span&gt;&lt;span class="s"&gt;"toolbar wrap"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Formatting"&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="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Bold&lt;span class="nt"&gt;&amp;lt;/button&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="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Italic&lt;span class="nt"&gt;&amp;lt;/button&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="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Underline&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser can provide arrow-key movement, one Tab stop, direction-aware navigation, and last-focused memory.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;wrap&lt;/code&gt; modifier lets navigation continue from the final item back to the first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigation is not selection
&lt;/h2&gt;

&lt;p&gt;Moving focus is only one part of many widgets.&lt;/p&gt;

&lt;p&gt;In a tab list, arrow keys may move focus, but your application still needs to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;update &lt;code&gt;aria-selected&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;show the selected panel&lt;/li&gt;
&lt;li&gt;hide other panels&lt;/li&gt;
&lt;li&gt;decide whether selection follows focus&lt;/li&gt;
&lt;li&gt;update the URL when deep linking is supported&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;focusgroup&lt;/code&gt; does not replace the business or state logic that makes a component work. It targets the repeated keyboard-navigation layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  A tab-list example
&lt;/h2&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;div&lt;/span&gt; &lt;span class="na"&gt;focusgroup=&lt;/span&gt;&lt;span class="s"&gt;"tablist wrap nomemory"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Account sections"&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;id=&lt;/span&gt;&lt;span class="s"&gt;"profile-tab"&lt;/span&gt;
    &lt;span class="na"&gt;aria-controls=&lt;/span&gt;&lt;span class="s"&gt;"profile-panel"&lt;/span&gt;
    &lt;span class="na"&gt;aria-selected=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
    &lt;span class="na"&gt;focusgroupstart&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Profile
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"security-tab"&lt;/span&gt;
    &lt;span class="na"&gt;aria-controls=&lt;/span&gt;&lt;span class="s"&gt;"security-panel"&lt;/span&gt;
    &lt;span class="na"&gt;aria-selected=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Security
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;focusgroupstart&lt;/code&gt; marks the initial entry point. The &lt;code&gt;nomemory&lt;/code&gt; modifier tells the browser not to restore the previously focused item when the user returns, which can be useful when focus should enter on the currently selected tab.&lt;/p&gt;

&lt;p&gt;Application JavaScript still switches the panels and selected state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logical directions matter
&lt;/h2&gt;

&lt;p&gt;The proposal uses logical directions instead of assuming every interface is horizontal and left-to-right.&lt;/p&gt;

&lt;p&gt;An inline focus group usually responds to left and right arrows in an English layout. In another writing mode or direction, the browser can adapt the navigation to the document.&lt;/p&gt;

&lt;p&gt;That behaviour is hard to reproduce correctly when every design system ships its own keyboard handler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature detection and fallback
&lt;/h2&gt;

&lt;p&gt;Do not remove your proven roving-tabindex implementation until your browser policy supports the new attribute.&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;focusgroup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Use the native focusgroup path.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Initialize the existing roving-tabindex fallback.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your HTML should retain meaningful labels and appropriate semantics in both paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility testing is still required
&lt;/h2&gt;

&lt;p&gt;A browser primitive can reduce implementation mistakes, but it cannot decide whether you chose the correct widget pattern.&lt;/p&gt;

&lt;p&gt;Test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tab and Shift+Tab entry and exit&lt;/li&gt;
&lt;li&gt;every relevant arrow key&lt;/li&gt;
&lt;li&gt;Home and End if your pattern expects them&lt;/li&gt;
&lt;li&gt;disabled and dynamically removed items&lt;/li&gt;
&lt;li&gt;zoom and reflow&lt;/li&gt;
&lt;li&gt;right-to-left content&lt;/li&gt;
&lt;li&gt;screen readers in your supported browser combinations&lt;/li&gt;
&lt;li&gt;touch and pointer interaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also check for controls such as text inputs that use arrow keys internally. A keyboard user must always have a clear way to navigate and leave the component.&lt;/p&gt;

&lt;h2&gt;
  
  
  A safe migration plan
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Find a small existing toolbar with a tested fallback.&lt;/li&gt;
&lt;li&gt;Document its current keyboard behaviour.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;focusgroup&lt;/code&gt; behind feature detection.&lt;/li&gt;
&lt;li&gt;Keep state and selection logic separate from focus movement.&lt;/li&gt;
&lt;li&gt;Run the same automated and manual accessibility tests on both paths.&lt;/li&gt;
&lt;li&gt;Expand only after browser support and real-world behaviour meet your policy.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;focusgroup&lt;/code&gt; is promising because it turns a common accessibility pattern into a platform capability. It can reduce repeated JavaScript while giving the browser more responsibility for writing modes, memory, and focus order.&lt;/p&gt;

&lt;p&gt;It is not a reason to stop thinking about semantics or testing. Use it as progressive enhancement, keep the current path, and let support evidence decide when the fallback can be retired.&lt;/p&gt;

&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/blog/focusgroup-rfc" rel="noopener noreferrer"&gt;https://developer.chrome.com/blog/focusgroup-rfc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/blog/new-in-chrome-150" rel="noopener noreferrer"&gt;https://developer.chrome.com/blog/new-in-chrome-150&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/release-notes/150" rel="noopener noreferrer"&gt;https://developer.chrome.com/release-notes/150&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/" rel="noopener noreferrer"&gt;https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>html</category>
      <category>a11y</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>WebMCP: Make Your Website Agent-Ready Without Screen Scraping</title>
      <dc:creator>Vishal Singh</dc:creator>
      <pubDate>Tue, 01 Sep 2026 11:53:32 +0000</pubDate>
      <link>https://dev.to/vishal_singh_0610/webmcp-make-your-website-agent-ready-without-screen-scraping-254c</link>
      <guid>https://dev.to/vishal_singh_0610/webmcp-make-your-website-agent-ready-without-screen-scraping-254c</guid>
      <description>&lt;p&gt;AI agents can already operate websites by reading text, inspecting accessibility trees, and clicking visible controls. That works, but it asks the agent to reverse-engineer an interface designed for humans.&lt;/p&gt;

&lt;p&gt;WebMCP proposes a different model: a website can expose supported actions as structured tools. Instead of guessing which button completes a booking, an agent can discover a &lt;code&gt;bookSlot&lt;/code&gt; tool with a description, typed inputs, and a defined result.&lt;/p&gt;

&lt;p&gt;WebMCP is currently an emerging Chrome capability and origin-trial technology, not a cross-browser production standard. That makes it useful to study and experiment with, but too early to use as the only path for an important workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why screen-driven automation is fragile
&lt;/h2&gt;

&lt;p&gt;A human can look at a page and understand that “Continue” advances a checkout. An agent has to infer that meaning from surrounding text and interface state.&lt;/p&gt;

&lt;p&gt;That approach can break when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;button labels change&lt;/li&gt;
&lt;li&gt;a responsive layout moves controls&lt;/li&gt;
&lt;li&gt;a modal covers the next step&lt;/li&gt;
&lt;li&gt;the page renders differently for another account&lt;/li&gt;
&lt;li&gt;an A/B test changes the interface&lt;/li&gt;
&lt;li&gt;an action has an important side effect that is not obvious from the label&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A structured tool gives the browser and agent a clearer contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basic WebMCP model
&lt;/h2&gt;

&lt;p&gt;A site registers tools. Each tool has a name, description, input schema, and implementation. A compatible browser can expose those tools to an agent, and the agent supplies structured arguments.&lt;/p&gt;

&lt;p&gt;For a consultation form, the conceptual flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The website registers &lt;code&gt;bookSlot&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The tool describes required fields such as date, time, name, and email.&lt;/li&gt;
&lt;li&gt;The browser presents the tool to an agent within the page's origin and permission context.&lt;/li&gt;
&lt;li&gt;The agent asks for or confirms the required information.&lt;/li&gt;
&lt;li&gt;The website's own code performs the booking.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The agent does not need to guess the DOM path to a submit button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declarative tools for existing forms
&lt;/h2&gt;

&lt;p&gt;The declarative API is designed for standard HTML forms. A form can be annotated with a tool name and description while its controls become tool parameters.&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;action=&lt;/span&gt;&lt;span class="s"&gt;"/consultations"&lt;/span&gt;
  &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;
  &lt;span class="na"&gt;toolname=&lt;/span&gt;&lt;span class="s"&gt;"book_consultation"&lt;/span&gt;
  &lt;span class="na"&gt;tooldescription=&lt;/span&gt;&lt;span class="s"&gt;"Book a 30-minute consultation slot"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;
    Date
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"date"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"date"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;
    Email
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/label&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="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Book&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;The important idea is progressive enhancement. The form should remain a real, usable form for people and browsers that do not support WebMCP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Imperative tools for application actions
&lt;/h2&gt;

&lt;p&gt;Some actions do not map neatly to one form. The imperative API lets application code register a tool with a JSON schema and an execute function.&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkOrderStatus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Return the latest status for an order owned by the signed-in user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;orderId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&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="nx"&gt;orderId&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;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOrderStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderId&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 tool should call the same trusted application layer used by the normal interface. WebMCP is an interaction surface, not a replacement for authorization or business rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security responsibilities do not disappear
&lt;/h2&gt;

&lt;p&gt;A structured action can be more reliable than screen scraping, but it can also make a sensitive capability easier to invoke. Treat every tool call as untrusted input.&lt;/p&gt;

&lt;p&gt;Good rules include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authenticate and authorize on the server&lt;/li&gt;
&lt;li&gt;validate every input again on the server&lt;/li&gt;
&lt;li&gt;make read-only and state-changing actions easy to distinguish&lt;/li&gt;
&lt;li&gt;require confirmation for purchases, deletion, publication, or account changes&lt;/li&gt;
&lt;li&gt;return the minimum necessary data&lt;/li&gt;
&lt;li&gt;log important tool calls for audit and debugging&lt;/li&gt;
&lt;li&gt;use idempotency protection for actions that must not run twice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Never assume that a tool is safe because it was called through a browser agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with narrow, high-confidence actions
&lt;/h2&gt;

&lt;p&gt;The best first tool is usually not “control my entire application.” Choose one action with clear inputs, predictable output, and an existing server-side permission check.&lt;/p&gt;

&lt;p&gt;Useful candidates include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;search public documentation&lt;/li&gt;
&lt;li&gt;check an order status&lt;/li&gt;
&lt;li&gt;calculate a quote without purchasing&lt;/li&gt;
&lt;li&gt;find available appointment times&lt;/li&gt;
&lt;li&gt;save a draft without publishing it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These actions are easier to test than a broad tool that hides many decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to evaluate a WebMCP tool
&lt;/h2&gt;

&lt;p&gt;Test more than the happy path.&lt;/p&gt;

&lt;p&gt;Ask whether the agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;chooses the tool for the right user request&lt;/li&gt;
&lt;li&gt;avoids it for unrelated requests&lt;/li&gt;
&lt;li&gt;asks for missing required information&lt;/li&gt;
&lt;li&gt;rejects invalid values cleanly&lt;/li&gt;
&lt;li&gt;explains a failure without inventing a result&lt;/li&gt;
&lt;li&gt;requests confirmation before a consequential action&lt;/li&gt;
&lt;li&gt;behaves correctly when the user is signed out or unauthorized&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chrome's developer tooling and Lighthouse work around WebMCP are useful for inspecting registered tools, but application-level tests are still necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical adoption plan
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Keep the existing human interface fully functional.&lt;/li&gt;
&lt;li&gt;Select one narrow action.&lt;/li&gt;
&lt;li&gt;Define a clear name, description, and input schema.&lt;/li&gt;
&lt;li&gt;Reuse existing server authorization and validation.&lt;/li&gt;
&lt;li&gt;Add confirmation for meaningful side effects.&lt;/li&gt;
&lt;li&gt;Test successful, invalid, unauthorized, cancelled, and repeated calls.&lt;/li&gt;
&lt;li&gt;Treat the implementation as experimental while the API and browser support evolve.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;WebMCP points toward a web where agents do not have to guess how every interface works. Sites can expose supported capabilities as explicit tools while preserving their normal human-facing experience.&lt;/p&gt;

&lt;p&gt;The opportunity is not to give agents unlimited control. It is to make a small set of user-approved actions clearer, safer, and more reliable.&lt;/p&gt;

&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/ai/agents" rel="noopener noreferrer"&gt;https://developer.chrome.com/docs/ai/agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/ai/webmcp/declarative-api" rel="noopener noreferrer"&gt;https://developer.chrome.com/docs/ai/webmcp/declarative-api&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/devtools/application/webmcp" rel="noopener noreferrer"&gt;https://developer.chrome.com/docs/devtools/application/webmcp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/lighthouse/agentic-browsing/registered-webmcp-tools" rel="noopener noreferrer"&gt;https://developer.chrome.com/docs/lighthouse/agentic-browsing/registered-webmcp-tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>webmcp</category>
      <category>a11y</category>
    </item>
    <item>
      <title>CSS Anchor Positioning in 2026: Build Tooltips and Popovers Without Coordinate Math</title>
      <dc:creator>Vishal Singh</dc:creator>
      <pubDate>Tue, 01 Sep 2026 11:51:43 +0000</pubDate>
      <link>https://dev.to/vishal_singh_0610/css-anchor-positioning-in-2026-build-tooltips-and-popovers-without-coordinate-math-gg4</link>
      <guid>https://dev.to/vishal_singh_0610/css-anchor-positioning-in-2026-build-tooltips-and-popovers-without-coordinate-math-gg4</guid>
      <description>&lt;p&gt;A tooltip belongs next to a button. A menu belongs next to its trigger. A validation message belongs next to its input.&lt;/p&gt;

&lt;p&gt;The design sounds simple, but the implementation often becomes a small geometry engine: read getBoundingClientRect(), calculate an offset, watch scrolling, handle resizing, detect viewport collisions, and update everything again when the layout changes.&lt;/p&gt;

&lt;p&gt;CSS Anchor Positioning gives the browser a declarative way to tether one element to another.&lt;/p&gt;

&lt;p&gt;In January 2026, the core anchor() function became Baseline Newly available across the latest major browser versions. The wider module is still evolving, and MDN marks some individual properties as limited availability, so production code still needs feature checks and a fallback.&lt;/p&gt;

&lt;p&gt;This guide builds a practical tooltip, explains overflow handling, and shows where JavaScript and accessibility are still required.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you will learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;how to tether a floating element to a trigger with CSS&lt;/li&gt;
&lt;li&gt;how to keep a dependable fallback for older browsers&lt;/li&gt;
&lt;li&gt;how anchor positioning works with tooltips and the Popover API&lt;/li&gt;
&lt;li&gt;which accessibility and interaction responsibilities still need attention&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The basic mental model
&lt;/h2&gt;

&lt;p&gt;Anchor positioning has three parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Name the anchor element.&lt;/li&gt;
&lt;li&gt;Associate the positioned element with that anchor.&lt;/li&gt;
&lt;li&gt;Place the positioned element relative to an anchor edge.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is a small tooltip structure:&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;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"help"&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;class=&lt;/span&gt;&lt;span class="s"&gt;"help__trigger"&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;
    &lt;span class="na"&gt;aria-describedby=&lt;/span&gt;&lt;span class="s"&gt;"billing-help"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    What is a billing cycle?
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"help__tooltip"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"billing-help"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"tooltip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    The period covered by one invoice.
  &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The wrapper is useful for the fallback. The CSS enhancement names the button as an anchor:&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;.help__trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--billing-trigger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.help__tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;position-anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--billing-trigger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-50%&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;max-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.65rem&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#111827&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0.75rem&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;20%&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 browser now calculates the button's bottom edge and horizontal centre. The tooltip stays tethered even when responsive layout changes move the trigger.&lt;/p&gt;

&lt;p&gt;No getBoundingClientRect() loop is needed for the position.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a dependable fallback first
&lt;/h2&gt;

&lt;p&gt;New CSS should not turn an unsupported browser into a broken interface.&lt;/p&gt;

&lt;p&gt;Start with a classic containing-block fallback:&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;.help&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&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;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.help__tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inset-block-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;inset-inline-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-50%&lt;/span&gt; &lt;span class="m"&gt;0&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;Then place the anchor-specific rules inside @supports:&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="k"&gt;@supports&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anchor-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--billing-trigger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.help__trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;anchor-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--billing-trigger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.help__tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;position-anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--billing-trigger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;inset-block-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;inset-inline-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&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;Browsers that do not support anchor positioning keep the wrapper-based layout. Supporting browsers use the anchor.&lt;/p&gt;

&lt;p&gt;Feature detection is better than checking a user-agent string because it asks the browser about the capability you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the interaction accessible
&lt;/h2&gt;

&lt;p&gt;CSS can calculate position, but position is only one part of a tooltip.&lt;/p&gt;

&lt;p&gt;A keyboard user must be able to reveal the content. A touch user cannot depend on hover. The trigger and tooltip need a meaningful relationship.&lt;/p&gt;

&lt;p&gt;A simple visual state can start with both hover and focus:&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;.help__tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.help&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.help__trigger&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;.help__tooltip&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.help&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.help__trigger&lt;/span&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;.help__tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;visible&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;For short, non-interactive supplementary text, role="tooltip" and aria-describedby can be appropriate.&lt;/p&gt;

&lt;p&gt;If the floating content contains buttons, links, form controls, or substantial information, it is not a tooltip. Use a dialog, menu, or popover pattern with correct focus management and keyboard behaviour.&lt;/p&gt;

&lt;p&gt;CSS Anchor Positioning does not replace semantic HTML or interaction logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Place a popover next to its trigger
&lt;/h2&gt;

&lt;p&gt;Anchor positioning works well with the Popover API because the two features solve different problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the Popover API controls top-layer display and light-dismiss behaviour&lt;/li&gt;
&lt;li&gt;anchor positioning controls geometry
&lt;/li&gt;
&lt;/ul&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;button&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"account-button"&lt;/span&gt;
  &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"account-menu"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Account
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"account-menu"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"account-menu"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/profile"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Profile&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/settings"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Settings&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.account-button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--account-button&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.account-menu&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;position-anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--account-button&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;min-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12rem&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;Using fixed positioning can be useful for top-layer content, but test the exact combination in the browsers you support. Popover behaviour, anchor association, scrolling, transforms, and containing blocks can interact in ways that are easy to miss in a small demo.&lt;/p&gt;

&lt;p&gt;Also choose the correct accessible pattern. A list of normal navigation links does not automatically need ARIA menu roles. Native link and button semantics are often simpler and more robust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle viewport overflow
&lt;/h2&gt;

&lt;p&gt;A tooltip below a trigger can be clipped when the trigger is near the bottom of the viewport.&lt;/p&gt;

&lt;p&gt;Anchor positioning includes position-try fallbacks that let the browser test alternate placements.&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;.help__tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;position-try-fallbacks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;flip-block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;flip-inline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;flip-block&lt;/span&gt; &lt;span class="n"&gt;flip-inline&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 browser can try flipping the block direction, the inline direction, or both when the preferred placement does not fit.&lt;/p&gt;

&lt;p&gt;You can also define a custom fallback:&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="k"&gt;@position-try&lt;/span&gt; &lt;span class="n"&gt;--above-trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;top&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.help__tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;position-try-fallbacks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--above-trigger&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;Support for the complete position-try feature set may differ from support for the core anchor() function. Check each property you depend on, not just one general compatibility badge.&lt;/p&gt;

&lt;p&gt;A practical rollout can therefore have two levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;anchor positioning for the main placement&lt;/li&gt;
&lt;li&gt;your existing overflow logic retained until the required position-try features meet your support target&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Progressive enhancement does not need to be all or nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repeated components need anchor scoping
&lt;/h2&gt;

&lt;p&gt;Imagine a product list with 20 help buttons. If every button uses the same anchor name, an unscoped positioned element can associate with the last matching anchor in source order.&lt;/p&gt;

&lt;p&gt;The anchor-scope property is designed to limit an anchor name to a subtree.&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;.product-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--details-trigger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.product-card__button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--details-trigger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.product-card__panel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;position-anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--details-trigger&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;This lets repeated components reuse a meaningful anchor name without all panels attaching to one button.&lt;/p&gt;

&lt;p&gt;Because anchor-scope is newer than the basic technique, verify its current compatibility. Until it fits your browser policy, unique generated anchor names are a reasonable fallback for component libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  When anchor() is better than position-area
&lt;/h2&gt;

&lt;p&gt;The anchor() function works inside inset properties such as top, right, bottom, and left. It is explicit and combines naturally with calc().&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;.notification&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;-50%&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 position-area property provides a higher-level grid-like placement vocabulary.&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;.notification&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;position-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt; &lt;span class="nb"&gt;right&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;position-area can be concise, while anchor() gives precise control. They are complementary. Choose the smallest feature set that expresses your layout and is supported by your target browsers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this replaces—and what it does not
&lt;/h2&gt;

&lt;p&gt;Anchor positioning can replace a lot of coordinate calculation for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tooltips&lt;/li&gt;
&lt;li&gt;dropdown panels&lt;/li&gt;
&lt;li&gt;contextual help&lt;/li&gt;
&lt;li&gt;callouts&lt;/li&gt;
&lt;li&gt;labels attached to controls&lt;/li&gt;
&lt;li&gt;floating action panels&lt;/li&gt;
&lt;li&gt;autocomplete surfaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does not automatically provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;show and hide state&lt;/li&gt;
&lt;li&gt;focus management&lt;/li&gt;
&lt;li&gt;Escape-key handling&lt;/li&gt;
&lt;li&gt;click-outside behaviour&lt;/li&gt;
&lt;li&gt;accessible roles and labels&lt;/li&gt;
&lt;li&gt;collision support in every target browser&lt;/li&gt;
&lt;li&gt;application-specific placement policy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Libraries such as Floating UI still provide value when you need mature cross-browser collision handling, virtual anchors, framework integration, or one abstraction across older browsers.&lt;/p&gt;

&lt;p&gt;The new CSS lets you reduce that dependency when your use case and browser matrix allow it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A safe migration plan
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Measure the current complexity
&lt;/h3&gt;

&lt;p&gt;Find components that read layout coordinates, subscribe to scroll and resize events, or repeatedly update inline top and left styles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Choose one low-risk component
&lt;/h3&gt;

&lt;p&gt;A non-interactive tooltip or simple contextual label is easier to migrate than a complex editor menu.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Keep the current fallback
&lt;/h3&gt;

&lt;p&gt;Write the old layout as the default and add the anchor enhancement inside @supports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Test real layout conditions
&lt;/h3&gt;

&lt;p&gt;Test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;viewport edges&lt;/li&gt;
&lt;li&gt;zoom at 200%&lt;/li&gt;
&lt;li&gt;long translated text&lt;/li&gt;
&lt;li&gt;right-to-left layout&lt;/li&gt;
&lt;li&gt;nested scrolling containers&lt;/li&gt;
&lt;li&gt;transforms on ancestors&lt;/li&gt;
&lt;li&gt;mobile touch interaction&lt;/li&gt;
&lt;li&gt;keyboard navigation&lt;/li&gt;
&lt;li&gt;the browser versions in your support policy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5: Measure before deleting JavaScript
&lt;/h3&gt;

&lt;p&gt;If the CSS path works across your required conditions, remove only the geometry code it actually replaces. Keep state and accessibility logic that is still necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Making the tooltip visible only on hover
&lt;/h3&gt;

&lt;p&gt;Hover does not cover keyboard or touch interaction. Include focus behaviour and choose a suitable disclosure pattern for mobile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using a tooltip for interactive content
&lt;/h3&gt;

&lt;p&gt;A tooltip should not contain buttons or form fields. Use a popover, dialog, or other interactive pattern.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assuming one support badge covers the whole module
&lt;/h3&gt;

&lt;p&gt;Core anchor() support and newer properties such as anchor-scope or some position-try features can have different compatibility states.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forgetting the fallback
&lt;/h3&gt;

&lt;p&gt;Unsupported CSS is ignored. If the anchor rules are the only placement rules, the element may appear in the wrong location.&lt;/p&gt;

&lt;h3&gt;
  
  
  Replacing tested code too early
&lt;/h3&gt;

&lt;p&gt;The goal is not to delete a library because a demo works. The goal is to simplify a real component without losing behaviour.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: let CSS own the geometry
&lt;/h2&gt;

&lt;p&gt;Floating interfaces have historically asked JavaScript to measure the layout and then tell CSS where to draw.&lt;/p&gt;

&lt;p&gt;Anchor positioning gives more of that work back to the browser's layout engine.&lt;/p&gt;

&lt;p&gt;That can mean less code, fewer scroll and resize listeners, and a more direct relationship between the trigger and the floating element. The win is strongest when it is combined with semantic HTML, accessible state handling, honest compatibility checks, and a tested fallback.&lt;/p&gt;

&lt;p&gt;Start with one tooltip. Keep the old path. Let the browser handle the geometry where it can.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Anchor_positioning" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Anchor_positioning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/anchor" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/learn/css/anchor-positioning" rel="noopener noreferrer"&gt;https://web.dev/learn/css/anchor-positioning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/blog/web-platform-01-2026" rel="noopener noreferrer"&gt;https://web.dev/blog/web-platform-01-2026&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>a11y</category>
    </item>
    <item>
      <title>JavaScript Temporal Reached Stage 4: A Practical Migration Guide Beyond Date</title>
      <dc:creator>Vishal Singh</dc:creator>
      <pubDate>Mon, 10 Aug 2026 06:45:55 +0000</pubDate>
      <link>https://dev.to/vishal_singh_0610/javascript-temporal-reached-stage-4-a-practical-migration-guide-beyond-date-4bjp</link>
      <guid>https://dev.to/vishal_singh_0610/javascript-temporal-reached-stage-4-a-practical-migration-guide-beyond-date-4bjp</guid>
      <description>&lt;p&gt;JavaScript's Date object has carried the web for decades, but almost every experienced developer has a story about it: an off-by-one-day bug, a daylight-saving surprise, a month numbered from zero, or a date-only value that silently became a timestamp.&lt;/p&gt;

&lt;p&gt;In July 2026, the TC39 Temporal proposal reached a Stage 4 draft. That is a major standardisation milestone: the API has completed the proposal process and is ready to become part of the ECMAScript standard.&lt;/p&gt;

&lt;p&gt;But Stage 4 does not mean you can assume every current browser already has Temporal. MDN still marks the API as limited availability. The practical approach is to learn the model now, use feature detection, and add the official polyfill when your support policy requires it.&lt;/p&gt;

&lt;p&gt;This guide explains the mental model, the most useful types, and a safe migration path from Date.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you will learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;how Temporal models dates, times, instants, zones, and durations&lt;/li&gt;
&lt;li&gt;which Temporal type fits each common use case&lt;/li&gt;
&lt;li&gt;how to migrate from Date without a risky rewrite&lt;/li&gt;
&lt;li&gt;how to handle current browser support safely&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Date causes so many bugs
&lt;/h2&gt;

&lt;p&gt;Date tries to represent several different concepts with one mutable object:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an exact instant on the global timeline&lt;/li&gt;
&lt;li&gt;a calendar date such as a birthday&lt;/li&gt;
&lt;li&gt;a local wall-clock time&lt;/li&gt;
&lt;li&gt;a date and time in a named time zone&lt;/li&gt;
&lt;li&gt;a duration between two values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those concepts are not interchangeable.&lt;/p&gt;

&lt;p&gt;Consider the string 2026-08-10. It might mean a birthday, a billing date, or midnight in some time zone. Turning it into a Date can introduce a time zone that the original value never had.&lt;/p&gt;

&lt;p&gt;Date also has historical API traps. Months are zero-indexed in the numeric constructor, parsing behaviour has edge cases, and methods can mutate an existing value.&lt;/p&gt;

&lt;p&gt;Temporal solves the modelling problem by using separate immutable types.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Temporal types you should know
&lt;/h2&gt;

&lt;p&gt;You do not need to memorise the whole API. Start by choosing the type that matches the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Temporal.PlainDate
&lt;/h3&gt;

&lt;p&gt;Use PlainDate for a calendar date with no time and no time zone.&lt;/p&gt;

&lt;p&gt;Good examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;birthdays&lt;/li&gt;
&lt;li&gt;public holidays&lt;/li&gt;
&lt;li&gt;invoice due dates&lt;/li&gt;
&lt;li&gt;check-in dates
&lt;/li&gt;
&lt;/ul&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="nx"&gt;launchDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PlainDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-08-10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reviewDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;launchDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;months&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;launchDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 2026-08-10&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reviewDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 2026-09-10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Temporal values are immutable. add() returns a new value; it does not change launchDate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Temporal.PlainTime
&lt;/h3&gt;

&lt;p&gt;Use PlainTime for a wall-clock time without a date or time zone.&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="nx"&gt;openingTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PlainTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;09:30&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;openingTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works for a recurring local concept such as "the store opens at 09:30." It does not identify one exact moment globally.&lt;/p&gt;

&lt;h3&gt;
  
  
  Temporal.Instant
&lt;/h3&gt;

&lt;p&gt;Use Instant for an exact point on the timeline.&lt;/p&gt;

&lt;p&gt;This is the closest Temporal equivalent to a Unix timestamp or a Date used as an event timestamp.&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="nx"&gt;deployedAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-08-10T06:30:00Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deployedAt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;epochMilliseconds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instants are useful for logs, database timestamps, audit events, and API data that ends in Z or includes an offset.&lt;/p&gt;

&lt;h3&gt;
  
  
  Temporal.ZonedDateTime
&lt;/h3&gt;

&lt;p&gt;Use ZonedDateTime when the named time zone matters.&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="nx"&gt;meeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ZonedDateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-08-10T10:00:00+05:30[Asia/Kolkata]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;meeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeZoneId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Asia/Kolkata&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;meeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The named zone is important because an offset such as +05:30 and a time zone such as Asia/Kolkata are different kinds of information. Time-zone rules can change, and many regions use daylight saving time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Temporal.Duration
&lt;/h3&gt;

&lt;p&gt;Use Duration for an amount of time.&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="nx"&gt;sprint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;weeks&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// P2W&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be careful when converting calendar units into clock units. A day is not always exactly 24 hours in a zone with daylight-saving changes. Temporal makes you provide the context when that context is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  A DST-safe example
&lt;/h2&gt;

&lt;p&gt;Imagine a recurring meeting at 10:00 in New York. Adding one calendar day should keep the meeting at 10:00 local time, even if the UTC offset changes.&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="nx"&gt;beforeChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ZonedDateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-03-07T10:00:00-05:00[America/New_York]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextDay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;beforeChange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;days&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextDay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 10&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextDay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// may change with DST rules&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is exactly why a named time zone is more useful than manually adding 86,400,000 milliseconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical migration map
&lt;/h2&gt;

&lt;p&gt;Do not replace every Date in one giant refactor. First classify what each value means.&lt;/p&gt;

&lt;h3&gt;
  
  
  Date-only form values
&lt;/h3&gt;

&lt;p&gt;Old approach:&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="nx"&gt;selected&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Safer Temporal model:&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="nx"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PlainDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An HTML date input returns a date-only string. PlainDate preserves that meaning without introducing midnight or a time zone.&lt;/p&gt;

&lt;h3&gt;
  
  
  API timestamps
&lt;/h3&gt;

&lt;p&gt;If an API returns an exact timestamp:&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="nx"&gt;createdAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your UI needs a local representation, convert it with an explicit zone:&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="nx"&gt;local&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toZonedDateTimeISO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Asia/Kolkata&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Current time
&lt;/h3&gt;

&lt;p&gt;For a timestamp:&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="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instant&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the current calendar date in a chosen zone:&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="nx"&gt;todayInIndia&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plainDateISO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Asia/Kolkata&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two calls answer different questions, which is the point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Calendar arithmetic
&lt;/h3&gt;

&lt;p&gt;Old code often adds milliseconds:&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="nx"&gt;tomorrow&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a calendar date, write the intent directly:&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="nx"&gt;tomorrow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plainDateISO&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;days&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interoperating with existing Date code
&lt;/h2&gt;

&lt;p&gt;Most projects will use Date and Temporal together during migration.&lt;/p&gt;

&lt;p&gt;Convert a Date to an Instant:&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="nx"&gt;legacyDate&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;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;legacyDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Convert an Instant back to Date:&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="nx"&gt;legacyAgain&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;epochMilliseconds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This boundary is useful when an existing library still expects Date.&lt;/p&gt;

&lt;p&gt;Do not convert a PlainDate to Date unless you decide what time and time zone that calendar date should represent. That decision cannot be inferred safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser support and the polyfill
&lt;/h2&gt;

&lt;p&gt;Before using the global Temporal object, check your runtime support.&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Temporal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Native Temporal is available.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For production code that must work in browsers without native support, use the @js-temporal/polyfill package and follow its current installation documentation.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Temporal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@js-temporal/polyfill&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A polyfill has bundle-size and compatibility implications, so apply it deliberately. For a small isolated date task, an existing well-tested library may still be the right short-term choice. For new domain modelling, Temporal gives the platform a consistent long-term direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes to avoid
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using PlainDateTime for a real event
&lt;/h3&gt;

&lt;p&gt;PlainDateTime has a date and clock time but no time zone. "10 August at 10:00" is not one exact global moment until a zone is supplied.&lt;/p&gt;

&lt;p&gt;Use ZonedDateTime or Instant for events that must be coordinated across locations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storing only a formatted string
&lt;/h3&gt;

&lt;p&gt;A string such as "10 Aug, 11:30 AM" is presentation, not durable data. Store a machine-readable Temporal string or the underlying structured value, then format it for the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assuming an offset is a time zone
&lt;/h3&gt;

&lt;p&gt;An offset tells you the relationship to UTC at one moment. A named time zone carries rules for future and historical changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparing objects with normal operators
&lt;/h3&gt;

&lt;p&gt;Temporal objects intentionally avoid ambiguous coercion. Use methods such as Temporal.PlainDate.compare(), equals(), since(), or until().&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignoring availability
&lt;/h3&gt;

&lt;p&gt;Stage 4 is a standards milestone, not proof that all users have upgraded browsers. Test the environments in your support matrix and provide a polyfill or fallback when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple decision checklist
&lt;/h2&gt;

&lt;p&gt;Before creating a Temporal value, ask:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is this only a calendar date? Use PlainDate.&lt;/li&gt;
&lt;li&gt;Is this only a clock time? Use PlainTime.&lt;/li&gt;
&lt;li&gt;Is this one exact timestamp? Use Instant.&lt;/li&gt;
&lt;li&gt;Does a named time zone affect the meaning? Use ZonedDateTime.&lt;/li&gt;
&lt;li&gt;Is this an amount of time? Use Duration.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That small modelling decision prevents many bugs before arithmetic or formatting begins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: clearer intent is the real upgrade
&lt;/h2&gt;

&lt;p&gt;Temporal is valuable not because it has more methods than Date, but because the type tells another developer what the value means.&lt;/p&gt;

&lt;p&gt;A birthday is not a timestamp. A meeting in New York is not just an offset. One calendar day is not always 24 hours.&lt;/p&gt;

&lt;p&gt;Temporal puts those distinctions into the API.&lt;/p&gt;

&lt;p&gt;The safest adoption plan in 2026 is straightforward: classify existing date values, migrate one boundary at a time, use native support when available, and include the polyfill where your browser matrix needs it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tc39.es/proposal-temporal/" rel="noopener noreferrer"&gt;https://tc39.es/proposal-temporal/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tc39.es/proposal-temporal/docs/" rel="noopener noreferrer"&gt;https://tc39.es/proposal-temporal/docs/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@js-temporal/polyfill" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@js-temporal/polyfill&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tags: javascript, webdev, temporal, tutorial&lt;/p&gt;

</description>
      <category>api</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stop Guessing Browser Support: A Practical Guide to Web Platform Baseline in 2026</title>
      <dc:creator>Vishal Singh</dc:creator>
      <pubDate>Mon, 10 Aug 2026 06:09:49 +0000</pubDate>
      <link>https://dev.to/vishal_singh_0610/stop-guessing-browser-support-a-practical-guide-to-web-platform-baseline-in-2026-20bg</link>
      <guid>https://dev.to/vishal_singh_0610/stop-guessing-browser-support-a-practical-guide-to-web-platform-baseline-in-2026-20bg</guid>
      <description>&lt;p&gt;Liquid syntax error: 'raw' tag was never closed&lt;/p&gt;
</description>
      <category>css</category>
      <category>frontend</category>
      <category>web</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why Lighthouse and Real-User Core Web Vitals Show Different Results</title>
      <dc:creator>Vishal Singh</dc:creator>
      <pubDate>Wed, 22 Jul 2026 08:34:57 +0000</pubDate>
      <link>https://dev.to/vishal_singh_0610/why-lighthouse-and-real-user-core-web-vitals-show-different-results-16jj</link>
      <guid>https://dev.to/vishal_singh_0610/why-lighthouse-and-real-user-core-web-vitals-show-different-results-16jj</guid>
      <description>&lt;p&gt;You run Lighthouse and get a green performance score. Then you open PageSpeed Insights and the Core Web Vitals assessment says your page needs improvement.&lt;/p&gt;

&lt;p&gt;Or the opposite happens: Lighthouse reports a slow page, while real-user data looks healthy.&lt;/p&gt;

&lt;p&gt;Nothing is necessarily broken. The two results answer different questions.&lt;/p&gt;

&lt;p&gt;Lighthouse gives you lab data from one controlled test. Chrome UX Report, usually called CrUX, gives you field data collected from real Chrome users. Understanding that difference can save hours of debugging the wrong problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab data: a controlled experiment
&lt;/h2&gt;

&lt;p&gt;Lighthouse loads a page using predefined device and network conditions. A typical mobile run applies CPU and network throttling and usually behaves like a new visitor with a cold cache.&lt;/p&gt;

&lt;p&gt;This makes Lighthouse useful because the test can be repeated. You can change your code, run it again, and compare the results under similar conditions.&lt;/p&gt;

&lt;p&gt;Lighthouse is particularly useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;finding render-blocking resources&lt;/li&gt;
&lt;li&gt;identifying large images or JavaScript bundles&lt;/li&gt;
&lt;li&gt;diagnosing main-thread work&lt;/li&gt;
&lt;li&gt;detecting layout shifts during the initial load&lt;/li&gt;
&lt;li&gt;catching performance regressions before deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But a Lighthouse result is still one test from one environment. It is not a survey of every person visiting your website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Field data: what real users experienced
&lt;/h2&gt;

&lt;p&gt;CrUX collects performance measurements from eligible real Chrome users. PageSpeed Insights uses this dataset in its field-data section.&lt;/p&gt;

&lt;p&gt;Instead of testing one simulated device, field data includes a distribution of real visits across different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;phones and computers&lt;/li&gt;
&lt;li&gt;network speeds&lt;/li&gt;
&lt;li&gt;locations&lt;/li&gt;
&lt;li&gt;cache states&lt;/li&gt;
&lt;li&gt;browsing sessions&lt;/li&gt;
&lt;li&gt;user behaviours&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CrUX generally reports a rolling 28-day view and evaluates Core Web Vitals at the 75th percentile. In simple terms, the reported value is intended to represent an experience that at least 75% of visits met or beat.&lt;/p&gt;

&lt;p&gt;This makes field data the better answer to: “What experience are our users actually getting?”&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the numbers disagree
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The tested device is not your users' devices
&lt;/h3&gt;

&lt;p&gt;A Lighthouse mobile test simulates a particular level of CPU and network performance. Your visitors may use faster devices, slower devices, or a completely different mix.&lt;/p&gt;

&lt;p&gt;One lab run cannot reproduce that entire distribution.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Lighthouse often tests a cold load
&lt;/h3&gt;

&lt;p&gt;A lab test commonly loads the page without the benefit of previously cached assets. Returning users may already have fonts, scripts and images cached.&lt;/p&gt;

&lt;p&gt;This can make Lighthouse look worse than field data. The reverse can happen when real users face slow networks, overloaded devices or third-party content that did not appear in the lab run.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Real users interact with the page
&lt;/h3&gt;

&lt;p&gt;A default Lighthouse audit focuses heavily on the initial page load. Real visitors scroll, open menus, accept cookie banners, load more content and use interactive components.&lt;/p&gt;

&lt;p&gt;This difference is especially important for CLS and INP.&lt;/p&gt;

&lt;p&gt;A page may have no obvious layout shift during loading but shift later when a lazy-loaded component, advertisement or embedded widget appears. CrUX can capture that real experience even when a basic Lighthouse run does not.&lt;/p&gt;

&lt;p&gt;Similarly, Interaction to Next Paint depends on actual interactions. A load-only test cannot reproduce every slow click, tap or keyboard action your users encounter.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. You may be comparing a URL with an entire origin
&lt;/h3&gt;

&lt;p&gt;PageSpeed Insights shows URL-level field data when enough data is available. When it is not, it may fall back to origin-level data.&lt;/p&gt;

&lt;p&gt;That means you could accidentally compare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lighthouse data for one specific page&lt;/li&gt;
&lt;li&gt;CrUX data representing many pages across the domain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always check whether the field section says “This URL” or “Origin” before investigating a mismatch.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Field data is historical; Lighthouse is happening now
&lt;/h3&gt;

&lt;p&gt;Lighthouse tests the current version of the page. CrUX uses a rolling window of recent user experiences.&lt;/p&gt;

&lt;p&gt;After deploying a performance improvement, your Lighthouse result can improve immediately while CrUX changes gradually as new visits replace older data.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. The page is not identical for every visitor
&lt;/h3&gt;

&lt;p&gt;A/B tests, ads, consent banners, personalisation, authentication state and third-party scripts can change what gets loaded.&lt;/p&gt;

&lt;p&gt;Lighthouse may see one version while different groups of users see something else. Even repeated Lighthouse runs can fluctuate when the page or underlying conditions change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which result should you trust?
&lt;/h2&gt;

&lt;p&gt;Trust field data when judging the experience your real users receive and whether your site meets Core Web Vitals in practice.&lt;/p&gt;

&lt;p&gt;Use Lighthouse to diagnose problems, reproduce controlled scenarios and prevent new regressions.&lt;/p&gt;

&lt;p&gt;It is not field data versus Lighthouse. A useful workflow needs both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Field data tells you that a real problem exists and how widespread it may be.&lt;/li&gt;
&lt;li&gt;Lab data helps you understand, reproduce and fix specific causes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The overall Lighthouse performance score is also not the same thing as the Core Web Vitals assessment. Compare the individual metrics—LCP, INP and CLS—rather than expecting the two headline results to match.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical debugging workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Read the field-data label
&lt;/h3&gt;

&lt;p&gt;In PageSpeed Insights, confirm whether the CrUX result represents the exact URL or the whole origin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Compare individual metrics
&lt;/h3&gt;

&lt;p&gt;Identify whether the disagreement is mainly in LCP, INP or CLS. Each metric points toward different causes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Re-run Lighthouse consistently
&lt;/h3&gt;

&lt;p&gt;Use the same test mode, device category and environment. Run it more than once and look for a pattern instead of treating one score as absolute truth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Investigate what the lab test misses
&lt;/h3&gt;

&lt;p&gt;If field data is worse, examine real user journeys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scroll through the full page&lt;/li&gt;
&lt;li&gt;interact with menus and forms&lt;/li&gt;
&lt;li&gt;test consent banners and embedded widgets&lt;/li&gt;
&lt;li&gt;check slower devices and networks&lt;/li&gt;
&lt;li&gt;investigate post-load layout shifts&lt;/li&gt;
&lt;li&gt;collect your own Real User Monitoring data when possible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The web-vitals JavaScript library can help collect field measurements and attribution information from your own users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Use Lighthouse diagnostics to fix reproducible causes
&lt;/h3&gt;

&lt;p&gt;If the lab and field metrics point in the same direction, Lighthouse audits can help locate heavy resources, long tasks, delayed content and layout-shift culprits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Monitor after deployment
&lt;/h3&gt;

&lt;p&gt;Confirm the immediate improvement in a controlled test, then watch field data over time. CrUX will not fully reflect a deployment immediately because its reporting window includes earlier visits.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple way to remember it
&lt;/h2&gt;

&lt;p&gt;Lighthouse is a crash test performed under controlled conditions.&lt;/p&gt;

&lt;p&gt;CrUX is information collected from cars being driven by real people on real roads.&lt;/p&gt;

&lt;p&gt;The crash test helps engineers find weaknesses. The road data shows what drivers actually experience. You need both to build something reliable.&lt;/p&gt;

&lt;p&gt;The next time Lighthouse and Core Web Vitals disagree, do not ask which tool is wrong. Ask what population, time window, device conditions and user journey each result represents.&lt;/p&gt;

&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/lab-and-field-data-differences" rel="noopener noreferrer"&gt;https://web.dev/articles/lab-and-field-data-differences&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/vitals-tools" rel="noopener noreferrer"&gt;https://web.dev/articles/vitals-tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/lighthouse/performance/performance-scoring" rel="noopener noreferrer"&gt;https://developer.chrome.com/docs/lighthouse/performance/performance-scoring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/optimize-cls" rel="noopener noreferrer"&gt;https://web.dev/articles/optimize-cls&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webperf</category>
      <category>webdev</category>
      <category>lighthouse</category>
      <category>corewebvitals</category>
    </item>
    <item>
      <title>Grok Build Is Now Open Source: What Developers Can Learn from Its Coding-Agent Architecture</title>
      <dc:creator>Vishal Singh</dc:creator>
      <pubDate>Mon, 20 Jul 2026 10:15:56 +0000</pubDate>
      <link>https://dev.to/vishal_singh_0610/grok-build-is-now-open-source-what-developers-can-learn-from-its-coding-agent-architecture-1gko</link>
      <guid>https://dev.to/vishal_singh_0610/grok-build-is-now-open-source-what-developers-can-learn-from-its-coding-agent-architecture-1gko</guid>
      <description>&lt;p&gt;AI coding tools are often discussed as black boxes: you give them a task, they inspect a repository, run commands, edit files, and return a result. Grok Build is now a useful exception.&lt;/p&gt;

&lt;p&gt;SpaceXAI has open-sourced Grok Build, its terminal-based coding agent and full-screen TUI. The public repository exposes the harness behind the product, so developers can inspect how context is assembled, model responses are parsed, tools are dispatched, and long-running work is managed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What has been open-sourced?
&lt;/h2&gt;

&lt;p&gt;The published Rust code includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the agent loop that connects model responses to tool calls&lt;/li&gt;
&lt;li&gt;tools for reading, editing, searching, and running commands&lt;/li&gt;
&lt;li&gt;the terminal UI, including plan review and inline diffs&lt;/li&gt;
&lt;li&gt;the extension system for skills, plugins, hooks, MCP servers, and subagents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because an AI coding agent is more than a model. The surrounding harness decides what context the model sees, which actions it can take, how failures are handled, and how a developer stays in control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three useful takeaways for developers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Context assembly is a product feature
&lt;/h3&gt;

&lt;p&gt;A coding agent must decide which files, instructions, tool results, and previous decisions belong in the next model request. Better context is not simply more context; it is the right information at the right time.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Tool execution needs a feedback loop
&lt;/h3&gt;

&lt;p&gt;The model can propose an edit or command, but the harness must execute it, capture the result, surface errors, and feed useful evidence back into the next step. Reliability comes from this loop, not from one perfect prompt.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Extensibility should have clear boundaries
&lt;/h3&gt;

&lt;p&gt;Grok Build supports skills, plugins, hooks, MCP servers, subagents, AGENTS.md instructions, and configuration. The architecture is a good reference for anyone building an internal developer agent that must adapt to different repositories and workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can you run it yourself?
&lt;/h2&gt;

&lt;p&gt;The repository contains the Rust source for the Grok CLI/TUI and its agent runtime. You can compile it locally, and the official announcement says it can be pointed at local inference through &lt;code&gt;config.toml&lt;/code&gt;. Prebuilt installers are also available for macOS, Linux, and Windows.&lt;/p&gt;

&lt;p&gt;One important distinction: an open-source agent harness does not automatically make every hosted model or service free. The code, model access, authentication, and usage costs are separate layers, so check the current terms before choosing a setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this release is worth studying
&lt;/h2&gt;

&lt;p&gt;Even if you do not plan to switch coding assistants, the repository is valuable as a real-world reference for agent loops, terminal UX, tool dispatch, sandboxing, memory, and extensibility.&lt;/p&gt;

&lt;p&gt;Open-source coding agents make an important question easier to answer: not only "What can the model do?" but also "How does the system decide, act, verify, and recover?"&lt;/p&gt;

&lt;p&gt;Would you explore the Grok Build source to learn from the architecture, contribute your own extension, or run it with local inference?&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://x.ai/news/grok-build-open-source" rel="noopener noreferrer"&gt;Grok Build is now open source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/xai-org/grok-build" rel="noopener noreferrer"&gt;xai-org/grok-build on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
