Skip to content

Field ​

A labeled control with an optional hint and error under it, stacked 6 pixels apart. FieldHint and FormError are the same two lines on their own, for a control that is not inside a Field.

tsx
import { Field, FieldHint, FormError } from '@basmilius/react-ui';
tsx
import { useState } from 'react';
import { Field, FieldHint, FormError, Input, Segmented } from '@basmilius/react-ui';

export default function FieldDemo() {
    const [name, setName] = useState('feature/export');
    const taken = name.trim() === 'main';
    const [ground, setGround] = useState('dark');

    return (
        <div className="flex w-80 flex-col gap-4">
            <Field label="Branch name" hint="Lowercase, with slashes for a folder." error={taken ? 'A branch called main already exists.' : null}>
                <Input mono value={name} onChange={(event) => setName(event.target.value)} />
            </Field>
            <Field group label="Ground" hint="What every frame is drawn on.">
                <Segmented
                    label="Ground"
                    value={ground}
                    onValueChange={setGround}
                    options={[
                        { id: 'dark', label: 'Dark' },
                        { id: 'light', label: 'Light' }
                    ]}
                />
            </Field>
            <div>
                <Input aria-label="Remote URL" placeholder="https://" />
                <FieldHint>A hint on its own keeps a margin above it.</FieldHint>
            </div>
            <FormError>The remote refused the push.</FormError>
        </div>
    );
}

Type main into the branch name to see the error. An Input or a TextArea inside a Field connects itself. The label points at it with htmlFor, aria-describedby names the hint and the error, and an error sets aria-invalid. Another control needs its own accessible name, since the Field cannot reach inside it.

A group ​

A <label> can point at an input, not at a Segmented, a Select, ChoiceCards or a path in a box with a button beside it. group draws the same label, hint and error with the same spacing, and makes the Field a role="group" its label names and its hint and error describe. The control inside still takes its own accessible name, and an Input inside a group is left unconnected, for an input that is one part of what the group holds.

tsx
<Field group label="Folder" hint="The project is made in a new folder here.">
    <div className="flex items-center gap-2">
        <div className="field flex min-w-0 flex-1 items-center text-text-muted">{path}</div>
        <Button variant="secondary" onClick={choose}>Choose…</Button>
    </div>
</Field>

Field props ​

PropType
childrenReactNodeRequired. The control.
labelReactNodeDrawn as a SectionLabel above the control.
hintReactNodeThe line under the control that says what goes in it.
errorReactNodeShown under the control and marks it invalid. null or '' shows nothing.
groupbooleanLabels a group rather than one control. Default false.
classNamestring
refRef<HTMLDivElement>

FieldHint and FormError ​

FieldHint is a <p> in the hint's style with a 4 pixel margin above it. FormError is a <p> with role="alert", so a screen reader announces it when it appears; use it for what went wrong in a dialog or a form as a whole. Both take className, ref and render.

FieldProps, FieldHintProps and FormErrorProps are exported types.