22 CSS Brutalism07 / 22

Light JSMIT licensed

Neubrutalist Modal Dialog

A native <dialog> opened with showModal() — so the focus trap, Escape handling and inert background come from the platform, not from 200 lines of JavaScript. The block has a pastel header strip, a black close button in the corner and a flat semi-opaque ::backdrop with no blur, because blur is the one thing this aesthetic never does.

Published

Live Demo
Try it

The code

<div class="brt-07">
  <button class="brt-07__theme" type="button" aria-pressed="false" aria-label="Switch to dark theme">
    <svg class="brt-07__moon" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><path d="M20 14.2A8.2 8.2 0 0 1 9.8 4 8.4 8.4 0 1 0 20 14.2" stroke="currentColor" stroke-width="2.2" stroke-linejoin="round"/></svg>
    <svg class="brt-07__sun" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="4.2" stroke="currentColor" stroke-width="2.2"/><path d="M12 3v2.2M12 18.8V21M3 12h2.2M18.8 12H21M5.6 5.6l1.6 1.6M16.8 16.8l1.6 1.6M18.4 5.6l-1.6 1.6M7.2 16.8l-1.6 1.6" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>
  </button>

  <div class="brt-07__stage">
    <section class="brt-07__card" aria-labelledby="brt-07-h">
      <h2 class="brt-07__h" id="brt-07-h">Northwind Studio</h2>
      <p class="brt-07__meta">14 pages · last published 3 days ago</p>
      <p class="brt-07__p">Archiving hides the project from your workspace and pauses billing for its seats. You can restore it for 90 days.</p>
      <div class="brt-07__actions">
        <button class="brt-07__open" id="brt-07-open" type="button">Archive project</button>
        <a class="brt-07__link" href="#brt-07-h">View activity</a>
      </div>
      <p class="brt-07__status" id="brt-07-status" role="status" aria-live="polite"></p>
    </section>

    <dialog class="brt-07__dialog" id="brt-07-dialog" aria-labelledby="brt-07-dh" aria-describedby="brt-07-dp">
      <header class="brt-07__strip">
        <h3 class="brt-07__dh" id="brt-07-dh">Archive this project?</h3>
        <button class="brt-07__x" id="brt-07-x" type="button" aria-label="Close dialog">
          <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="3" stroke-linecap="square"/></svg>
        </button>
      </header>
      <div class="brt-07__body">
        <p class="brt-07__dp" id="brt-07-dp">Northwind Studio and its 14 pages will be hidden from the workspace. Custom domains stay reserved.</p>
        <ul class="brt-07__facts">
          <li><b>3 seats</b> released back to your plan</li>
          <li><b>90 days</b> to restore before deletion</li>
          <li><b>2 domains</b> stay pointed at a holding page</li>
        </ul>
        <label class="brt-07__confirm">
          <input class="brt-07__box" type="checkbox" id="brt-07-ack">
          <span>Tell the three collaborators by email</span>
        </label>
      </div>
      <footer class="brt-07__foot">
        <button class="brt-07__btn brt-07__btn--ghost" type="button" id="brt-07-cancel">Keep it active</button>
        <button class="brt-07__btn brt-07__btn--dark" type="button" id="brt-07-confirm" autofocus>Archive project</button>
      </footer>
    </dialog>
  </div>
</div>
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&display=swap');

.brt-07 {
  --page: #f8f5ed;
  --panel: #ffffff;
  --ink: #0b0b0f;
  --on: #0b0b0f;
  --muted: #4a4a52;
  --lilac: #c4b5fd;
  --mint: #86efac;
  --coral: #fda4af;
  --butter: #fde68a;
  --sky: #93c5fd;
  --strip: #c4b5fd;
  --edge: #000000;
  --sh: #000000;
  --bw: 3px;
  --r: 16px;
  --w: 34rem;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  position: relative;
  box-sizing: border-box;
  background: var(--page);
  color: var(--ink);
  font-family: 'Space Grotesk','Sora',ui-sans-serif,system-ui,sans-serif;
}

.brt-07 *,
.brt-07 *::before,
.brt-07 *::after {
  box-sizing: border-box;
}

@supports (color: oklch(0.7 0.1 300)) {
  .brt-07 {
    --page: oklch(0.97 0.016 90);
    --panel: oklch(1 0 0);
    --ink: oklch(0.16 0.01 285);
    --lilac: oklch(0.82 0.11 296);
    --mint: oklch(0.89 0.13 155);
    --coral: oklch(0.83 0.10 15);
    --butter: oklch(0.92 0.11 95);
    --sky: oklch(0.83 0.09 250);
    --strip: oklch(0.82 0.11 296);
  }
}

