A centered layout puts one task in the middle of the viewport and nothing else: signing in, confirming an address, an error page. The frame is a page, so it is as tall as the viewport in dvh; two utilities center what is in it.
Centered
<main class="page items-center justify-center p-lg bg-surface-tray fg-text-body text-sm"> <form class="sheet form w-full max-w(380) p-xl"> <header class="flex flex-col gap-3xs"> <h1 class="heading heading-lg">Sign in to Atlas</h1> <p class="m(0) fg-text-muted">Welcome back.</p> </header> <label class="field">Email <input class="input" type="email" name="email" autocomplete="email" placeholder="you@example.com"> </label> <label class="field">Password <input class="input" type="password" name="password" autocomplete="current-password"> </label> <button class="btn btn-primary">Sign in</button> <p class="m(0) text-center fg-text-muted">New here? <a class="link" href="#">Create an account</a> </p> </form></main>#How it is built
page items-center justify-center. The page is a column at least as tall as the viewport, on the<main>itself since the task is the whole page; the two utilities center its content on both axes.p-lgkeeps the card off the edges of a phone.w-full max-w(380)on the card. It fills a phone and stops at a comfortable width for one column of fields.- The tray under the sheet.
bg-surface-trayon the frame and a sheet for the card: the card is the one raised thing on the page. - When the card is taller than the viewport, the page scrolls. A minimum height, not a fixed one, is what makes that work. A phone with its keyboard open is this case.
The card is also a form, which sets the distance between its fields.
#Beside a panel
From the md breakpoint the frame becomes two equal columns: the task in one, and a panel in the other for the brand, a quotation or a picture. Below it the panel leaves, and the task is alone again.
Beside a panel
<div class="page bg-surface-base fg-text-body text-sm"> <div class="grow(1) grid grid-cols(2)@md"> <main class="flex items-center justify-center p-lg"> <form class="form w-full max-w(340)"> <header class="flex flex-col gap-3xs"> <h1 class="heading heading-lg">Sign in to Atlas</h1> <p class="m(0) fg-text-muted">Welcome back.</p> </header> <label class="field">Email <input class="input" type="email" name="email" autocomplete="email" placeholder="you@example.com"> </label> <label class="field">Password <input class="input" type="password" name="password" autocomplete="current-password"> </label> <button class="btn btn-primary">Sign in</button> <p class="m(0) text-center fg-text-muted">New here? <a class="link" href="#">Create an account</a> </p> </form> </main> <aside class="hidden flex@md flex-col justify-between p-2xl bg-surface-inverse fg-text-inverse"> <p class="m(0) flex items-center gap-xs text-md font-medium"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M12 3a9 9 0 1 0 0 18a9 9 0 0 0 0-18M12 3a9 9 0 0 0 0 18" /> </svg>Atlas</p> <blockquote class="m(0) flex flex-col gap-md max-w(440)"> <p class="m(0) text-2xl">We replaced four libraries with a stylesheet, and the interface got quieter.</p> <footer class="text-sm">Katherine Johnson, Engineering at Orbit</footer> </blockquote> </aside> </div></div>grid grid-cols(2)@md splits the frame; the panel is hidden flex@md. This one measures the viewport, with @md, not a container: the frame is the viewport.
The panel is ink, bg-surface-inverse with fg-text-inverse. Keep it to a few words: it is the first thing that leaves, so nothing a reader needs may live there.
#Accessibility
- The page has one
<h1>, in the card. - The panel is an
<aside>. When it holds only decoration, hide it from assistive technology witharia-hidden="true"; when it holds a quotation, it is content and stays. - Fields carry
autocompletevalues, so a password manager can fill them.
#Related
- FormThe rhythm of a form: rows, sections and actions that follow their column.
- SurfaceTrays, sheets and wells: the material ladder every component is built on.
- InputText fields cut into the surface, with labels, hints, errors and groups.
- ButtonA raised control that lifts on hover and sinks when pressed.
- LinkText that goes somewhere, in the ink of its sentence.