Usage
Use SettingsLayout for the page and SettingsSection for each setting. Put your fields and save actions in the section. Add tabs or links through the navigation slot.
<script setup lang="ts">
import { ref } from "vue"
import { Button, Field, SettingsLayout, SettingsSection } from "@dicehub/kappa"
const username = ref("avery")
const savedUsername = ref("avery")
const saveUsername = () => { savedUsername.value = username.value.trim() }
</script>
<template>
<SettingsLayout title="User settings">
<SettingsSection
title="Username"
:description="`Your current username is @${savedUsername}.`"
default-open
>
<form @submit.prevent="saveUsername">
<Field.Root required>
<Field.Label>Username</Field.Label>
<Field.Input v-model="username" autocomplete="username" />
</Field.Root>
<Button type="submit" variant="primary">Save username</Button>
</form>
</SettingsSection>
</SettingsLayout>
</template>Examples
The examples use sample data. Changes stay in the example.
Account forms have one save button with a short label for the setting. Notification options use separate rows. Emails and linked accounts use tables. Privacy checkboxes save on change and show a brief status beside each group.
Account
Account tabs, current values, and a separate form for each setting.
Username
Email address
Password
Time zone
Date format
Time format
Delete account
Email notifications
Profile information
Activity and usage
Current emails
| Status | Actions | |
|---|---|---|
avery@example.comPrimary | Verified | |
avery.work@example.com | Unverified |
Add email address
Linked accounts
| Account type | Name | Actions | |
|---|---|---|---|
GitHubNot connected | — | — | |
GitLabConnected | Avery North | avery@example.com | |
GoogleNot connected | — | — |
<script setup lang="ts">
import { SettingsLayout, SettingsSection, Tabs } from "@dicehub/kappa"
</script>
<template>
<Tabs.Root default-value="account">
<SettingsLayout>
<template #navigation>
<Tabs.List variant="line" aria-label="User settings">
<Tabs.Trigger value="account">Account</Tabs.Trigger>
<Tabs.Trigger value="notifications">Notifications</Tabs.Trigger>
<Tabs.Indicator />
</Tabs.List>
</template>
<Tabs.Content value="account">
<SettingsSection title="Username" description="Current username: @avery" default-open>
<slot name="username-form" />
</SettingsSection>
<SettingsSection title="Email address" description="avery@example.com">
<slot name="email-form" />
</SettingsSection>
</Tabs.Content>
<Tabs.Content value="notifications">
<SettingsSection title="Notifications" :collapsible="false">
<slot name="notification-form" />
</SettingsSection>
</Tabs.Content>
</SettingsLayout>
</Tabs.Root>
</template>Preferences
Time zone and date formats in sections that start open.
Preferences
Time zone
Date format
Time format
<script setup lang="ts">
import { SettingsLayout, SettingsSection } from "@dicehub/kappa"
</script>
<template>
<SettingsLayout title="Preferences">
<SettingsSection title="Time zone" description="Europe/Berlin" default-open>
<slot name="timezone-form" />
</SettingsSection>
<SettingsSection title="Date format" description="05 Oct 2026" default-open>
<slot name="date-format-form" />
</SettingsSection>
</SettingsLayout>
</template>Controlled sections
Open a section from another control, disable it, or keep it visible.
Controlled section
Email notifications
Read-only setting
Contact an administrator to change this setting.
Always open
Keep short settings visible.
Initially open
Uncontrolled sections can open by default.
Fixed section
Ignored close requests: 0
<script setup lang="ts">
import { ref } from "vue"
import { Button, SettingsLayout, SettingsSection } from "@dicehub/kappa"
const open = ref(false)
</script>
<template>
<SettingsLayout title="Notifications">
<template #actions>
<Button @click="open = true">Open section</Button>
<Button @click="open = false">Close section</Button>
</template>
<SettingsSection v-model:open="open" title="Email notifications">
<slot name="notification-form" />
</SettingsSection>
</SettingsLayout>
</template>Installation
Import from the package root or @dicehub/kappa/blocks/settings-layout. Load the theme once in your app.
import { SettingsLayout, SettingsSection } from "@dicehub/kappa/blocks/settings-layout"
import "@dicehub/kappa/styles/theme-kappa.css"API
SettingsLayout
title and description are optional strings. Slots: title, description, actions, navigation, and the default panel content. The title renders as h1. Omit it when your app already has a page heading.
SettingsSection
| Prop | Default | Use |
|---|---|---|
title | Required | Section heading and accessible toggle name. |
description | — | Current value or short help text. |
headingLevel | 2 | Heading level: 2, 3, or 4. |
collapsible | true | Set to false to keep the section open without a toggle. |
defaultOpen | false | Initial state for an uncontrolled section. |
open | — | Controlled state. Use v-model:open. |
disabled | — | Disables the toggle. Disable form controls separately. |
expandLabel, closeLabel | Expand, Close | Translated toggle labels. |
id, ids | Generated | Collapsible identifiers. |
lazyMount, unmountOnExit | false | Optional mounting behavior. Unmounting can discard form state. |
Slots: title, description, actions, and default content. Each receives { open }. Events: update:open, openChange, and exitComplete. The section is also available as SettingsLayout.Section.
Attributes and DOM listeners pass to the layout root or section. Sections stay mounted when closed by default, so form values remain available.
Accessibility
Toggle sections with Enter or Space. Sections expand and close with a short height animation; reduced motion disables it. Closed content is hidden from keyboard navigation. If closing hides the focused control, focus returns to the section toggle, or to its heading when the toggle is disabled. Choose heading levels that fit your page, label every field, and announce save results near the form. Use a confirmation dialog for account deletion.
The time zone example lists UTC offsets and city names, sorted by offset. Offsets use the current date and include daylight saving time. Type a city or region to search. The saved value is the time-zone ID.
The app supplies validation, save requests, permissions, and routing. Use Button's loading state and disable fields while saving.
Related components: Tabs, Field, and Delete Resource. Behavior reference: Collapsible.
Styling
The block uses Kappa tokens in both themes. Set --kappa-settings-layout-content-max, --kappa-settings-layout-padding, or --kappa-settings-section-padding to adjust the layout. Keep forms narrower than the panel when they contain short account fields.