.brt-07__theme {
  position: absolute;
  inset-block-start: 1rem;
  inset-inline-end: 1rem;
  z-index: 5;
  inline-size: 2.5rem;
  block-size: 2.5rem;
  display: grid;
  place-items: center;
  padding: 0;
  background: var(--panel);
  color: var(--ink);
  border: var(--bw) solid var(--edge);
  border-radius: 10px;
  box-shadow: 4px 4px 0 var(--sh);
  cursor: pointer;
  transition: translate .14s ease, box-shadow .14s ease;
}

.brt-07__theme::after {
  content: '';
  position: absolute;
  inset: -2px;
}

.brt-07__theme svg {
  inline-size: 1.25rem;
  block-size: 1.25rem;
}

.brt-07__theme:active {
  translate: 2px 2px;
  box-shadow: 2px 2px 0 var(--sh);
}

.brt-07__theme:focus-visible {
  outline: 3px solid var(--edge);
  outline-offset: 3px;
}

.brt-07__sun {
  display: none;
}

.brt-07__stage {
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: clamp(1.25rem,5vw,3rem);
}

.brt-07__card {
  inline-size: min(30rem,100%);
  padding: clamp(1.4rem,3.5vw,2rem);
  background: var(--panel);
  border: var(--bw) solid var(--edge);
  border-radius: var(--r);
  box-shadow: 8px 8px 0 var(--sh);
}

.brt-07__h {
  margin: 0;
  font-size: clamp(1.5rem,4vw,2rem);
  font-weight: 700;
  letter-spacing: -.025em;
}

.brt-07__meta {
  margin: .35rem 0 0;
  font-size: .8rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}

.brt-07__p {
  margin: 1rem 0 1.5rem;
  font-size: .98rem;
  line-height: 1.55;
  color: var(--ink);
  text-wrap: pretty;
}

.brt-07__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
}

.brt-07__open {
  min-block-size: 2.9rem;
  padding: .75rem 1.15rem;
  font: inherit;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  background: var(--coral);
  color: var(--on);
  border: var(--bw) solid var(--edge);
  border-radius: 11px;
  box-shadow: 5px 5px 0 var(--sh);
  transition: translate .14s ease, box-shadow .14s ease;
}

.brt-07__open:hover {
  translate: -1px -1px;
  box-shadow: 7px 7px 0 var(--sh);
}

.brt-07__open:active {
  translate: 2px 2px;
  box-shadow: 2px 2px 0 var(--sh);
}

.brt-07__open:focus-visible {
  outline: 3px solid var(--edge);
  outline-offset: 3px;
}

.brt-07__link {
  display: inline-flex;
  align-items: center;
  min-block-size: 2.75rem;
  color: var(--ink);
  font-weight: 700;
  font-size: .95rem;
  text-decoration: 3px underline var(--sky);
  text-underline-offset: 4px;
}

.brt-07__link:hover {
  background: var(--sky);
  color: var(--on);
}

.brt-07__link:focus-visible {
  outline: 3px solid var(--edge);
  outline-offset: 2px;
}

.brt-07__status {
  margin: 1.1rem 0 0;
  min-block-size: 1.2rem;
  font-size: .88rem;
  font-weight: 700;
}

.brt-07__status:not(:empty) {
  display: inline-block;
  padding: .4rem .6rem;
  background: var(--mint);
  color: var(--on);
  border: 2px solid var(--edge);
  border-radius: 8px;
}

.brt-07__dialog {
  inline-size: min(var(--w),calc(100% - 2rem));
  max-block-size: min(90svh,42rem);
  padding: 0;
  margin: auto;
  background: var(--panel);
  color: var(--ink);
  overflow: hidden;
  border: var(--bw) solid var(--edge);
  border-radius: var(--r);
  box-shadow: 12px 12px 0 var(--sh);
}

@supports selector(dialog: modal) {
  .brt-07__dialog {
    opacity: 1;
    translate: 0 0;
    scale: 1;
    transition: opacity .17s ease, translate .17s ease, scale .17s ease, display .17s allow-discrete, overlay .17s allow-discrete;
  }

  @starting-style {
    .brt-07__dialog[open] {
      opacity: 0;
      translate: 0 -12px;
      scale: .97;
    }
  }

  .brt-07__dialog:not([open]) {
    opacity: 0;
    translate: 0 -12px;
    scale: .97;
  }
}

