layout
Auth shell
The frame of a sign-in screen — its layout is one CSS variable, so any arrangement of panel and form is data, not code.
UpdatedCSS onlyPlain classes; no JavaScript in any stack.
Usage
The split layout at a constrained height — panel beside the form once the viewport is 48rem wide, a full sign-in card with SSO and recovery links.
<div class="ca-auth-shell ca-auth-shell--split min-h-96 w-full rounded-lg border">
<aside class="ca-auth-shell__panel">
<p class="ca-eyebrow">Ground Control</p>
<h2 class="ca-heading ca-heading--md">Every app, one flight path to production.</h2>
<p class="ca-text">Deploys, rollbacks and logs for the whole stack.</p>
</aside>
<div class="ca-auth-shell__main">
<div class="ca-auth-shell__card">
<a class="ca-auth-shell__brand" href="#">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2 2 7l10 5 10-5-10-5Z" /><path d="m2 17 10 5 10-5" /><path d="m2 12 10 5 10-5" /></svg>
<strong>Ground Control</strong>
</a>
<h1 class="ca-auth-shell__title">Sign in</h1>
<p class="ca-auth-shell__description">Welcome back. Use your workspace account.</p>
<form class="mt-6 grid gap-4" novalidate>
<div class="ca-field">
<label class="ca-field__label" for="auth-email">Email address</label>
<input class="ca-input ca-input--md" id="auth-email" name="email" type="email" autocomplete="email" />
</div>
<div class="ca-field">
<label class="ca-field__label" for="auth-pass">Password</label>
<input class="ca-input ca-input--md" id="auth-pass" name="password" type="password" autocomplete="current-password" />
</div>
<button class="ca-button ca-button--primary ca-button--md w-full" type="submit">Sign in</button>
</form>
<div class="mt-4 flex items-center gap-3 text-xs text-muted-foreground">
<span class="h-px flex-1 bg-border" aria-hidden="true"></span>
or continue with
<span class="h-px flex-1 bg-border" aria-hidden="true"></span>
</div>
<button class="ca-button ca-button--outline ca-button--md mt-4 w-full" type="button">
<svg class="ca-icon" aria-hidden="true" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .5A11.5 11.5 0 0 0 .5 12a11.5 11.5 0 0 0 7.86 10.92c.58.1.79-.25.79-.56v-2c-3.2.7-3.87-1.54-3.87-1.54-.53-1.33-1.28-1.69-1.28-1.69-1.05-.72.08-.7.08-.7 1.16.08 1.77 1.19 1.77 1.19 1.03 1.77 2.7 1.26 3.36.96.1-.75.4-1.26.73-1.55-2.55-.29-5.23-1.28-5.23-5.68 0-1.26.45-2.28 1.19-3.09-.12-.29-.52-1.46.11-3.05 0 0 .97-.31 3.17 1.18a11 11 0 0 1 5.78 0c2.2-1.49 3.17-1.18 3.17-1.18.63 1.59.23 2.76.11 3.05.74.81 1.19 1.83 1.19 3.09 0 4.41-2.69 5.38-5.25 5.67.41.36.78 1.05.78 2.13v3.16c0 .31.2.67.8.56A11.5 11.5 0 0 0 23.5 12 11.5 11.5 0 0 0 12 .5Z" /></svg>
GitHub
</button>
<div class="ca-auth-shell__footer">
<a class="ca-link" href="#reset">Forgot your password?</a>
<a class="ca-link" href="#signup">Create an account</a>
</div>
</div>
</div>
</div>Examples
Centered card
The default layout — just the card. Use it for compact screens like password recovery.
<div class="ca-auth-shell ca-auth-shell--card min-h-96 w-full rounded-lg border">
<div class="ca-auth-shell__main">
<div class="ca-auth-shell__card">
<a class="ca-auth-shell__brand" href="#">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2 2 7l10 5 10-5-10-5Z" /><path d="m2 17 10 5 10-5" /><path d="m2 12 10 5 10-5" /></svg>
<strong>Ground Control</strong>
</a>
<h1 class="ca-auth-shell__title">Reset your password</h1>
<p class="ca-auth-shell__description">We email you a link to choose a new one.</p>
<form class="mt-6 grid gap-4" novalidate>
<div class="ca-field">
<label class="ca-field__label" for="reset-email">Email address</label>
<input class="ca-input ca-input--md" id="reset-email" name="email" type="email" autocomplete="email" />
</div>
<button class="ca-button ca-button--primary ca-button--md w-full" type="submit">Send reset link</button>
</form>
<div class="ca-auth-shell__footer">
<a class="ca-link" href="#signin">Back to sign in</a>
</div>
</div>
</div>
</div>Other layouts
--ca-auth-template holds the whole grid template — this panel-on-top arrangement is the same markup, one variable different.
<div class="ca-auth-shell min-h-96 w-full rounded-lg border" style="--ca-auth-template: 'panel' auto 'main' 1fr / 1fr">
<aside class="ca-auth-shell__panel flex-row items-center justify-between gap-6 border-e-0 border-b p-6">
<div>
<p class="ca-eyebrow">Ground Control</p>
<h2 class="ca-heading ca-heading--md">Every app, one flight path to production.</h2>
</div>
<p class="ca-text max-w-xs">Deploys, rollbacks and logs for the whole stack.</p>
</aside>
<div class="ca-auth-shell__main">
<div class="ca-auth-shell__card">
<a class="ca-auth-shell__brand" href="#">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2 2 7l10 5 10-5-10-5Z" /><path d="m2 17 10 5 10-5" /><path d="m2 12 10 5 10-5" /></svg>
<strong>Ground Control</strong>
</a>
<h1 class="ca-auth-shell__title">Sign in</h1>
<p class="ca-auth-shell__description">Welcome back. Use your workspace account.</p>
<form class="mt-6 grid gap-4" novalidate>
<div class="ca-field">
<label class="ca-field__label" for="auth-email-top">Email address</label>
<input class="ca-input ca-input--md" id="auth-email-top" name="email" type="email" autocomplete="email" />
</div>
<div class="ca-field">
<label class="ca-field__label" for="auth-pass-top">Password</label>
<input class="ca-input ca-input--md" id="auth-pass-top" name="password" type="password" autocomplete="current-password" />
</div>
<button class="ca-button ca-button--primary ca-button--md w-full" type="submit">Sign in</button>
</form>
<div class="ca-auth-shell__footer">
<a class="ca-link" href="#reset">Forgot your password?</a>
<a class="ca-link" href="#signup">Create an account</a>
</div>
</div>
</div>
</div>Guidance and accessibility
When to use
- Use an auth shell for sign-in, registration and password recovery screens — the pages before the app.
- The card layout is the default; add
ca-auth-shell--splitfor a visual panel beside the form on wide screens. Compose the panel with ordinary content: a headline, a checklist, a quote. - Put the form inside the card: fields, an alert for errors, a submit button. The footer holds the secondary links.
Layouts are data
Every part owns a named grid area (panel, main), and
--ca-auth-template on the root holds the whole grid-template
shorthand. The same markup rearranges into any layout by setting the
variable — and grid areas follow the document direction, so a panel at
the inline start and the inline end mirror automatically in RTL:
/* Panel at the end, two-thirds to the form */
.my-auth {
--ca-auth-template: "main panel" 1fr / 3fr 2fr;
}Including an element shows it — the __panel displays whenever it is in
the markup, unless a layout hides it. Extra regions work the same way:
name an area in your template and give your element a plain
[grid-area:*] utility — no contract class needed:
/* Banner above the form, footer below — for elements with
[grid-area:banner] and [grid-area:footer] */
.my-auth {
--ca-auth-template:
"banner" auto
"main" 1fr
"footer" auto /
1fr;
}The ca-auth-shell-wide variant drives the built-in --split at a 48rem
viewport; a custom layout picks its own breakpoint with an ordinary media
query around the same variable. To hide the panel conditionally instead
of removing it, set --ca-auth-panel: none.
Accessibility
- The main region is a
<main>so the form is the landmark; the panel is complementary decoration — keep essential copy out of it. - Put
ca-auth-shell__panelbeforeca-auth-shell__mainso the reading order matches the visual order.
Classes
Generated from the component contract. Parts use __, modifiers use --.
| Class | Kind | Use it for |
|---|---|---|
ca-auth-shell | Component | The frame; fills the viewport. |
ca-auth-shell__panel | Part | The visual half of the split layout; the host composes its content. Hidden by the card layout. |
ca-auth-shell__main | Part | The form column — centers the card and scrolls when the card is taller than the viewport. |
ca-auth-shell__card | Part | The box around the brand and the form. |
ca-auth-shell__brand | Part | The logo and product name above the title. |
ca-auth-shell__title | Part | The page heading, such as "Sign in". |
ca-auth-shell__description | Part | The line under the title that says what happens next. |
ca-auth-shell__footer | Part | Secondary links under the card, such as sign-up and password recovery. |
ca-auth-shell--card | Layout | Layout: the default — the card centered in the viewport. |
ca-auth-shell--split | Layout | Layout: a visual panel beside the form column once the viewport is 48rem wide. |
Skins
The same markup, dressed by each skin. Structure and behavior stay the same; only the look changes.
Base skin
Neutral grayscale starting point; product skins extend it and change what differs.
Calaca skin
The Calaca UI brand: hueso paper and tinta ink, cempasúchil marigold, rosa and jade.
Bloque skin
Example skin pushed far from base: square, heavy, offset shadows, larger targets.
Pliego skin
Example skin from the press sheet: cool ground, bond-paper sheets, rule-grey hairlines, one red mark.
Framework notes
Link the prebuilt stylesheet of your skin next to your brand stylesheet. No build step.
<link rel="stylesheet" href="brand.css" />
<link rel="stylesheet" href="ca-base.css" />In Astro, Vite and other Tailwind projects, import Calaca after Tailwind and your brand in your main stylesheet.
@import "tailwindcss";
@import "./brand.css";
@import "@usecalaca/ui/core.css";
@import "@usecalaca/ui/skins/base.css";Same stylesheet as above; write the classes in className.
<button className="ca-button ca-button--primary ca-button--md" type="button">Save changes</button>