Forms and controls
Dialog
A native modal for a short edit form or a confirm that keeps the member on the page.
- Block
.hs-dialog- CSS
components/dialog.css(in/brand.css)- JS
components/dialog.js: load it only on a page with this component- Status
- Decided
- Decided in
- Components round 2b
- Markdown
- /components/dialog.md
What it is
A native <dialog> opened with showModal(). Sizes: --sm 440px, default 560px, --lg 720px. Didot title, 40px round close button; the primary button first, then the secondary pill.
When to use
- A short edit form that keeps the member on the page, such as “Edit academic record” on the profile. Use the default 560px width.
- A confirm before the member loses work, such as “Leave without saving?”. Use
--sm. - A long form or a photo editor. Use
--lg.
When not to use
- Body HTML for
hs_html_pagecarries no<script>, so nothing can open the dialog. Put the content in the page. - For a message after an action, use a Callout on the page.
- For legal or participation terms, use the Disclosure in its own band.
- For a video, use the Video facade. It plays in place.
Rules
- Markup:
<dialog class="hs-dialog" id="ID" aria-labelledby="TITLE-ID">holds.hs-dialog__header,.hs-dialog__bodyand.hs-dialog__footer. For a form, wrap the body and the footer inform.hs-dialog__form. - The header holds an
h2.hs-dialog__titleand abutton.hs-dialog__closewithdata-hs-dialog-close. - Open it from
<button type="button" data-hs-dialog-open="ID">. Every other closer carriesdata-hs-dialog-close. Loaddialog.js. - In the footer, put the primary
.hs-button.hs-button--smfirst, then a.hs-button--secondary.hs-button--smsuch as “Cancel”. Below 30rem the two stack full width, the primary on top. - Use the secondary pill only for the second action. A dialog has one primary button.
Accessibility
showModal()traps focus, closes on Escape and returns focus to the opener.dialog.jsalso closes the dialog on a backdrop click.aria-labelledbypoints at the title. The close button carriesaria-label="Close", and its icon carriesaria-hidden="true" focusable="false".- The title is navy, 12.4:1 on white.
- Without
dialog.jsthe opener does nothing. The no-JS page must still give every answer, so the task needs a second route. - The fade and the 8px rise run only under
prefers-reduced-motion: no-preference.
Variants
Dialog
Try the three buttons. Needs dialog.js.
Preview on white
<button class="hs-button hs-button--secondary hs-button--sm" type="button" data-hs-dialog-open="edit-academic">Edit academic record</button>
<dialog class="hs-dialog" id="edit-academic" aria-labelledby="edit-academic-title">
<header class="hs-dialog__header">
<h2 class="hs-dialog__title" id="edit-academic-title">Edit academic record</h2>
<button class="hs-dialog__close" type="button" aria-label="Close" data-hs-dialog-close>
<svg class="hs-dialog__close-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true" focusable="false"><path d="M6 6l12 12M18 6 6 18"/></svg>
</button>
</header>
<form class="hs-dialog__form" action="/user/profile/academic" method="post">
<div class="hs-dialog__body">
<div class="hs-form">
<div class="hs-field">
<label class="hs-label" for="dlg-school">School</label>
<input class="hs-control" id="dlg-school" name="school" type="text" value="Sample College">
</div>
<div class="hs-field">
<label class="hs-label" for="dlg-gpa">GPA <span class="hs-optional">(optional)</span></label>
<input class="hs-control hs-control--short" id="dlg-gpa" name="gpa" type="text" inputmode="decimal" value="3.75">
</div>
</div>
</div>
<footer class="hs-dialog__footer">
<button class="hs-button hs-button--sm" type="submit">Save</button>
<button class="hs-button hs-button--secondary hs-button--sm" type="button" data-hs-dialog-close>Cancel</button>
</footer>
</form>
</dialog>