.brt-07__dialog::backdrop {
  background: rgb(0 0 0 / .55);
  transition: opacity .17s ease, display .17s allow-discrete;
}

@starting-style {
  .brt-07__dialog[open]::backdrop {
    opacity: 0;
  }
}

.brt-07__strip {
  display: flex;
  align-items: center;
  gap: 1rem;
  justify-content: space-between;
  padding: .9rem 1rem .9rem 1.25rem;
  background: var(--strip);
  color: var(--on);
  border-block-end: var(--bw) solid var(--edge);
}

.brt-07__dh {
  margin: 0;
  font-size: 1.2rem;
  font-weight: 700;
  letter-spacing: -.015em;
}

.brt-07__x {
  display: grid;
  place-items: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  padding: 0;
  flex: none;
  background: var(--ink);
  color: var(--panel);
  border: var(--bw) solid var(--edge);
  border-radius: 9px;
  box-shadow: 3px 3px 0 var(--sh);
  cursor: pointer;
  transition: translate .13s ease, box-shadow .13s ease;
}

.brt-07__x svg {
  inline-size: 1.05rem;
  block-size: 1.05rem;
}

.brt-07__x:active {
  translate: 2px 2px;
  box-shadow: 1px 1px 0 var(--sh);
}

.brt-07__x:focus-visible {
  outline: 3px solid var(--edge);
  outline-offset: 3px;
}

.brt-07__body {
  padding: 1.25rem;
  overflow: auto;
}

.brt-07__dp {
  margin: 0 0 1rem;
  font-size: .98rem;
  line-height: 1.55;
  text-wrap: pretty;
}

.brt-07__facts {
  list-style: none;
  margin: 0 0 1.1rem;
  padding: 0;
  display: grid;
  gap: .5rem;
}

.brt-07__facts li {
  padding: .55rem .7rem;
  background: var(--page);
  border: 2px solid var(--edge);
  border-radius: 9px;
  font-size: .9rem;
}

.brt-07__confirm {
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  gap: .7rem;
  align-items: center;
  min-block-size: 2.75rem;
  font-size: .92rem;
  cursor: pointer;
}

.brt-07__box {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  inline-size: 1.6rem;
  block-size: 1.6rem;
  display: grid;
  place-items: center;
  background: var(--panel);
  cursor: pointer;
  border: var(--bw) solid var(--edge);
  border-radius: 6px;
  box-shadow: 3px 3px 0 var(--sh);
}

.brt-07__box::after {
  content: '';
  inline-size: .5rem;
  block-size: .9rem;
  border: solid var(--on);
  border-width: 0 4px 4px 0;
  rotate: 45deg;
  translate: 0 -2px;
  opacity: 0;
}

.brt-07__box:checked {
  background: var(--mint);
}

.brt-07__box:checked::after {
  opacity: 1;
}

.brt-07__box:focus-visible {
  outline: 3px solid var(--edge);
  outline-offset: 3px;
}

.brt-07__foot {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  justify-content: flex-end;
  padding: 1rem 1.25rem 1.25rem;
  border-block-start: 2px dashed var(--edge);
}

.brt-07__btn {
  min-block-size: 2.75rem;
  padding: .7rem 1.1rem;
  font: inherit;
  font-size: .98rem;
  font-weight: 700;
  cursor: pointer;
  background: var(--panel);
  color: var(--ink);
  border: var(--bw) solid var(--edge);
  border-radius: 10px;
  box-shadow: 4px 4px 0 var(--sh);
  transition: translate .13s ease, box-shadow .13s ease;
}

.brt-07__btn--dark {
  background: var(--ink);
  color: var(--page);
}

.brt-07__btn--ghost {
  background: var(--panel);
}

.brt-07__btn:hover {
  translate: -1px -1px;
  box-shadow: 6px 6px 0 var(--sh);
}

.brt-07__btn:active {
  translate: 2px 2px;
  box-shadow: 2px 2px 0 var(--sh);
}

.brt-07__btn:focus-visible {
  outline: 3px solid var(--edge);
  outline-offset: 3px;
}

.brt-07[data-theme="dark"] {
  --page: #131218;
  --panel: #201e28;
  --ink: #f7f5ef;
  --muted: #b5b1c2;
  --edge: #f7f5ef;
  --sh: #f7f5ef;
}

.brt-07[data-theme="dark"] .brt-07__moon {
  display: none;
}

