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/react-ui';
tsx
import { useState } from 'react';
import { Cloud, HardDrive } from 'lucide-react';
import { ChoiceCards } from '@basmilius/react-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/react-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>
            }
        />
    );
}

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'
radio'start' | 'end''end'
detailReactNodeVertical groups only.
disabledboolean
classNamestring
refRef<HTMLDivElement>

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