36 CSS Modals16 / 36

Native <dialog>MIT licensed

Detailed Contact / Query Form

A multi-field support form that opens over the current dashboard view so users file a query without losing context — name, email, topic, priority and message, with inline validation and a success state. Keeps people in flow.

Published

Live Demo
Try it

The code

<div class="cm-16">
  <button class="cm-16__open" type="button">Contact support</button>

  <dialog class="cm-16__dlg" aria-labelledby="cm-16-title">
    <div class="cm-16__card">
      <header class="cm-16__head">
        <div><h2 id="cm-16-title">Contact support</h2><p class="cm-16__sub">We usually reply within a few hours.</p></div>
        <button class="cm-16__x" type="button" aria-label="Close">&times;</button>
      </header>

      <form class="cm-16__form">
        <div class="cm-16__row">
          <div class="cm-16__field"><label for="cm-16-name">Name</label>
            <input id="cm-16-name" type="text" required autocomplete="name"></div>
          <div class="cm-16__field"><label for="cm-16-email">Email</label>
            <input id="cm-16-email" type="email" required autocomplete="email"></div>
        </div>
        <div class="cm-16__field"><label for="cm-16-topic">Topic</label>
          <select id="cm-16-topic" required>
            <option value="">Choose a topic…</option>
            <option>Billing &amp; plans</option><option>Technical issue</option>
            <option>Feature request</option><option>Something else</option>
          </select>
        </div>
        <fieldset class="cm-16__field cm-16__prio"><legend>Priority</legend>
          <label><input type="radio" name="cm-16-p" checked> Low</label>
          <label><input type="radio" name="cm-16-p"> Normal</label>
          <label><input type="radio" name="cm-16-p"> Urgent</label>
        </fieldset>
        <div class="cm-16__field"><label for="cm-16-msg">Message</label>
          <textarea id="cm-16-msg" required maxlength="500" rows="4" placeholder="Describe what's happening…"></textarea>
          <span class="cm-16__count"><b id="cm-16-c">0</b>/500</span>
        </div>
        <div class="cm-16__actions">
          <button type="button" class="cm-16__cancel">Cancel</button>
          <button type="submit" class="cm-16__submit">Send message</button>
        </div>
      </form>

      <div class="cm-16__done" hidden role="status">
        <div class="cm-16__tick" aria-hidden="true">✓</div>
        <h3>Message sent</h3>
        <p>Thanks — we've logged your request and emailed a copy to your inbox.</p>
        <button type="button" class="cm-16__close2">Close</button>
      </div>
    </div>
  </dialog>