.brt-07[data-theme="dark"] .brt-07__sun {
  display: block;
}

@media (prefers-color-scheme: dark) {
  .brt-07:not([data-theme="light"]) {
    --page: #131218;
    --panel: #201e28;
    --ink: #f7f5ef;
    --muted: #b5b1c2;
    --edge: #f7f5ef;
    --sh: #f7f5ef;
  }

  .brt-07:not([data-theme="light"]) .brt-07__moon {
    display: none;
  }

  .brt-07:not([data-theme="light"]) .brt-07__sun {
    display: block;
  }
}

@media (prefers-reduced-motion: reduce) {
  .brt-07 *,
    .brt-07 *::before,
    .brt-07 *::after {
    transition: none !important;
    animation: none !important;
  }
}

@media (forced-colors: active) {
  .brt-07__dialog,
    .brt-07__card,
    .brt-07__btn,
    .brt-07__x,
    .brt-07__theme {
    border-color: CanvasText;
    box-shadow: none;
  }
}
/* stage children default to min-width:auto, so a wide heading or grid
   track forces the panel past the stage; 0 lets it shrink instead */

.brt-07__stage > * {
  min-inline-size: 0;
  max-inline-size: 100%;
}
(() => {
  const root = document.querySelector('.brt-07');
  if (!root) return;
  const dlg = root.querySelector('#brt-07-dialog');
  const open = root.querySelector('#brt-07-open');
  const status = root.querySelector('#brt-07-status');

  open.addEventListener('click', () => (dlg.showModal ? dlg.showModal() : (dlg.open = true)));
  root.querySelector('#brt-07-x').addEventListener('click', () => dlg.close('dismiss'));
  root.querySelector('#brt-07-cancel').addEventListener('click', () => dlg.close('cancel'));
  root.querySelector('#brt-07-confirm').addEventListener('click', () => dlg.close('archive'));
  dlg.addEventListener('click', (e) => { if (e.target === dlg) dlg.close('dismiss'); });

  dlg.addEventListener('close', () => {
    open.focus();                                     /* focus must come back */
    const notify = root.querySelector('#brt-07-ack').checked;
    status.textContent = dlg.returnValue === 'archive'
      ? 'Northwind Studio archived' + (notify ? ' — collaborators emailed.' : '.')
      : 'Still active. Nothing changed.';
  });
})();

