Skip to content
On this page Form first, with a full-bleed map or image

Layout

AuthLayout

A two-column page for sign in, sign up and password reset, with AuthCard for the form.

<script>
  import { AuthLayout, AuthCard, TextField, Button, Stack } from "@codeatlasdev/ui";
</script>

<AuthLayout logoName="Acme">
  {#snippet panel()}
    <blockquote>Ship the boring parts in a day.</blockquote>
  {/snippet}

  <AuthCard title="Welcome back" subtitle="Sign in to continue">
    <form>
      <Stack gap="3">
        <TextField label="Email" type="email" />
        <Button type="submit" fullWidth>Sign in</Button>
      </Stack>
    </form>
    {#snippet footer()}
      <a href="/sign-up">Create an account</a>
    {/snippet}
  </AuthCard>
</AuthLayout>

AuthLayout renders the page <main id="main">, so a route that uses it must not add another one. The panel snippet fills the side column; below 900px (panelHidden="lg": 1200px) it is hidden and mobileBrand shows a compact brand strip above the form. The full pages live in the sign in, sign up and forgot password templates.

Form first, with a full-bleed map or image

Put the panel on the right, fill it edge to edge, add a bar above the form and left-align the form. The panel is hidden below 1200px, and its snippet is not mounted there, so a map, video or image inside it is never fetched on mobile. The header snippet sticks to the top of the form column with the safe-area inset and replaces the mobile brand strip. See the waitlist with map template.

<AuthLayout panelSide="right" panelBleed panelHidden="lg" align="start">
  {#snippet header()}
    <a href="/"><img src="/wordmark.svg" alt="Acme" height="24" /></a>
    <Button variant="outlined" size="sm" href="/sign-up">Create account</Button>
  {/snippet}
  {#snippet panel()}<LiveMap />{/snippet}

  <AuthCard variant="plain" title="Sign in">…</AuthCard>
</AuthLayout>

With panelBleed the panel has no padding, no logo and no blobs, sticks to the viewport height, and the divider between the columns is the low-contrast --ca-outline-subtle hairline.

Flat panel, no gradient or blobs

The default panel is the brand gradient with three animated blobs. A theme with defaultStyle: 'flat' or 'outlined' gets a flat panel on its own (panelTone="auto", the default): a solid --ca-surface-raised fill, a hairline divider, --ca-text-primary panel text and no blobs. Force it with panelTone="flat", or keep the gradient under a flat theme with panelTone="brand". Never override .ca-auth__panel or hide .ca-auth__blobs from app CSS.

<AuthLayout panelTone="flat">…</AuthLayout>
/* Any tone: a color or gradient of your own. Declare it on :root or a wrapper, without !important. */
:root { --ca-auth-panel-bg: var(--ca-surface-sunken); }
/* The text inside the panel follows --ca-auth-panel-fg (white on brand, --ca-text-primary on flat). */
#840

Flat panel

Opens a sign-in page with a flat panel; toggle Brand to see the gradient and blobs.

Reference #840 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { AuthLayout, AuthCard, Button, SegmentedControl, Stack } from '@codeatlasdev/ui';

  let open = $state(false);
  let tone = $state<'brand' | 'flat'>('flat');
</script>

<Button variant="outlined" size="sm" onclick={() => (open = true)}>Open sign-in page</Button>

{#if open}
  <div class="overlay">
    <AuthLayout logoName="Acme" panelTone={tone}>
      {#snippet panel()}
        <p class="auth-quote__text">Ship the boring parts in a day.</p>
      {/snippet}

      <AuthCard title="Welcome back" subtitle="Panel tone: {tone}">
        <Stack gap="3">
          <SegmentedControl
            aria-label="Panel tone"
            bind:value={tone}
            items={[
              { value: 'flat', label: 'Flat' },
              { value: 'brand', label: 'Brand' },
            ]}
          />
          <Button variant="outlined" onclick={() => (open = false)}>Close</Button>
        </Stack>
      </AuthCard>
    </AuthLayout>
  </div>
{/if}

<style>
  .overlay {
    position: fixed;
    inset: 0;
    z-index: var(--ca-z-modal);
    overflow: auto;
    background: var(--ca-surface-base);
  }
</style>

AuthLayout props

PropTypeDefaultDescription
logoMarkstring'CA'Text inside the logo square.
logoNamestring'CodeAtlas UI' (none when brand is set)Product name next to the mark. With a `brand` snippet it renders only when you pass it.
logoHrefstring'/'Where the logo links to.
mobileBrandbooleantrueShows the compact brand strip below 768px, where the side panel is hidden.
brandSnippet–Replaces the whole logo, mark and name (an SVG wordmark). Pass `logoName` too if you want a name beside it.
panelSnippet–Content of the side column.
panelSide'left' | 'right''left'Which side the panel sits on.
panelTone'auto' | 'brand' | 'flat''auto'`brand` is the brand gradient with blobs; `flat` is a solid `--ca-auth-panel-bg` (default `--ca-surface-raised`) with no blobs; `auto` is `flat` under the `flat` and `outlined` surface styles, `brand` otherwise.
panelBleedbooleanfalseFills the panel edge to edge with the `panel` snippet: no padding, no logo, no blobs. Sticky to the viewport height.
panelHidden'md' | 'lg''md'Width from which the panel shows: `md` 900px, `lg` 1200px. Below it only the form shows.
headerSnippet–Bar across the top of the form column (logo, language switcher, CTA). Sticky, honors the safe-area inset, and replaces the mobile brand strip.
align'center' | 'start''center'Horizontal alignment of the form in its column.
childrenSnippet–The form column, usually an AuthCard.

AuthCard props

PropTypeDefaultDescription
variant'card' | 'plain''card'`plain` sets the title at display size and drops the footer centering, for a left-aligned form.
titlestring–Heading of the card.
subtitlestring–Text under the heading.
footerSnippet–Links under the form, such as sign up.
childrenSnippet–The form.