</div>
.cm-16,
.cm-16 *,
.cm-16 *::before,
.cm-16 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cm-16 {
  --accent: oklch(0.7 0.15 260);
  font-family: system-ui,'Segoe UI',sans-serif;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 32px;
  background: radial-gradient(120% 120% at 50% 0%,#0e0f18,#08080d);
}

.cm-16__open {
  cursor: pointer;
  padding: 13px 26px;
  border-radius: 11px;
  color: #e8e9fb;
  font: 600 15px system-ui;
  border: 1px solid color-mix(in oklch,var(--accent) 45%,#2a2c42);
  background: color-mix(in oklch,var(--accent) 14%,#0e0f18);
  transition: background .2s,border-color .2s;
}

.cm-16__open:hover {
  border-color: var(--accent);
  background: color-mix(in oklch,var(--accent) 22%,#0e0f18);
}

.cm-16__open:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.cm-16__dlg {
  margin: auto;
  width: min(500px,94vw);
  padding: 0;
  border: none;
  border-radius: 18px;
  color: #e7e9f4;
  background: linear-gradient(180deg,#15161f,#0f1017);
  box-shadow: 0 40px 90px -30px #000,0 0 0 1px #262838;
  max-block-size: 92dvh;
  overflow: auto;
  opacity: 0;
  transform: translateY(10px) scale(.98);
  transition: opacity .26s,transform .26s,overlay .26s allow-discrete,display .26s allow-discrete;
}

.cm-16__dlg[open] {
  opacity: 1;
  transform: none;
}

@starting-style {
  .cm-16__dlg[open] {
    opacity: 0;
    transform: translateY(10px) scale(.98);
  }
}

.cm-16__dlg::backdrop {
  background: rgba(6,6,10,0);
  backdrop-filter: blur(0);
  transition: background .3s,backdrop-filter .3s,overlay .3s allow-discrete,display .3s allow-discrete;
}

.cm-16__dlg[open]::backdrop {
  background: rgba(6,6,10,.62);
  backdrop-filter: blur(5px);
}

@starting-style {
  .cm-16__dlg[open]::backdrop {
    background: rgba(6,6,10,0);
    backdrop-filter: blur(0);
  }
}

.cm-16__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  padding: 26px 28px 8px;
}

.cm-16__head h2 {
  font-size: 20px;
  font-weight: 700;
  color: #f2f3fb;
}

.cm-16__sub {
  font-size: 13px;
  color: #8f92a8;
  margin-top: 4px;
}

.cm-16__x {
  width: 34px;
  height: 34px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: #888ba0;
  font-size: 22px;
  cursor: pointer;
  flex: none;
}

.cm-16__x:hover {
  background: #1e2030;
  color: #fff;
}

.cm-16__x:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.cm-16__form {
  padding: 16px 28px 26px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.cm-16__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}

.cm-16__field {
  display: flex;
  flex-direction: column;
  gap: 7px;
  position: relative;
}

.cm-16__field label,
.cm-16__field legend {
  font-size: 12.5px;
  font-weight: 600;
  color: #aab0c4;
}

.cm-16__field input,
.cm-16__field select,
.cm-16__field textarea {
  width: 100%;
  padding: 11px 13px;
  border-radius: 10px;
  border: 1px solid #2b2e42;
  background: #0c0d14;
  color: #eef0f7;
  font: 14px system-ui;
  transition: border-color .2s,box-shadow .2s;
}

.cm-16__field textarea {
  resize: vertical;
  min-height: 92px;
}

.cm-16__field input:focus,
.cm-16__field select:focus,
.cm-16__field textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklch,var(--accent) 26%,transparent);
}

.cm-16__field input:user-invalid,
.cm-16__field textarea:user-invalid,
.cm-16__field select:user-invalid {
  border-color: oklch(0.62 0.2 22);
}

.cm-16__prio {
  border: none;
}

.cm-16__prio {
  flex-direction: row;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}

.cm-16__prio legend {
  flex-basis: 100%;
}

.cm-16__prio label {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 14px;
  color: #c6cbdb;
  cursor: pointer;
  font-weight: 400;
}

.cm-16__prio input {
  accent-color: var(--accent);
  width: 16px;
  height: 16px;
}

.cm-16__count {
  position: absolute;
  right: 2px;
  bottom: -20px;
  font-size: 11.5px;
  color: #787c92;
}

.cm-16__count b {
  color: #adb2c5;
}

.cm-16__actions {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 14px;
}

.cm-16__cancel {
  padding: 12px 20px;
  border-radius: 10px;
  border: 1px solid #2f3247;
  background: #171826;
  color: #d6dae8;
  font: 600 14px system-ui;
  cursor: pointer;
  transition: all .18s;
}

.cm-16__cancel:hover {
  background: #1e2032;
}

.cm-16__submit {
  padding: 12px 22px;
  border: none;
  border-radius: 10px;
  background: var(--accent);
  color: #080615;
  font: 700 14px system-ui;
  cursor: pointer;
  transition: filter .2s,transform .1s;
}

.cm-16__submit:hover {
  filter: brightness(1.08);
}

.cm-16__submit:active {
  transform: translateY(1px);
}

.cm-16__cancel:focus-visible,
.cm-16__submit:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.cm-16__done {
  padding: 44px 30px;
  text-align: center;
}

.cm-16__tick {
  width: 60px;
  height: 60px;
  margin: 0 auto 18px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 28px;
  color: #0a1410;
  background: oklch(0.82 0.16 155);
  box-shadow: 0 0 44px -8px oklch(0.82 0.16 155);
  animation: cm-16-pop .4s cubic-bezier(.2,1.4,.4,1) both;
}

@keyframes cm-16-pop {
  from {
    transform: scale(.3);
    opacity: 0;
  }

  to {
    transform: scale(1);
    opacity: 1;
  }
}

.cm-16__done h3 {
  font-size: 20px;
  font-weight: 700;
  color: #f2f3fb;
  margin-bottom: 8px;
}

.cm-16__done p {
  font-size: 14px;
  line-height: 1.6;
  color: #9599ad;
  max-width: 32ch;
  margin: 0 auto 22px;
}

.cm-16__close2 {
  padding: 12px 28px;
  border: none;
  border-radius: 10px;
  background: var(--accent);
  color: #080615;
  font: 700 14px system-ui;
  cursor: pointer;
}

.cm-16__close2:hover {
  filter: brightness(1.08);
}

.cm-16__close2:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

@media (max-width: 480px) {
  .cm-16__row {
    grid-template-columns: 1fr;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cm-16__dlg,
    .cm-16__dlg::backdrop {
    transition-duration: .001s;
  }

  .cm-16__tick {
    animation: none;
  }
}
(function(){
  var root = document.querySelector('.cm-16');
  var dlg = root.querySelector('.cm-16__dlg');
  var form = root.querySelector('.cm-16__form');
  var done = root.querySelector('.cm-16__done');
  var msg = root.querySelector('#cm-16-msg');
  var count = root.querySelector('#cm-16-c');
  function reset(){ form.hidden = false; done.hidden = true; }
  root.querySelector('.cm-16__open').addEventListener('click', function(){ reset(); dlg.showModal(); });
  root.querySelector('.cm-16__x').addEventListener('click', function(){ dlg.close(); });
  root.querySelector('.cm-16__cancel').addEventListener('click', function(){ dlg.close(); });
  root.querySelector('.cm-16__close2').addEventListener('click', function(){ dlg.close(); });
  msg.addEventListener('input', function(){ count.textContent = msg.value.length; });
  form.addEventListener('submit', function(e){
    e.preventDefault();
    if(!form.checkValidity()){ form.reportValidity(); return; }
    form.hidden = true; done.hidden = false;
    root.querySelector('.cm-16__close2').focus();
  });
  dlg.addEventListener('close', reset);
})();
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 Modal 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: Detailed Contact / Query Form
Source: https://codefronts.com/components/css-modals/detailed-contact-query-form/

A multi-field support form that opens over the current dashboard view so users file a query without losing context — name, email, topic, priority and message, with inline validation and a success state. Keeps people in flow.
## HTML
```html
<div class="cm-16">
  <button class="cm-16__open" type="button">Contact support</button>

  <dialog class="cm-16__dlg" aria-labelledby="cm-16-title">
    <div class="cm-16__card">
      <header class="cm-16__head">
        <div><h2 id="cm-16-title">Contact support</h2><p class="cm-16__sub">We usually reply within a few hours.</p></div>
        <button class="cm-16__x" type="button" aria-label="Close">&times;</button>
      </header>

      <form class="cm-16__form">
        <div class="cm-16__row">
          <div class="cm-16__field"><label for="cm-16-name">Name</label>
            <input id="cm-16-name" type="text" required autocomplete="name"></div>
          <div class="cm-16__field"><label for="cm-16-email">Email</label>
            <input id="cm-16-email" type="email" required autocomplete="email"></div>
        </div>
        <div class="cm-16__field"><label for="cm-16-topic">Topic</label>
          <select id="cm-16-topic" required>
            <option value="">Choose a topic…</option>
            <option>Billing &amp; plans</option><option>Technical issue</option>
            <option>Feature request</option><option>Something else</option>
          </select>
        </div>
        <fieldset class="cm-16__field cm-16__prio"><legend>Priority</legend>
          <label><input type="radio" name="cm-16-p" checked> Low</label>
          <label><input type="radio" name="cm-16-p"> Normal</label>
          <label><input type="radio" name="cm-16-p"> Urgent</label>
        </fieldset>
        <div class="cm-16__field"><label for="cm-16-msg">Message</label>
          <textarea id="cm-16-msg" required maxlength="500" rows="4" placeholder="Describe what's happening…"></textarea>
          <span class="cm-16__count"><b id="cm-16-c">0</b>/500</span>
        </div>
        <div class="cm-16__actions">
          <button type="button" class="cm-16__cancel">Cancel</button>
          <button type="submit" class="cm-16__submit">Send message</button>
        </div>
      </form>

      <div class="cm-16__done" hidden role="status">
        <div class="cm-16__tick" aria-hidden="true">✓</div>
        <h3>Message sent</h3>
        <p>Thanks — we've logged your request and emailed a copy to your inbox.</p>
        <button type="button" class="cm-16__close2">Close</button>
      </div>
    </div>
  </dialog>
</div>
```
## CSS
```css
.cm-16,
.cm-16 *,
.cm-16 *::before,
.cm-16 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cm-16 {
  --accent: oklch(0.7 0.15 260);
  font-family: system-ui,'Segoe UI',sans-serif;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 32px;
  background: radial-gradient(120% 120% at 50% 0%,#0e0f18,#08080d);
}

.cm-16__open {
  cursor: pointer;
  padding: 13px 26px;
  border-radius: 11px;
  color: #e8e9fb;
  font: 600 15px system-ui;
  border: 1px solid color-mix(in oklch,var(--accent) 45%,#2a2c42);
  background: color-mix(in oklch,var(--accent) 14%,#0e0f18);
  transition: background .2s,border-color .2s;
}

.cm-16__open:hover {
  border-color: var(--accent);
  background: color-mix(in oklch,var(--accent) 22%,#0e0f18);
}

.cm-16__open:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.cm-16__dlg {
  margin: auto;
  width: min(500px,94vw);
  padding: 0;
  border: none;
  border-radius: 18px;
  color: #e7e9f4;
  background: linear-gradient(180deg,#15161f,#0f1017);
  box-shadow: 0 40px 90px -30px #000,0 0 0 1px #262838;
  max-block-size: 92dvh;
  overflow: auto;
  opacity: 0;
  transform: translateY(10px) scale(.98);
  transition: opacity .26s,transform .26s,overlay .26s allow-discrete,display .26s allow-discrete;
}

.cm-16__dlg[open] {
  opacity: 1;
  transform: none;
}

@starting-style {
  .cm-16__dlg[open] {
    opacity: 0;
    transform: translateY(10px) scale(.98);
  }
}

.cm-16__dlg::backdrop {
  background: rgba(6,6,10,0);
  backdrop-filter: blur(0);
  transition: background .3s,backdrop-filter .3s,overlay .3s allow-discrete,display .3s allow-discrete;
}

.cm-16__dlg[open]::backdrop {
  background: rgba(6,6,10,.62);
  backdrop-filter: blur(5px);
}

@starting-style {
  .cm-16__dlg[open]::backdrop {
    background: rgba(6,6,10,0);
    backdrop-filter: blur(0);
  }
}

.cm-16__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  padding: 26px 28px 8px;
}

.cm-16__head h2 {
  font-size: 20px;
  font-weight: 700;
  color: #f2f3fb;
}

.cm-16__sub {
  font-size: 13px;
  color: #8f92a8;
  margin-top: 4px;
}

.cm-16__x {
  width: 34px;
  height: 34px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: #888ba0;
  font-size: 22px;
  cursor: pointer;
  flex: none;
}

.cm-16__x:hover {
  background: #1e2030;
  color: #fff;
}

.cm-16__x:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.cm-16__form {
  padding: 16px 28px 26px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.cm-16__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}

.cm-16__field {
  display: flex;
  flex-direction: column;
  gap: 7px;
  position: relative;
}

.cm-16__field label,
.cm-16__field legend {
  font-size: 12.5px;
  font-weight: 600;
  color: #aab0c4;
}

.cm-16__field input,
.cm-16__field select,
.cm-16__field textarea {
  width: 100%;
  padding: 11px 13px;
  border-radius: 10px;
  border: 1px solid #2b2e42;
  background: #0c0d14;
  color: #eef0f7;
  font: 14px system-ui;
  transition: border-color .2s,box-shadow .2s;
}

.cm-16__field textarea {
  resize: vertical;
  min-height: 92px;
}

.cm-16__field input:focus,
.cm-16__field select:focus,
.cm-16__field textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklch,var(--accent) 26%,transparent);
}

.cm-16__field input:user-invalid,
.cm-16__field textarea:user-invalid,
.cm-16__field select:user-invalid {
  border-color: oklch(0.62 0.2 22);
}

.cm-16__prio {
  border: none;
}

.cm-16__prio {
  flex-direction: row;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}

.cm-16__prio legend {
  flex-basis: 100%;
}

.cm-16__prio label {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 14px;
  color: #c6cbdb;
  cursor: pointer;
  font-weight: 400;
}

.cm-16__prio input {
  accent-color: var(--accent);
  width: 16px;
  height: 16px;
}

.cm-16__count {
  position: absolute;
  right: 2px;
  bottom: -20px;
  font-size: 11.5px;
  color: #787c92;
}

.cm-16__count b {
  color: #adb2c5;
}

.cm-16__actions {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 14px;
}

.cm-16__cancel {
  padding: 12px 20px;
  border-radius: 10px;
  border: 1px solid #2f3247;
  background: #171826;
  color: #d6dae8;
  font: 600 14px system-ui;
  cursor: pointer;
  transition: all .18s;
}

.cm-16__cancel:hover {
  background: #1e2032;
}

.cm-16__submit {
  padding: 12px 22px;
  border: none;
  border-radius: 10px;
  background: var(--accent);
  color: #080615;
  font: 700 14px system-ui;
  cursor: pointer;
  transition: filter .2s,transform .1s;
}

.cm-16__submit:hover {
  filter: brightness(1.08);
}

.cm-16__submit:active {
  transform: translateY(1px);
}

.cm-16__cancel:focus-visible,
.cm-16__submit:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.cm-16__done {
  padding: 44px 30px;
  text-align: center;
}

.cm-16__tick {
  width: 60px;
  height: 60px;
  margin: 0 auto 18px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 28px;
  color: #0a1410;
  background: oklch(0.82 0.16 155);
  box-shadow: 0 0 44px -8px oklch(0.82 0.16 155);
  animation: cm-16-pop .4s cubic-bezier(.2,1.4,.4,1) both;
}

@keyframes cm-16-pop {
  from {
    transform: scale(.3);
    opacity: 0;
  }

  to {
    transform: scale(1);
    opacity: 1;
  }
}

.cm-16__done h3 {
  font-size: 20px;
  font-weight: 700;
  color: #f2f3fb;
  margin-bottom: 8px;
}

.cm-16__done p {
  font-size: 14px;
  line-height: 1.6;
  color: #9599ad;
  max-width: 32ch;
  margin: 0 auto 22px;
}

.cm-16__close2 {
  padding: 12px 28px;
  border: none;
  border-radius: 10px;
  background: var(--accent);
  color: #080615;
  font: 700 14px system-ui;
  cursor: pointer;
}

.cm-16__close2:hover {
  filter: brightness(1.08);
}

.cm-16__close2:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

@media (max-width: 480px) {
  .cm-16__row {
    grid-template-columns: 1fr;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cm-16__dlg,
    .cm-16__dlg::backdrop {
    transition-duration: .001s;
  }

  .cm-16__tick {
    animation: none;
  }
}
```

## JavaScript
```js
(function(){
  var root = document.querySelector('.cm-16');
  var dlg = root.querySelector('.cm-16__dlg');
  var form = root.querySelector('.cm-16__form');
  var done = root.querySelector('.cm-16__done');
  var msg = root.querySelector('#cm-16-msg');
  var count = root.querySelector('#cm-16-c');
  function reset(){ form.hidden = false; done.hidden = true; }
  root.querySelector('.cm-16__open').addEventListener('click', function(){ reset(); dlg.showModal(); });
  root.querySelector('.cm-16__x').addEventListener('click', function(){ dlg.close(); });
  root.querySelector('.cm-16__cancel').addEventListener('click', function(){ dlg.close(); });
  root.querySelector('.cm-16__close2').addEventListener('click', function(){ dlg.close(); });
  msg.addEventListener('input', function(){ count.textContent = msg.value.length; });
  form.addEventListener('submit', function(e){
    e.preventDefault();
    if(!form.checkValidity()){ form.reportValidity(); return; }
    form.hidden = true; done.hidden = false;
    root.querySelector('.cm-16__close2').focus();
  });
  dlg.addEventListener('close', reset);
})();
```

How this works

A native <dialog> houses a real <form> with grouped fields: labelled inputs, a topic <select>, priority radios and a message <textarea> with a live character count. Browser constraint validation handles required fields; on valid submit, JS swaps the form for a confirmation panel — the ticket is filed without a page change.

The dialog keeps the dashboard behind it inert but visible, so users keep their place; the same @starting-style/::backdrop pattern animates it in.

Make it yours

  • Add file-attachment or screenshot fields for richer support tickets.
  • Prefill name/email from the signed-in user and hide those fields.
  • Route by the topic <select> value to the right queue on submit.
  • Retheme via --accent on .cm-16.

Gotchas — read before shipping

  • Use real <label for> associations and let native validation fire before your JS submit — don't reinvent required-field checking.
  • Give the textarea a sensible min-height and a visible character counter so long messages don't feel cramped.
  • Preserve entered values if submission fails; never clear the form on error.

Browser support

ChromeSafariFirefoxEdge
37+ · 117+ anim15.4+ · 17.5+ anim98+ · 129+ anim37+ · 117+ anim

Dialog + native form validation are universal; animation degrades gracefully.

Techniques used in this demo

Search CodeFronts

Loading…