(() => {                                     /* theme toggle */
  const root = document.querySelector('.brt-07');
  const btn = root && root.querySelector('.brt-07__theme');
  if (!btn) return;
  const dark = () => (root.dataset.theme || (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')) === 'dark';
  const sync = () => {
    btn.setAttribute('aria-pressed', String(dark()));
    btn.setAttribute('aria-label', dark() ? 'Switch to light theme' : 'Switch to dark theme');
  };
  btn.addEventListener('click', () => { root.dataset.theme = dark() ? 'light' : 'dark'; sync(); });
  sync();
})();
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 Brutalism 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: Neubrutalist Modal Dialog
Source: https://codefronts.com/design-styles/css-brutalist/neubrutalist-modal-dialog/

A native <dialog> opened with showModal() — so the focus trap, Escape handling and inert background come from the platform, not from 200 lines of JavaScript. The block has a pastel header strip, a black close button in the corner and a flat semi-opaque ::backdrop with no blur, because blur is the one thing this aesthetic never does.
## HTML
```html
<div class="brt-07">
  <button class="brt-07__theme" type="button" aria-pressed="false" aria-label="Switch to dark theme">
    <svg class="brt-07__moon" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><path d="M20 14.2A8.2 8.2 0 0 1 9.8 4 8.4 8.4 0 1 0 20 14.2" stroke="currentColor" stroke-width="2.2" stroke-linejoin="round"/></svg>
    <svg class="brt-07__sun" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="4.2" stroke="currentColor" stroke-width="2.2"/><path d="M12 3v2.2M12 18.8V21M3 12h2.2M18.8 12H21M5.6 5.6l1.6 1.6M16.8 16.8l1.6 1.6M18.4 5.6l-1.6 1.6M7.2 16.8l-1.6 1.6" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>
  </button>

  <div class="brt-07__stage">
    <section class="brt-07__card" aria-labelledby="brt-07-h">
      <h2 class="brt-07__h" id="brt-07-h">Northwind Studio</h2>
      <p class="brt-07__meta">14 pages · last published 3 days ago</p>
      <p class="brt-07__p">Archiving hides the project from your workspace and pauses billing for its seats. You can restore it for 90 days.</p>
      <div class="brt-07__actions">
        <button class="brt-07__open" id="brt-07-open" type="button">Archive project</button>
        <a class="brt-07__link" href="#brt-07-h">View activity</a>
      </div>
      <p class="brt-07__status" id="brt-07-status" role="status" aria-live="polite"></p>
    </section>

    <dialog class="brt-07__dialog" id="brt-07-dialog" aria-labelledby="brt-07-dh" aria-describedby="brt-07-dp">
      <header class="brt-07__strip">
        <h3 class="brt-07__dh" id="brt-07-dh">Archive this project?</h3>
        <button class="brt-07__x" id="brt-07-x" type="button" aria-label="Close dialog">
          <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="3" stroke-linecap="square"/></svg>
        </button>
      </header>
      <div class="brt-07__body">
        <p class="brt-07__dp" id="brt-07-dp">Northwind Studio and its 14 pages will be hidden from the workspace. Custom domains stay reserved.</p>
        <ul class="brt-07__facts">
          <li><b>3 seats</b> released back to your plan</li>
          <li><b>90 days</b> to restore before deletion</li>
          <li><b>2 domains</b> stay pointed at a holding page</li>
        </ul>
        <label class="brt-07__confirm">
          <input class="brt-07__box" type="checkbox" id="brt-07-ack">
          <span>Tell the three collaborators by email</span>
        </label>
      </div>
      <footer class="brt-07__foot">
        <button class="brt-07__btn brt-07__btn--ghost" type="button" id="brt-07-cancel">Keep it active</button>
        <button class="brt-07__btn brt-07__btn--dark" type="button" id="brt-07-confirm" autofocus>Archive project</button>
      </footer>
    </dialog>
  </div>
</div>
```
## CSS
```css
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&display=swap');

.brt-07 {
  --page: #f8f5ed;
  --panel: #ffffff;
  --ink: #0b0b0f;
  --on: #0b0b0f;
  --muted: #4a4a52;
  --lilac: #c4b5fd;
  --mint: #86efac;
  --coral: #fda4af;
  --butter: #fde68a;
  --sky: #93c5fd;
  --strip: #c4b5fd;
  --edge: #000000;
  --sh: #000000;
  --bw: 3px;
  --r: 16px;
  --w: 34rem;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  position: relative;
  box-sizing: border-box;
  background: var(--page);
  color: var(--ink);
  font-family: 'Space Grotesk','Sora',ui-sans-serif,system-ui,sans-serif;
}

.brt-07 *,
.brt-07 *::before,
.brt-07 *::after {
  box-sizing: border-box;
}

@supports (color: oklch(0.7 0.1 300)) {
  .brt-07 {
    --page: oklch(0.97 0.016 90);
    --panel: oklch(1 0 0);
    --ink: oklch(0.16 0.01 285);
    --lilac: oklch(0.82 0.11 296);
    --mint: oklch(0.89 0.13 155);
    --coral: oklch(0.83 0.10 15);
    --butter: oklch(0.92 0.11 95);
    --sky: oklch(0.83 0.09 250);
    --strip: oklch(0.82 0.11 296);
  }
}

.brt-07__theme {
  position: absolute;
  inset-block-start: 1rem;
  inset-inline-end: 1rem;
  z-index: 5;
  inline-size: 2.5rem;
  block-size: 2.5rem;
  display: grid;
  place-items: center;
  padding: 0;
  background: var(--panel);
  color: var(--ink);
  border: var(--bw) solid var(--edge);
  border-radius: 10px;
  box-shadow: 4px 4px 0 var(--sh);
  cursor: pointer;
  transition: translate .14s ease, box-shadow .14s ease;
}

.brt-07__theme::after {
  content: '';
  position: absolute;
  inset: -2px;
}

.brt-07__theme svg {
  inline-size: 1.25rem;
  block-size: 1.25rem;
}

.brt-07__theme:active {
  translate: 2px 2px;
  box-shadow: 2px 2px 0 var(--sh);
}

.brt-07__theme:focus-visible {
  outline: 3px solid var(--edge);
  outline-offset: 3px;
}

.brt-07__sun {
  display: none;
}

.brt-07__stage {
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: clamp(1.25rem,5vw,3rem);
}

.brt-07__card {
  inline-size: min(30rem,100%);
  padding: clamp(1.4rem,3.5vw,2rem);
  background: var(--panel);
  border: var(--bw) solid var(--edge);
  border-radius: var(--r);
  box-shadow: 8px 8px 0 var(--sh);
}

.brt-07__h {
  margin: 0;
  font-size: clamp(1.5rem,4vw,2rem);
  font-weight: 700;
  letter-spacing: -.025em;
}

.brt-07__meta {
  margin: .35rem 0 0;
  font-size: .8rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}

.brt-07__p {
  margin: 1rem 0 1.5rem;
  font-size: .98rem;
  line-height: 1.55;
  color: var(--ink);
  text-wrap: pretty;
}

.brt-07__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
}

.brt-07__open {
  min-block-size: 2.9rem;
  padding: .75rem 1.15rem;
  font: inherit;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  background: var(--coral);
  color: var(--on);
  border: var(--bw) solid var(--edge);
  border-radius: 11px;
  box-shadow: 5px 5px 0 var(--sh);
  transition: translate .14s ease, box-shadow .14s ease;
}

.brt-07__open:hover {
  translate: -1px -1px;
  box-shadow: 7px 7px 0 var(--sh);
}

.brt-07__open:active {
  translate: 2px 2px;
  box-shadow: 2px 2px 0 var(--sh);
}

.brt-07__open:focus-visible {
  outline: 3px solid var(--edge);
  outline-offset: 3px;
}

.brt-07__link {
  display: inline-flex;
  align-items: center;
  min-block-size: 2.75rem;
  color: var(--ink);
  font-weight: 700;
  font-size: .95rem;
  text-decoration: 3px underline var(--sky);
  text-underline-offset: 4px;
}

.brt-07__link:hover {
  background: var(--sky);
  color: var(--on);
}

.brt-07__link:focus-visible {
  outline: 3px solid var(--edge);
  outline-offset: 2px;
}

.brt-07__status {
  margin: 1.1rem 0 0;
  min-block-size: 1.2rem;
  font-size: .88rem;
  font-weight: 700;
}

.brt-07__status:not(:empty) {
  display: inline-block;
  padding: .4rem .6rem;
  background: var(--mint);
  color: var(--on);
  border: 2px solid var(--edge);
  border-radius: 8px;
}

.brt-07__dialog {
  inline-size: min(var(--w),calc(100% - 2rem));
  max-block-size: min(90svh,42rem);
  padding: 0;
  margin: auto;
  background: var(--panel);
  color: var(--ink);
  overflow: hidden;
  border: var(--bw) solid var(--edge);
  border-radius: var(--r);
  box-shadow: 12px 12px 0 var(--sh);
}

@supports selector(dialog: modal) {
  .brt-07__dialog {
    opacity: 1;
    translate: 0 0;
    scale: 1;
    transition: opacity .17s ease, translate .17s ease, scale .17s ease, display .17s allow-discrete, overlay .17s allow-discrete;
  }

  @starting-style {
    .brt-07__dialog[open] {
      opacity: 0;
      translate: 0 -12px;
      scale: .97;
    }
  }

  .brt-07__dialog:not([open]) {
    opacity: 0;
    translate: 0 -12px;
    scale: .97;
  }
}

.brt-07__dialog::backdrop {
  background: rgb(0 0 0 / .55);
  transition: opacity .17s ease, display .17s allow-discrete;
}

@starting-style {
  .brt-07__dialog[open]::backdrop {
    opacity: 0;
  }
}

.brt-07__strip {
  display: flex;
  align-items: center;
  gap: 1rem;
  justify-content: space-between;
  padding: .9rem 1rem .9rem 1.25rem;
  background: var(--strip);
  color: var(--on);
  border-block-end: var(--bw) solid var(--edge);
}

.brt-07__dh {
  margin: 0;
  font-size: 1.2rem;
  font-weight: 700;
  letter-spacing: -.015em;
}

.brt-07__x {
  display: grid;
  place-items: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  padding: 0;
  flex: none;
  background: var(--ink);
  color: var(--panel);
  border: var(--bw) solid var(--edge);
  border-radius: 9px;
  box-shadow: 3px 3px 0 var(--sh);
  cursor: pointer;
  transition: translate .13s ease, box-shadow .13s ease;
}

.brt-07__x svg {
  inline-size: 1.05rem;
  block-size: 1.05rem;
}

.brt-07__x:active {
  translate: 2px 2px;
  box-shadow: 1px 1px 0 var(--sh);
}

.brt-07__x:focus-visible {
  outline: 3px solid var(--edge);
  outline-offset: 3px;
}

.brt-07__body {
  padding: 1.25rem;
  overflow: auto;
}

.brt-07__dp {
  margin: 0 0 1rem;
  font-size: .98rem;
  line-height: 1.55;
  text-wrap: pretty;
}

.brt-07__facts {
  list-style: none;
  margin: 0 0 1.1rem;
  padding: 0;
  display: grid;
  gap: .5rem;
}

.brt-07__facts li {
  padding: .55rem .7rem;
  background: var(--page);
  border: 2px solid var(--edge);
  border-radius: 9px;
  font-size: .9rem;
}

.brt-07__confirm {
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  gap: .7rem;
  align-items: center;
  min-block-size: 2.75rem;
  font-size: .92rem;
  cursor: pointer;
}

.brt-07__box {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  inline-size: 1.6rem;
  block-size: 1.6rem;
  display: grid;
  place-items: center;
  background: var(--panel);
  cursor: pointer;
  border: var(--bw) solid var(--edge);
  border-radius: 6px;
  box-shadow: 3px 3px 0 var(--sh);
}

.brt-07__box::after {
  content: '';
  inline-size: .5rem;
  block-size: .9rem;
  border: solid var(--on);
  border-width: 0 4px 4px 0;
  rotate: 45deg;
  translate: 0 -2px;
  opacity: 0;
}

.brt-07__box:checked {
  background: var(--mint);
}

.brt-07__box:checked::after {
  opacity: 1;
}

.brt-07__box:focus-visible {
  outline: 3px solid var(--edge);
  outline-offset: 3px;
}

.brt-07__foot {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  justify-content: flex-end;
  padding: 1rem 1.25rem 1.25rem;
  border-block-start: 2px dashed var(--edge);
}

.brt-07__btn {
  min-block-size: 2.75rem;
  padding: .7rem 1.1rem;
  font: inherit;
  font-size: .98rem;
  font-weight: 700;
  cursor: pointer;
  background: var(--panel);
  color: var(--ink);
  border: var(--bw) solid var(--edge);
  border-radius: 10px;
  box-shadow: 4px 4px 0 var(--sh);
  transition: translate .13s ease, box-shadow .13s ease;
}

.brt-07__btn--dark {
  background: var(--ink);
  color: var(--page);
}

.brt-07__btn--ghost {
  background: var(--panel);
}

.brt-07__btn:hover {
  translate: -1px -1px;
  box-shadow: 6px 6px 0 var(--sh);
}

.brt-07__btn:active {
  translate: 2px 2px;
  box-shadow: 2px 2px 0 var(--sh);
}

.brt-07__btn:focus-visible {
  outline: 3px solid var(--edge);
  outline-offset: 3px;
}

.brt-07[data-theme="dark"] {
  --page: #131218;
  --panel: #201e28;
  --ink: #f7f5ef;
  --muted: #b5b1c2;
  --edge: #f7f5ef;
  --sh: #f7f5ef;
}

.brt-07[data-theme="dark"] .brt-07__moon {
  display: none;
}

.brt-07[data-theme="dark"] .brt-07__sun {
  display: block;
}

@media (prefers-color-scheme: dark) {
  .brt-07:not([data-theme="light"]) {
    --page: #131218;
    --panel: #201e28;
    --ink: #f7f5ef;
    --muted: #b5b1c2;
    --edge: #f7f5ef;
    --sh: #f7f5ef;
  }

  .brt-07:not([data-theme="light"]) .brt-07__moon {
    display: none;
  }

  .brt-07:not([data-theme="light"]) .brt-07__sun {
    display: block;
  }
}

@media (prefers-reduced-motion: reduce) {
  .brt-07 *,
    .brt-07 *::before,
    .brt-07 *::after {
    transition: none !important;
    animation: none !important;
  }
}

@media (forced-colors: active) {
  .brt-07__dialog,
    .brt-07__card,
    .brt-07__btn,
    .brt-07__x,
    .brt-07__theme {
    border-color: CanvasText;
    box-shadow: none;
  }
}
/* stage children default to min-width:auto, so a wide heading or grid
   track forces the panel past the stage; 0 lets it shrink instead */

.brt-07__stage > * {
  min-inline-size: 0;
  max-inline-size: 100%;
}
```

## JavaScript
```js
(() => {
  const root = document.querySelector('.brt-07');
  if (!root) return;
  const dlg = root.querySelector('#brt-07-dialog');
  const open = root.querySelector('#brt-07-open');
  const status = root.querySelector('#brt-07-status');

  open.addEventListener('click', () => (dlg.showModal ? dlg.showModal() : (dlg.open = true)));
  root.querySelector('#brt-07-x').addEventListener('click', () => dlg.close('dismiss'));
  root.querySelector('#brt-07-cancel').addEventListener('click', () => dlg.close('cancel'));
  root.querySelector('#brt-07-confirm').addEventListener('click', () => dlg.close('archive'));
  dlg.addEventListener('click', (e) => { if (e.target === dlg) dlg.close('dismiss'); });

  dlg.addEventListener('close', () => {
    open.focus();                                     /* focus must come back */
    const notify = root.querySelector('#brt-07-ack').checked;
    status.textContent = dlg.returnValue === 'archive'
      ? 'Northwind Studio archived' + (notify ? ' — collaborators emailed.' : '.')
      : 'Still active. Nothing changed.';
  });
})();

(() => {                                     /* theme toggle */
  const root = document.querySelector('.brt-07');
  const btn = root && root.querySelector('.brt-07__theme');
  if (!btn) return;
  const dark = () => (root.dataset.theme || (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')) === 'dark';
  const sync = () => {
    btn.setAttribute('aria-pressed', String(dark()));
    btn.setAttribute('aria-label', dark() ? 'Switch to light theme' : 'Switch to dark theme');
  };
  btn.addEventListener('click', () => { root.dataset.theme = dark() ? 'light' : 'dark'; sync(); });
  sync();
})();
```

How this works

Let the platform do the hard part. showModal() gives you a focus trap, Escape-to-close, the top layer and an inert background for free:

open.addEventListener('click', () => dlg.showModal());
dlg.addEventListener('close', () => {
  open.focus();                                  /* return focus — required */
  status.textContent = dlg.returnValue === 'archive' ? 'Project archived.' : 'Nothing changed.';
});

Animating the top layer. A dialog toggles display, so the transition needs allow-discrete, and @starting-style provides the from-frame. The backdrop is a separate box and animates on its own:

.brt-07__dialog{ opacity:1; translate:0 0; scale:1;
  transition:opacity .16s, translate .16s, scale .16s, display .16s allow-discrete, overlay .16s allow-discrete; }
@starting-style{ .brt-07__dialog[open]{ opacity:0; translate:0 -10px; scale:.97; } }
.brt-07__dialog::backdrop{ background:rgb(0 0 0 / .55); }   /* flat — no blur */

Light dismiss, correctly. The dialog element's box is only the panel, and clicks on the backdrop are reported with the dialog as the target — so a single identity check is enough, no wrapper div and no coordinate maths:

dlg.addEventListener('click', (e) => { if (e.target === dlg) dlg.close('dismiss'); });

Make it yours

  • Change the header strip colour with --strip; keep the body white so long copy stays comfortable.
  • Make the dialog wide for content-heavy flows by raising --w; it is a min() so small screens are unaffected.
  • For a drawer instead of a centre modal, set margin-inline-start:auto, full block size and border-radius: 16px 0 0 16px — the same shadow reads as a panel edge.
  • Swap light-dismiss off by removing the click handler when the flow is destructive and needs an explicit choice.
  • The backdrop opacity is one value (.55). Below about .4 the modal stops feeling modal; above .7 the page context is lost.

Gotchas — read before shipping

  • <dialog open> in markup is not modal: no focus trap, no backdrop, no top layer. Always open it with showModal().
  • ::backdrop does not inherit from the page, so custom properties defined on your root are not visible inside it. Declare its colour literally, or define the property on :root.
  • Never backdrop-filter: blur() here — the collection's whole premise is hard, unblurred depth. It also costs a full-screen composite every frame.
  • Without overlay .16s allow-discrete in the transition list, the exit animation is skipped because the element leaves the top layer immediately.
  • Returning focus is your job, not the platform's — if you skip it, keyboard users land back at the top of the document.
  • Don't put autofocus on the destructive action. Focus the safe primary, or the dialog itself if there is no obvious default.

Browser support

ChromeSafariFirefoxEdge
117+17.5+129+117+

<dialog> and showModal() are Chrome 37 / Safari 15.4 / Firefox 98. The entry/exit animation needs @starting-style and allow-discrete (Chrome 117 / Safari 17.5 / Firefox 129) and degrades to an instant open. @supports selector(dialog:modal) hides the animation-only rules from browsers that would misapply them.

Techniques used in this demo

Search CodeFronts

Loading…