Pure CSSMIT licensed

Clay Modal Dialog with Floating Depth

A top-tier clay dialog built on the native popover API — no focus-trap library, no aria-modal hand-wiring. The browser handles the top layer, light dismiss, Escape and focus. Clay does the rest: tier-4 depth, a blurred scrim, and an entry that scales from 0.94 with @starting-style, plus a matching exit.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: a 10,000+ component Figma UI kit · from $129 one-time

Untitled UI · Affiliate link

Get Untitled UI Pro

The code

<section class="cl-14" aria-label="Clay modal dialog with floating depth demo">
  <div class="cl-14__stage">
    <div class="cl-14__page">
      <h2 class="cl-14__h">Native popover, clay depth</h2>
      <p class="cl-14__lede">The top layer, light dismiss, Escape and focus are the browser's job. Tier-4 shadows and a blurred backdrop are ours.</p>
      <button class="cl-14__open" type="button" popovertarget="cl-14-dialog">Publish collection</button>
      <ul class="cl-14__facts">
        <li>popover="auto"</li>
        <li>@starting-style</li>
        <li>transition-behavior: allow-discrete</li>
        <li>::backdrop blur</li>
      </ul>
    </div>
    <div class="cl-14__modal" id="cl-14-dialog" popover="auto" aria-labelledby="cl-14-title">
      <span class="cl-14__grip" aria-hidden="true"></span>
      <h3 class="cl-14__title" id="cl-14-title">Publish “20 CSS Claymorphism”?</h3>
      <p class="cl-14__body">All 20 demos become public and the collection page goes live at once. You can unpublish any single demo later.</p>
      <ul class="cl-14__check">
        <li>Snippets validated</li>
        <li>Reduced-motion pass</li>
        <li>Contrast pass</li>
      </ul>
      <div class="cl-14__actions">
        <button class="cl-14__btn cl-14__btn--ghost" type="button" popovertarget="cl-14-dialog" popovertargetaction="hide">Not yet</button>
        <button class="cl-14__btn cl-14__btn--go" type="button" popovertarget="cl-14-dialog" popovertargetaction="hide">Publish now</button>
      </div>
    </div>
    <p class="cl-14__chip" aria-hidden="true">tier 4 elevation · display + overlay allow-discrete · native top layer</p>
  </div>
</section>
.cl-14,
.cl-14 *,
.cl-14 *::before,
.cl-14 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cl-14 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  background: var(--cl-14-bg);
  --cl-14-bg: oklch(0.93 0.035 300);
  --cl-14-clay: oklch(0.9 0.05 300);
  --cl-14-accent: oklch(0.79 0.14 300);
  --cl-14-shade: oklch(0.5 0.1 300/.55);
  --cl-14-light: oklch(1 0.012 300/.95);
  --cl-14-ink: oklch(0.27 0.06 300);
  --cl-14-mut: oklch(0.5 0.045 300);
  font-family: system-ui,-apple-system,'Segoe UI',sans-serif;
  color: var(--cl-14-ink);
}

.cl-14__stage {
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 20px;
  padding: clamp(20px,5vw,56px);
  background: radial-gradient(85% 60% at 50% 0%,oklch(0.97 0.035 310),transparent 68%);
}

.cl-14__page {
  display: grid;
  gap: 14px;
  justify-items: center;
  text-align: center;
  width: min(100%,620px);
  padding: clamp(26px,4vw,44px);
  border-radius: 44px;
  background: color-mix(in oklch,var(--cl-14-bg) 94%,white);
  box-shadow: 0 26px 48px -20px var(--cl-14-shade),inset 0 -16px 28px -16px var(--cl-14-shade),inset 0 18px 28px -12px var(--cl-14-light);
}

.cl-14__h {
  font-size: clamp(23px,3.6vw,32px);
  font-weight: 780;
  letter-spacing: -.022em;
  text-wrap: balance;
}

