Skip to content

ChoiceCards ​

One of a few options, each a card that says what it means. Use it for a choice that deserves more than a select, where the description is what helps a person decide.

tsx
import { ChoiceCards } from '@basmilius/desktop-ui';
tsx
import { useState } from 'react';
import { Cloud, HardDrive } from 'lucide-react';
import { ChoiceCards } from '@basmilius/desktop-ui';

type Storage = 'local' | 'cloud';

export default function ChoiceCardsDemo() {
    const [storage, setStorage] = useState<Storage>('local');

    return (
        <ChoiceCards<Storage>
            label="Where to keep the project"
            className="w-full max-w-xl"
            value={storage}
            onValueChange={setStorage}
            choices={[
                { value: 'local', title: 'On this computer', description: 'Fast, and only here.', icon: HardDrive },
                { value: 'cloud', title: 'In your account', description: 'On every device you sign in on.', icon: Cloud }
            ]}
        />
    );
}

Vertical, with a detail ​

Choices with long descriptions stand one under the other with orientation="vertical". A vertical group can show a detail right under the checked card, for what that choice still asks for, such as a folder or an address. radio="start" moves the dot before the title.

tsx
import { useState } from 'react';
import { ChoiceCards, Field, Input } from '@basmilius/desktop-ui';

type Source = 'empty' | 'clone';

export default function ChoiceCardsVertical() {
    const [source, setSource] = useState<Source>('clone');

    return (
        <ChoiceCards<Source>
            label="How to start"
            orientation="vertical"
            radio="start"
            className="w-full max-w-md"
            value={source}
            onValueChange={setSource}
            choices={[
                { value: 'empty', title: 'An empty folder', description: 'Nothing in it yet.' },
                { value: 'clone', title: 'Clone a repository', description: 'Copy one from a remote.' }
            ]}
            detail={
                <Field label="Repository URL" className="px-1 pb-1">
                    <Input mono placeholder="git@github.com:owner/repo.git" />
                </Field>
            }
        />
    );
}

Columns ​

More choices than one row has room for fill a set number of columns with columns, row by row. It takes the place of orientation, so a group with columns shows no detail.

tsx
import { useState } from 'react';
import { FileText, Image, Music, Video } from 'lucide-react';
import { ChoiceCards } from '@basmilius/desktop-ui';

type Kind = 'document' | 'image' | 'audio' | 'video';

export default function ChoiceCardsColumns() {
    const [kind, setKind] = useState<Kind>('document');

    return (
        <ChoiceCards<Kind>
            label="What to create"
            columns={2}
            className="w-full max-w-xl"
            value={kind}
            onValueChange={setKind}
            choices={[
                { value: 'document', title: 'Document', description: 'Text with headings, lists and tables.', icon: FileText },
                { value: 'image', title: 'Image', description: 'A picture to draw on or crop.', icon: Image },
                { value: 'audio', title: 'Audio', description: 'A recording to trim and mix.', icon: Music },
                { value: 'video', title: 'Video', description: 'Clips on a timeline, cut to length.', icon: Video }
            ]}
        />
    );
}

Keyboard ​

The cards are a Base UI radio group. Tab moves into the group and onto the checked card, and the arrow keys move between cards and check them.

Props ​

ChoiceCards is generic over the value, a string or a number.

PropTypeDefault
valueValueRequired.
onValueChange(value: Value) => voidRequired.
choicesreadonly Choice<Value>[]Required.
labelstringRequired. The group has no visible heading of its own.
orientation'horizontal' | 'vertical''horizontal'
columnsnumberReplaces orientation.
radio'start' | 'end''end'
detailReactNodeVertical groups without columns only.
disabledboolean
classNamestring
refRef<HTMLDivElement>

A Choice<Value> is { value, title, description, icon?, disabled? }. ChoiceCardsProps and Choice are exported types.