Account settings
The blocks of an account page, so every app has the same polished one: the identity card (picture, name, verified e-mail, member since), a picture grid to choose from, a labelled language control, selectable cards for a single choice — beside SettingsSection's one inline save.
import { AccountProfileCard, AvatarPicker, ChoiceCards, LanguagePicker, AppearanceSettings } from "@krizaka/ui/account-settings";Web only — React, from @krizaka/ui/account-settings. Beta: its API may still change in a minor version.
- Mobile · ✗ to build. The Expo apps get the same blocks on @krizaka/ui/native. issue #503
Why it is here. Used by Yomizaka, Akinaza.
When to use
- The profile page of any Krizaka app (a product's
/settings/profile, the future@krizaka/auth). ChoiceCardsfor any single choice that deserves a sentence each: a side, a plan, a way to be answered.
When not to use
- A choice between a few short words (a view, a size).Use instead: Radio group
- A set of tiles that lead somewhere or show a line-up.Use instead: Choice grid
- The language of a public page: it is a link to the same page in the other language.Use instead: Language switch
Installation
Install
npm install @krizaka/ui @krizaka/tailwind @krizaka/tokens tailwindcssStyles
@import "tailwindcss";
@import "@krizaka/tailwind";
@import "@krizaka/ui/tailwind.css";Import
import { AccountProfileCard, AvatarPicker, ChoiceCards, LanguagePicker, AppearanceSettings } from "@krizaka/ui/account-settings";Examples
Profile page
A whole profile section: identity, name, pictures, language, side — one save, dirty then saved with Undo.
Avatar picker
The family's default pictures as one radio group, the upload beside them.
Choice cards
A single choice as two cards with an icon, a title and a line.
Language picker
Labelled, native names.
Appearance
Theme cards (live miniatures, one check), accent, density and language as settings rows; applied at once with useAppearance().
Section patterns
Security, subscription, storage and my data as flat sections of rows.
Props
AccountProfileCard
The account's identity card: picture, name, verified e-mail, member since.
| Prop | Type | Default | Description |
|---|---|---|---|
emailrequired | string | — | The e-mail address. |
namerequired | string | — | The name (the card's heading). |
unverifiedLabelrequired | string | — | “Not verified” — passed translated. |
verifiedrequired | boolean | — | Whether the address is verified. |
verifiedLabelrequired | string | — | “Verified” — passed translated. |
action | ReactNode | — | Beside the picture: the change action (a Button). |
avatar | Pick<AvatarProps, "src" | "fallback" | "defaultAvatar"> | — | The picture: the person's photo, or the default avatar of the brand. |
compact | boolean | false | One calm line for a settings row: a medium picture beside the name and the e-mail, no wrapping. |
memberSince | string | — | “Member since October 2026” — formatted by the app, in the viewer's language. |
AppearanceSettings
The appearance rows of a settings page: theme cards, accent, density, language, sound.
| Prop | Type | Default | Description |
|---|---|---|---|
onThemeChangerequired | (id: string) => void | — | Called with the id chosen. |
themerequired | string | — | The id of the theme chosen. |
themesrequired | readonly ThemeCardOption[] | — | The themes offered: at least system, light and dark. |
accent | string | null | — | The id of the accent chosen; null for the product's own. |
accents | readonly AccentOption[] | — | The accents offered (the product's own first). Without it the row is not shown. |
className | string | — | Classes merged on the root. |
densities | readonly { value: string; label: string; }[] | — | The densities offered, with their names (standard, compact). |
density | string | — | The density chosen. |
labels | Partial<AppearanceLabels> | — | The words, passed translated (English by default). |
language | string | — | The code of the language chosen. |
languages | readonly LanguageOption[] | — | The languages offered, each named in its own language. |
onAccentChange | ((id: string) => void) | — | Called with the id chosen. |
onDensityChange | ((density: string) => void) | — | Called with the density chosen. |
onLanguageChange | ((code: string) => void) | — | Called with the code chosen. |
onSoundChange | ((on: boolean) => void) | — | Called with the new state. |
sound | boolean | — | Shown only when given: the sound is opt-in. |
AvatarPicker
A grid of pictures to choose from, with an upload beside them: a radio group, never a row of loose radios.
| Prop | Type | Default | Description |
|---|---|---|---|
labelrequired | string | — | The group's accessible name (“Profile picture”) — passed translated. |
optionsrequired | readonly AvatarOption[] | — | The pictures. |
dense | boolean | false | Smaller tiles, for a settings row. |
description | ReactNode | — | One line under the visible label: what it changes. |
onValueChange | ((value: string) => void) | ((id: string) => void) | — | The viewer chooses another picture. |
showLabel | boolean | — | Shows the label above the grid, with description under it. |
upload | ReactNode | — | After the pictures, in the grid: the upload action (a Button, a file input's label). Not a radio. |
value | string | null | — | The chosen id (controlled). |
ChoiceCards
Cards to choose one from: a radio group where each card has an icon, a title and a description.
| Prop | Type | Default | Description |
|---|---|---|---|
labelrequired | string | — | The question (“Which side of the lease are you?”) — the group's accessible name, passed translated. |
optionsrequired | readonly ChoiceCardOption[] | — | The choices. |
description | ReactNode | — | One line under the visible question. |
invalid | boolean | — | Marks every radio invalid: aria-invalid (pair it with a Field.Error). |
onValueChange | ((value: string) => void) | ((id: string) => void) | — | The viewer chooses another picture. |
routeKey | string | — | Binds the choice to this search parameter of the URL (?kind=…): the URL is the state — for a view's selection (a segmented switch), not a form field. |
routeValues | readonly string[] | — | The values the parameter may hold (the radios' values); the first is the default unless defaultValue says otherwise. |
showLabel | boolean | — | Shows the question above the cards, with description under it. |
value | string | null | — | The chosen id (controlled). |
LanguagePicker
The language control of an account page: a labelled segmented choice, native names.
| Prop | Type | Default | Description |
|---|---|---|---|
labelrequired | ReactNode | — | The visible label (“Language”) — passed translated. |
languagesrequired | readonly LanguageOption[] | — | The languages offered. |
description | ReactNode | — | One line under it: what it changes. |
invalid | boolean | — | Marks every radio invalid: aria-invalid (pair it with a Field.Error). |
onValueChange | ((value: string) => void) | ((id: string) => void) | — | The viewer chooses another picture. |
routeKey | string | — | Binds the choice to this search parameter of the URL (?kind=…): the URL is the state — for a view's selection (a segmented switch), not a form field. |
routeValues | readonly string[] | — | The values the parameter may hold (the radios' values); the first is the default unless defaultValue says otherwise. |
showLabel | boolean | true | Shows the label above the control; false when a SettingsRow already says it (the group keeps its accessible name). |
value | string | null | — | The chosen id (controlled). |
Accessibility
- Each group is a named
radiogroup; the chosen item saysaria-checked, shows a ring and a check (not colour alone). - A card's description is its
aria-describedby; a picture is named by itslabel, not its image. - The save result is a polite live region that stays mounted; a failure is
role="alert".
Keyboard
| Keys | Action |
|---|---|
| ← → ↑ ↓ | Moves to the next picture, card or language and chooses it (one tab stop per group). |
| Space | Chooses the focused one. |
| Enter (in a field) | Submits the section's form: saves when it is dirty. |
Best practices
- One save per section (
SettingsSectionsave): enabled while the section is dirty, « Saved » and Undo after it, the failure as an alert. Never a save above the fields. AvatarPickeris one radiogroup — never a row of circles with loose radios; keep the upload inside its grid, after the pictures.- Name every language in its own language and label the control: « Langue », never an unlabelled switch.
- Pass every word translated; the member-since date is formatted by the app in the viewer's language.
- Appearance: pass the themes the product offers (system, light, dark, then Blueprint and Carbone if wanted — never a paper theme inside a signed-in product) from
appearanceof @krizaka/tokens, and only the accents that read on all of them (appearance.passes). The change applies at once (useAppearance()of@krizaka/ui/theme, restored before paint byThemeScript); save it in the account too so it follows the person. Sound is opt-in, off by default, never under reduced motion; density ishtml[data-density]. - Section patterns (security, subscription, storage, my data) are flat sections of
SettingsRows: the billing portal is one link, the export one button, the delete aConfirmButton.
Related components
Generated from the code of @krizaka/ui 3.63.1: its meta.ts, its examples and its types.Edit this documentation on GitHub