.cl-14__lede {
  max-width: 48ch;
  font-size: 15px;
  line-height: 1.6;
  color: var(--cl-14-mut);
  text-wrap: pretty;
}

.cl-14__open {
  min-height: 58px;
  margin-top: 6px;
  padding: 0 34px;
  border: 0;
  border-radius: 26px;
  font: inherit;
  font-size: 16px;
  font-weight: 680;
  color: oklch(0.24 0.07 300);
  cursor: pointer;
  background: var(--cl-14-accent);
  box-shadow: 0 22px 36px -12px var(--cl-14-shade),inset 0 -16px 24px -10px var(--cl-14-shade),inset 0 18px 24px -8px var(--cl-14-light);
  transition: translate .2s cubic-bezier(.2,.8,.3,1.4),scale .2s cubic-bezier(.2,.8,.3,1.4),box-shadow .2s;
}

.cl-14__open:hover {
  translate: 0 -4px;
}

.cl-14__open:active {
  scale: 1 .93;
  translate: 0 3px;
  box-shadow: inset 0 14px 22px -8px var(--cl-14-shade);
}

.cl-14__open:focus-visible {
  outline: 3px solid var(--cl-14-ink);
  outline-offset: 4px;
}

.cl-14__facts {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  margin-top: 6px;
}

.cl-14__facts li {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  padding: 7px 12px;
  border-radius: 99px;
  color: var(--cl-14-mut);
  background: oklch(1 0 0/.6);
  box-shadow: inset 0 -4px 8px -7px var(--cl-14-shade),inset 0 5px 8px -5px oklch(1 0 0);
}

.cl-14__modal {
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(92vw,460px);
  max-height: 86svh;
  overflow: auto;
  display: none;
  gap: 14px;
  padding: clamp(24px,3.6vw,34px);
  border: 0;
  border-radius: 40px;
  background: var(--cl-14-clay);
  color: var(--cl-14-ink);
  box-shadow: 0 60px 110px -30px oklch(0.32 0.08 300/.65),0 24px 44px -16px var(--cl-14-shade),inset 0 -20px 34px -16px var(--cl-14-shade),inset 0 24px 34px -10px var(--cl-14-light);
  opacity: 0;
  scale: .94;
  translate: 0 12px;
  transition: opacity .32s cubic-bezier(.2,.8,.3,1),scale .32s cubic-bezier(.2,.9,.3,1.15),translate .32s cubic-bezier(.2,.9,.3,1.15),display .32s allow-discrete,overlay .32s allow-discrete;
}

.cl-14__modal:popover-open {
  display: grid;
  opacity: 1;
  scale: 1;
  translate: 0;
}

@starting-style {
  .cl-14__modal:popover-open {
    display: grid;
    opacity: 0;
    scale: .94;
    translate: 0 12px;
  }
}

.cl-14__modal::backdrop {
  background: oklch(0.34 0.06 300/.42);
  -webkit-backdrop-filter: blur(7px);
  backdrop-filter: blur(7px);
  opacity: 0;
  transition: opacity .32s,display .32s allow-discrete,overlay .32s allow-discrete;
}

.cl-14__modal:popover-open::backdrop {
  opacity: 1;
}

@starting-style {
  .cl-14__modal:popover-open::backdrop {
    opacity: 0;
  }
}

.cl-14__grip {
  justify-self: center;
  width: 54px;
  height: 6px;
  border-radius: 99px;
  background: color-mix(in oklch,var(--cl-14-shade) 40%,transparent);
  box-shadow: inset 0 2px 3px -1px var(--cl-14-shade);
}

.cl-14__title {
  font-size: clamp(20px,3vw,24px);
  font-weight: 780;
  letter-spacing: -.02em;
  text-wrap: balance;
}

.cl-14__body {
  font-size: 14.5px;
  line-height: 1.6;
  color: color-mix(in oklch,var(--cl-14-ink) 82%,transparent);
  text-wrap: pretty;
}

.cl-14__check {
  list-style: none;
  display: grid;
  gap: 8px;
  margin: 2px 0 4px;
}

