Skip to content
Components

Field

Keep a label, input, help text and validation message together.

TS
import { … } from "@fungi.computer/caps/components/field";

Hero

TSX
import { Field } from "@fungi.computer/caps/components/field";
import { Input } from "@fungi.computer/caps/components/input";
export default function Hero() {
return (
<Field description="Shown to other foragers near you." label="Display name">
<Input defaultValue="Spore Scout" name="displayName" />
</Field>
);
}

Orientations

TSX
import { Field } from "@fungi.computer/caps/components/field";
import { Input } from "@fungi.computer/caps/components/input";
import { Switch } from "@fungi.computer/caps/components/switch";
export default function Orientations() {
return (
<div className="flex w-full max-w-sm flex-col gap-4">
<Field label="Radius" orientation="horizontal">
<Input className="w-24" defaultValue="5 mi" name="radius" />
</Field>
<Field label="Notify on new spots" orientation="inline">
<Switch defaultChecked name="notify" />
</Field>
</div>
);
}

Validation

TSX
import { useState } from "react";
import { Field } from "@fungi.computer/caps/components/field";
import { Input } from "@fungi.computer/caps/components/input";
export default function Validation() {
const [value, setValue] = useState("");
const error = value.length > 0 && value.length < 3 ? "Too short" : undefined;
return (
<Field error={error} label="Club name" required>
<Input
onChange={(event) => {
setValue(event.target.value);
}}
value={value}
/>
</Field>
);
}

Number field

TSX
import { useState } from "react";
import { Field, NumberField } from "@fungi.computer/caps/components/field";
export default function Quantity() {
const [count, setCount] = useState<number | null>(3);
return (
<Field label="Baskets to bring">
<NumberField max={10} min={0} onValueChange={setCount} value={count} />
</Field>
);
}

Props

FormFooterProps

PropTypeDescription
error?ReactNodeThe submit-time error, announced above the actions.
layout?"end" | "stretch"end right-aligns actions; stretch stacks full-width actions for single-column cards.
status?ReactNodeQuiet state opposite the actions (role="status"), such as a Status LED reading "Saved".
divider?booleanDraws the rule above the footer; short inline forms leave it off.
  • error?ReactNode

    The submit-time error, announced above the actions.

  • layout?"end" | "stretch"

    end right-aligns actions; stretch stacks full-width actions for single-column cards.

  • status?ReactNode

    Quiet state opposite the actions (role="status"), such as a Status LED reading "Saved".

  • divider?boolean

    Draws the rule above the footer; short inline forms leave it off.

FormProps

PropTypeDescription
busy?booleanDisables every control while a submission is in flight.
  • busy?boolean

    Disables every control while a submission is in flight.

FormSectionProps

PropTypeDescription
titleReactNode
description?ReactNode
level?2 | 3 | 4Heading level inside the page outline.
childrenReactNode
  • titleReactNode
  • description?ReactNode
  • level?2 | 3 | 4

    Heading level inside the page outline.

  • childrenReactNode

FieldSetProps

PropTypeDescription
legendReactNode
description?ReactNode
error?ReactNodeThe group's announced error, such as "Choose at least one right".
childrenReactNode
  • legendReactNode
  • description?ReactNode
  • error?ReactNode

    The group's announced error, such as "Choose at least one right".

  • childrenReactNode

FieldProps

PropTypeDescription
childrenReactNode
description?ReactNode
error?ReactNode
icon?ReactNodeA decorative marker, such as a Sigil or App icon, before the label. It is hidden from assistive tech; the label stays the control's name.
label?ReactNode
orientation?"horizontal" | "inline" | "vertical"vertical stacks label, control and error; horizontal puts the control opposite its copy (settings rows); inline puts a checkbox or switch before its copy.
optional?booleanMarks an optional field in a mostly required form.
readout?booleanThe child is a read-only readout, such as a Status, not a control: the label names the row and nothing is wired to the child.
required?boolean
  • childrenReactNode
  • description?ReactNode
  • error?ReactNode
  • icon?ReactNode

    A decorative marker, such as a Sigil or App icon, before the label. It is hidden from assistive tech; the label stays the control's name.

  • label?ReactNode
  • orientation?"horizontal" | "inline" | "vertical"

    vertical stacks label, control and error; horizontal puts the control opposite its copy (settings rows); inline puts a checkbox or switch before its copy.

  • optional?boolean

    Marks an optional field in a mostly required form.

  • readout?boolean

    The child is a read-only readout, such as a Status, not a control: the label names the row and nothing is wired to the child.

  • required?boolean

NumberFieldProps

PropTypeDescription
value?number | nullControlled value; null is empty.
defaultValue?number | null
onValueChange()?(value) => voidCalled with each committed value, already clamped to min/max.
min?number
max?number
step?numberArrow keys and the − / + keys move by step; Page Up/Down by ten steps.
unit?ReactNodeA short unit readout after the value, such as "min" or "GB".
wrap?booleanCycle from max back to min (and back) instead of stopping at the ends.
invalid?boolean
decrementLabel?stringAccessible names for the − / + keys.
incrementLabel?string
  • value?number | null

    Controlled value; null is empty.

  • defaultValue?number | null
  • onValueChange()?(value) => void

    Called with each committed value, already clamped to min/max.

  • min?number
  • max?number
  • step?number

    Arrow keys and the − / + keys move by step; Page Up/Down by ten steps.

  • unit?ReactNode

    A short unit readout after the value, such as "min" or "GB".

  • wrap?boolean

    Cycle from max back to min (and back) instead of stopping at the ends.

  • invalid?boolean
  • decrementLabel?string

    Accessible names for the − / + keys.

  • incrementLabel?string

Full types: API reference.