Layout
AppForm
A page-level form layout with a fixed header, a scrollable body and a sticky footer.
<script>
import { AppForm, FormSection, TextField, Button } from "@codeatlasdev/ui";
</script>
<AppForm title="New user" backHref="/users">
<FormSection title="Account">
<TextField label="Name" bind:value={name} />
</FormSection>
{#snippet footer()}
<Button variant="ghost" href="/users">Cancel</Button>
<Button variant="filled" onclick={save}>Save</Button>
{/snippet}
</AppForm>
AppForm fills its parent, so give the parent a height. It replaces the removed FormPage. For a floating action bar inside your own scroll container, use StickyActions.
Header with a back button, a grid of fields, a list of switches and a sticky footer.
Code
<script lang="ts">
import { AppForm, FormSection, TextField, Select, Switch, Button, Divider } from '@codeatlasdev/ui';
import { ArrowLeft } from '@lucide/svelte';
let name = $state('');
let email = $state('');
let role = $state('viewer');
let welcome = $state(true);
let digest = $state(false);
let saving = $state(false);
function save() {
saving = true;
setTimeout(() => (saving = false), 1400);
}
const roleOptions = [
{ value: 'admin', label: 'Admin' },
{ value: 'editor', label: 'Editor' },
{ value: 'viewer', label: 'Viewer' },
];
</script>
<div class="frame">
<AppForm title="New User" description="Fill in the details to create a new user.">
{#snippet leading()}
<Button variant="ghost" size="sm" aria-label="Back">
{#snippet leading()}<ArrowLeft size={15} />{/snippet}
</Button>
{/snippet}
<FormSection title="Account details" description="Basic information for the new user.">
<TextField label="Full name" bind:value={name} placeholder="Ada Lovelace" />
<TextField label="Work email" bind:value={email} placeholder="[email protected]" type="email" />
<Select label="Role" bind:value={role} options={roleOptions} />
</FormSection>
<FormSection title="Notifications" description="Email preferences." layout="list">
<Switch label="Send welcome email" bind:checked={welcome} />
<Divider />
<Switch label="Weekly digest" bind:checked={digest} />
</FormSection>
{#snippet footer()}
<Button variant="ghost">Cancel</Button>
<Button variant="filled" onclick={save} disabled={saving || !name || !email}>
{saving ? 'Saving…' : 'Create user'}
</Button>
{/snippet}
</AppForm>
</div>
<style>
.frame {
width: 100%;
height: 480px;
border-radius: var(--ca-radius-lg);
box-shadow: inset 0 0 0 1px var(--ca-outline-subtle);
overflow: hidden;
display: flex;
flex-direction: column;
}
</style>
Omit title, description, leading, trailing and backHref to drop the header.
Code
<script lang="ts">
import { AppForm, FormSection, TextField, Button } from '@codeatlasdev/ui';
</script>
<div class="frame">
<AppForm>
<FormSection title="Quick update">
<TextField label="Name" placeholder="Enter name…" />
<TextField label="Email" type="email" placeholder="[email protected]" />
</FormSection>
{#snippet footer()}
<Button variant="ghost">Cancel</Button>
<Button variant="filled">Save</Button>
{/snippet}
</AppForm>
</div>
<style>
.frame {
width: 100%;
height: 360px;
border-radius: var(--ca-radius-lg);
box-shadow: inset 0 0 0 1px var(--ca-outline-subtle);
overflow: hidden;
display: flex;
flex-direction: column;
}
</style>
Props
| Prop | Type | Default | Description |
|---|
title | string | – | Page title in the header. |
description | string | – | Subtitle under the title. |
backHref | string | – | Renders a back-arrow button linking here. Ignored when leading is provided. |
backLabel | string | 'Back' | Accessible label of the automatic back button. |
leading | Snippet | – | Left side of the header, such as a back button or breadcrumb. Overrides backHref. |
trailing | Snippet | – | Right side of the header, such as a status chip or secondary actions. |
footer | Snippet | – | Sticky bottom bar for cancel, save and delete buttons. |
children | Snippet | – | Form body. Usually FormSection components. |
class | string | '' | Extra class names. |