.cl-14__check li {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 15px;
  border-radius: 20px;
  font-size: 13.5px;
  font-weight: 600;
  background: color-mix(in oklch,var(--cl-14-clay) 78%,white);
  box-shadow: inset 0 -6px 11px -8px var(--cl-14-shade),inset 0 7px 11px -6px var(--cl-14-light);
}

.cl-14__check li::before {
  content: '';
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: oklch(0.78 0.14 155);
  box-shadow: inset 0 5px 7px -4px oklch(1 0 0/.7),0 4px 7px -4px var(--cl-14-shade);
}

.cl-14__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 4px;
}

.cl-14__btn {
  flex: 1;
  min-width: 130px;
  min-height: 52px;
  padding: 0 22px;
  border: 0;
  border-radius: 24px;
  font: inherit;
  font-size: 14.5px;
  font-weight: 680;
  cursor: pointer;
  transition: translate .18s,scale .18s cubic-bezier(.2,.8,.3,1.4),box-shadow .18s;
}

.cl-14__btn--ghost {
  color: var(--cl-14-ink);
  background: color-mix(in oklch,var(--cl-14-clay) 66%,white);
  box-shadow: 0 12px 20px -12px var(--cl-14-shade),inset 0 -9px 15px -9px var(--cl-14-shade),inset 0 11px 15px -7px var(--cl-14-light);
}

.cl-14__btn--go {
  color: oklch(0.24 0.07 300);
  background: var(--cl-14-accent);
  box-shadow: 0 16px 26px -12px var(--cl-14-shade),inset 0 -11px 18px -9px var(--cl-14-shade),inset 0 13px 18px -7px var(--cl-14-light);
}

.cl-14__btn:hover {
  translate: 0 -3px;
}

.cl-14__btn:active {
  scale: 1 .94;
  translate: 0 3px;
  box-shadow: inset 0 12px 18px -8px var(--cl-14-shade);
}

.cl-14__btn:focus-visible {
  outline: 3px solid var(--cl-14-ink);
  outline-offset: 3px;
}

.cl-14__chip {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  color: var(--cl-14-mut);
  padding: 9px 16px;
  border-radius: 99px;
  text-align: center;
  background: oklch(1 0 0/.55);
  box-shadow: inset 0 -4px 10px -8px var(--cl-14-shade),inset 0 6px 10px -6px oklch(1 0 0);
}

@media (prefers-reduced-motion: reduce) {
  .cl-14 * {
    transition-duration: .01ms !important;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Claymorphism from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Clay Modal Dialog with Floating Depth
Source: https://codefronts.com/design-styles/css-claymorphism/clay-modal-dialog-with-floating-depth/

A top-tier clay dialog built on the native popover API — no focus-trap library, no aria-modal hand-wiring. The browser handles the top layer, light dismiss, Escape and focus. Clay does the rest: tier-4 depth, a blurred scrim, and an entry that scales from 0.94 with @starting-style, plus a matching exit.
## HTML
```html
<section class="cl-14" aria-label="Clay modal dialog with floating depth demo">
  <div class="cl-14__stage">
    <div class="cl-14__page">
      <h2 class="cl-14__h">Native popover, clay depth</h2>
      <p class="cl-14__lede">The top layer, light dismiss, Escape and focus are the browser's job. Tier-4 shadows and a blurred backdrop are ours.</p>
      <button class="cl-14__open" type="button" popovertarget="cl-14-dialog">Publish collection</button>
      <ul class="cl-14__facts">
        <li>popover="auto"</li>
        <li>@starting-style</li>
        <li>transition-behavior: allow-discrete</li>
        <li>::backdrop blur</li>
      </ul>
    </div>
    <div class="cl-14__modal" id="cl-14-dialog" popover="auto" aria-labelledby="cl-14-title">
      <span class="cl-14__grip" aria-hidden="true"></span>
      <h3 class="cl-14__title" id="cl-14-title">Publish “20 CSS Claymorphism”?</h3>
      <p class="cl-14__body">All 20 demos become public and the collection page goes live at once. You can unpublish any single demo later.</p>
      <ul class="cl-14__check">
        <li>Snippets validated</li>
        <li>Reduced-motion pass</li>
        <li>Contrast pass</li>
      </ul>
      <div class="cl-14__actions">
        <button class="cl-14__btn cl-14__btn--ghost" type="button" popovertarget="cl-14-dialog" popovertargetaction="hide">Not yet</button>
        <button class="cl-14__btn cl-14__btn--go" type="button" popovertarget="cl-14-dialog" popovertargetaction="hide">Publish now</button>
      </div>
    </div>
    <p class="cl-14__chip" aria-hidden="true">tier 4 elevation · display + overlay allow-discrete · native top layer</p>
  </div>
</section>
```
## CSS
```css
.cl-14,
.cl-14 *,
.cl-14 *::before,
.cl-14 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cl-14 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  background: var(--cl-14-bg);
  --cl-14-bg: oklch(0.93 0.035 300);
  --cl-14-clay: oklch(0.9 0.05 300);
  --cl-14-accent: oklch(0.79 0.14 300);
  --cl-14-shade: oklch(0.5 0.1 300/.55);
  --cl-14-light: oklch(1 0.012 300/.95);
  --cl-14-ink: oklch(0.27 0.06 300);
  --cl-14-mut: oklch(0.5 0.045 300);
  font-family: system-ui,-apple-system,'Segoe UI',sans-serif;
  color: var(--cl-14-ink);
}

.cl-14__stage {
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 20px;
  padding: clamp(20px,5vw,56px);
  background: radial-gradient(85% 60% at 50% 0%,oklch(0.97 0.035 310),transparent 68%);
}

.cl-14__page {
  display: grid;
  gap: 14px;
  justify-items: center;
  text-align: center;
  width: min(100%,620px);
  padding: clamp(26px,4vw,44px);
  border-radius: 44px;
  background: color-mix(in oklch,var(--cl-14-bg) 94%,white);
  box-shadow: 0 26px 48px -20px var(--cl-14-shade),inset 0 -16px 28px -16px var(--cl-14-shade),inset 0 18px 28px -12px var(--cl-14-light);
}

.cl-14__h {
  font-size: clamp(23px,3.6vw,32px);
  font-weight: 780;
  letter-spacing: -.022em;
  text-wrap: balance;
}

.cl-14__lede {
  max-width: 48ch;
  font-size: 15px;
  line-height: 1.6;
  color: var(--cl-14-mut);
  text-wrap: pretty;
}

.cl-14__open {
  min-height: 58px;
  margin-top: 6px;
  padding: 0 34px;
  border: 0;
  border-radius: 26px;
  font: inherit;
  font-size: 16px;
  font-weight: 680;
  color: oklch(0.24 0.07 300);
  cursor: pointer;
  background: var(--cl-14-accent);
  box-shadow: 0 22px 36px -12px var(--cl-14-shade),inset 0 -16px 24px -10px var(--cl-14-shade),inset 0 18px 24px -8px var(--cl-14-light);
  transition: translate .2s cubic-bezier(.2,.8,.3,1.4),scale .2s cubic-bezier(.2,.8,.3,1.4),box-shadow .2s;
}

.cl-14__open:hover {
  translate: 0 -4px;
}

.cl-14__open:active {
  scale: 1 .93;
  translate: 0 3px;
  box-shadow: inset 0 14px 22px -8px var(--cl-14-shade);
}

.cl-14__open:focus-visible {
  outline: 3px solid var(--cl-14-ink);
  outline-offset: 4px;
}

.cl-14__facts {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  margin-top: 6px;
}

.cl-14__facts li {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  padding: 7px 12px;
  border-radius: 99px;
  color: var(--cl-14-mut);
  background: oklch(1 0 0/.6);
  box-shadow: inset 0 -4px 8px -7px var(--cl-14-shade),inset 0 5px 8px -5px oklch(1 0 0);
}

.cl-14__modal {
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(92vw,460px);
  max-height: 86svh;
  overflow: auto;
  display: none;
  gap: 14px;
  padding: clamp(24px,3.6vw,34px);
  border: 0;
  border-radius: 40px;
  background: var(--cl-14-clay);
  color: var(--cl-14-ink);
  box-shadow: 0 60px 110px -30px oklch(0.32 0.08 300/.65),0 24px 44px -16px var(--cl-14-shade),inset 0 -20px 34px -16px var(--cl-14-shade),inset 0 24px 34px -10px var(--cl-14-light);
  opacity: 0;
  scale: .94;
  translate: 0 12px;
  transition: opacity .32s cubic-bezier(.2,.8,.3,1),scale .32s cubic-bezier(.2,.9,.3,1.15),translate .32s cubic-bezier(.2,.9,.3,1.15),display .32s allow-discrete,overlay .32s allow-discrete;
}

.cl-14__modal:popover-open {
  display: grid;
  opacity: 1;
  scale: 1;
  translate: 0;
}

@starting-style {
  .cl-14__modal:popover-open {
    display: grid;
    opacity: 0;
    scale: .94;
    translate: 0 12px;
  }
}

.cl-14__modal::backdrop {
  background: oklch(0.34 0.06 300/.42);
  -webkit-backdrop-filter: blur(7px);
  backdrop-filter: blur(7px);
  opacity: 0;
  transition: opacity .32s,display .32s allow-discrete,overlay .32s allow-discrete;
}

.cl-14__modal:popover-open::backdrop {
  opacity: 1;
}

@starting-style {
  .cl-14__modal:popover-open::backdrop {
    opacity: 0;
  }
}

.cl-14__grip {
  justify-self: center;
  width: 54px;
  height: 6px;
  border-radius: 99px;
  background: color-mix(in oklch,var(--cl-14-shade) 40%,transparent);
  box-shadow: inset 0 2px 3px -1px var(--cl-14-shade);
}

.cl-14__title {
  font-size: clamp(20px,3vw,24px);
  font-weight: 780;
  letter-spacing: -.02em;
  text-wrap: balance;
}

.cl-14__body {
  font-size: 14.5px;
  line-height: 1.6;
  color: color-mix(in oklch,var(--cl-14-ink) 82%,transparent);
  text-wrap: pretty;
}

.cl-14__check {
  list-style: none;
  display: grid;
  gap: 8px;
  margin: 2px 0 4px;
}

.cl-14__check li {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 15px;
  border-radius: 20px;
  font-size: 13.5px;
  font-weight: 600;
  background: color-mix(in oklch,var(--cl-14-clay) 78%,white);
  box-shadow: inset 0 -6px 11px -8px var(--cl-14-shade),inset 0 7px 11px -6px var(--cl-14-light);
}

.cl-14__check li::before {
  content: '';
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: oklch(0.78 0.14 155);
  box-shadow: inset 0 5px 7px -4px oklch(1 0 0/.7),0 4px 7px -4px var(--cl-14-shade);
}

.cl-14__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 4px;
}

.cl-14__btn {
  flex: 1;
  min-width: 130px;
  min-height: 52px;
  padding: 0 22px;
  border: 0;
  border-radius: 24px;
  font: inherit;
  font-size: 14.5px;
  font-weight: 680;
  cursor: pointer;
  transition: translate .18s,scale .18s cubic-bezier(.2,.8,.3,1.4),box-shadow .18s;
}

.cl-14__btn--ghost {
  color: var(--cl-14-ink);
  background: color-mix(in oklch,var(--cl-14-clay) 66%,white);
  box-shadow: 0 12px 20px -12px var(--cl-14-shade),inset 0 -9px 15px -9px var(--cl-14-shade),inset 0 11px 15px -7px var(--cl-14-light);
}

.cl-14__btn--go {
  color: oklch(0.24 0.07 300);
  background: var(--cl-14-accent);
  box-shadow: 0 16px 26px -12px var(--cl-14-shade),inset 0 -11px 18px -9px var(--cl-14-shade),inset 0 13px 18px -7px var(--cl-14-light);
}

.cl-14__btn:hover {
  translate: 0 -3px;
}

.cl-14__btn:active {
  scale: 1 .94;
  translate: 0 3px;
  box-shadow: inset 0 12px 18px -8px var(--cl-14-shade);
}

.cl-14__btn:focus-visible {
  outline: 3px solid var(--cl-14-ink);
  outline-offset: 3px;
}

.cl-14__chip {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  color: var(--cl-14-mut);
  padding: 9px 16px;
  border-radius: 99px;
  text-align: center;
  background: oklch(1 0 0/.55);
  box-shadow: inset 0 -4px 10px -8px var(--cl-14-shade),inset 0 6px 10px -6px oklch(1 0 0);
}

@media (prefers-reduced-motion: reduce) {
  .cl-14 * {
    transition-duration: .01ms !important;
  }
}
```

How this works

The markup is two attributes:

<button popovertarget="dlg">Open</button>
<div id="dlg" popover="auto" class="clay-modal">…</div>

That gives you the top layer (no z-index wars), light dismiss on outside click, Escape to close, and correct focus handling — the things every hand-rolled modal gets wrong.

Animating from display:none used to be impossible. Three modern pieces make it work:

[popover]{ opacity:0; scale:.94; transition:opacity .3s, scale .3s, display .3s allow-discrete, overlay .3s allow-discrete; }
[popover]:popover-open{ opacity:1; scale:1; }
@starting-style{ [popover]:popover-open{ opacity:0; scale:.94; } }
[popover]::backdrop{ background:oklch(0.3 0.05 300/.4); backdrop-filter:blur(6px); }

allow-discrete lets display and overlay participate in the transition; @starting-style supplies the "before it was shown" values. Without the overlay entry the element leaves the top layer instantly and the exit animation is invisible.

The clay part is elevation: this is tier 4 — the longest drop shadow in the system plus a brighter inset sheen — over a blurred ::backdrop. That combination is what makes the dialog read as floating well above the page rather than pasted onto it.

Make it yours

  • Bottom sheet on mobile: @media (max-width:520px) — pin to inset:auto 0 0 0, radius 40px 40px 0 0, and animate translate:0 100% instead of scale.
  • Non-modal popovers (menus, tooltips) use popover="auto" with CSS anchor positioning — same animation recipe, no scrim.
  • Confirm-destructive variant: swap the accent hue to your danger token and put the primary action on the right; keep the clay press state so it still feels physical.
  • Nested dialogs: popovers stack in the top layer automatically — just make sure the inner one is not a descendant of a transformed ancestor.

Gotchas — read before shipping

  • popover="auto" gives light dismiss and Escape but is NOT automatically a modal — it does not block background interaction the way <dialog.showModal()> does. For a true modal use <dialog> plus showModal(), or accept light-dismiss semantics.
  • Without overlay .3s allow-discrete the exit animation silently does not run.
  • @starting-style must be a separate rule, and specificity matters: put it after the open-state rule.
  • A dialog with no focusable content leaves focus on the body — always include a close button as the first or last focusable element.
  • ::backdrop does not inherit custom properties from the element in older engines; declare its colors literally or on :root.

Browser support

ChromeSafariFirefoxEdge
117+17.5+129+117+

Floor set by oklch(), color-mix(), backdrop-filter, @starting-style, transition-behavior, text-wrap as this demo is written. The core technique itself goes back further — Chrome 114+.

popover: Chrome 114 / Safari 17 / Firefox 125. @starting-style + allow-discrete: Chrome 117 / Safari 17.5 / Firefox 129. Older engines ignore the popover attribute — ship a <dialog> fallback if you need them.

Techniques used in this demo

Search CodeFronts

Loading…