All components verticalhorizontal
Field
Label, control, helper text, and error slots with vertical and horizontal orientations.
We never share your email.
12345678910111213141516171819202122import * as stylex from "@stylexjs/stylex";import { Field } from "#components/ui/base-ui/field.tsx";import { inputSizes, inputStyles, inputVariants } from "@stylexblocks/ui/recipes/input.ts";
export function Example() { return ( // Root wires label/control/validation state; Error renders automatically // when the control is invalid <Field.Root orientation="vertical"> <Field.Label> Email <Field.RequiredIndicator /> </Field.Label> <Field.Control required placeholder="you@company.com" {...stylex.props(inputStyles.base, inputSizes.md, inputVariants.outline)} /> <Field.Description>We never share your email.</Field.Description> </Field.Root> );}Variants
We never share your email.
We never share your email.
Install
1npx stylexblocks add fieldSource
components/ui/base-ui/field.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132import * as stylex from "@stylexjs/stylex";import { Field as BaseField } from "@base-ui-components/react/field";import { createSlotRecipeContext } from "#lib/base-ui/create-slot-recipe-context.tsx";import { defineSlotRecipe } from "#lib/recipe-props.ts";import { fontSizes } from '@stylexblocks/ui/tokens/font-sizes.stylex.ts'import { fontWeights } from '@stylexblocks/ui/tokens/font-weights.stylex.ts'import { lineHeights } from '@stylexblocks/ui/tokens/line-heights.stylex.ts'import { semanticColors } from '@stylexblocks/ui/semantic-tokens/colors.stylex.ts'import { spacing } from '@stylexblocks/ui/tokens/spacing.stylex.ts'
const fieldRequiredIndicator = stylex.create({ base: { color: semanticColors.fgError, lineHeight: 1, },})
const fieldRoot = stylex.create({ base: { display: 'flex', width: '100%', position: 'relative', gap: spacing._1_5, },})
const fieldRootOrientations = stylex.create({ vertical: { flexDirection: 'column', alignItems: 'flex-start', }, horizontal: { display: 'grid', gridTemplateColumns: 'var(--field-label-width, 8rem) 1fr', alignItems: 'center', },})
const fieldLabel = stylex.create({ base: { display: 'flex', alignItems: 'center', textAlign: 'start', fontSize: fontSizes.sm, lineHeight: lineHeights.sm, fontWeight: fontWeights.medium, gap: spacing._1, userSelect: 'none', opacity: { default: null, ':is(:disabled, [disabled], [data-disabled])': 0.5, }, },})
const fieldErrorText = stylex.create({ base: { display: 'inline-flex', alignItems: 'center', fontWeight: fontWeights.medium, gap: spacing._1, color: semanticColors.fgError, fontSize: fontSizes.xs, lineHeight: lineHeights.xs, },})
const fieldErrorTextOrientations = stylex.create({ horizontal: { gridColumn: 2, },})
const fieldHelperText = stylex.create({ base: { color: semanticColors.fgMuted, fontSize: fontSizes.xs, lineHeight: lineHeights.xs, },})
const fieldHelperTextOrientations = stylex.create({ horizontal: { gridColumn: 2, },})
export type FieldOrientation = keyof typeof fieldRootOrientations
const fieldRecipe = defineSlotRecipe({ slots: { requiredIndicator: { styles: fieldRequiredIndicator }, root: { styles: fieldRoot, orientations: fieldRootOrientations }, label: { styles: fieldLabel }, errorText: { styles: fieldErrorText, orientations: fieldErrorTextOrientations }, helperText: { styles: fieldHelperText, orientations: fieldHelperTextOrientations }, }, defaultVariants: { orientation: "vertical" },});
function RequiredIndicatorNode({ children, ...props }: React.ComponentProps<"span">) { return <span {...props}>{children ?? "*"}</span>;}
const { withProvider, withContext } = createSlotRecipeContext(fieldRecipe);
/** * Field — Base UI's field machine (label/control wiring, validation-aware * description and error text) wearing the field slot recipe. * `<Field.Root orientation>` resolves the recipe once and every part * inherits it through context. */export const Field = { Root: withProvider(BaseField.Root, "root", "FieldRoot"), Label: withContext(BaseField.Label, "label", "FieldLabel"), RequiredIndicator: withContext( RequiredIndicatorNode, "requiredIndicator", "FieldRequiredIndicator", ), Control: BaseField.Control, Description: withContext(BaseField.Description, "helperText", "FieldDescription"), Error: withContext(BaseField.Error, "errorText", "FieldError"),};
export type FieldRootProps = React.ComponentProps<typeof Field.Root>;export type FieldLabelProps = React.ComponentProps<typeof Field.Label>;export type FieldRequiredIndicatorProps = React.ComponentProps<typeof Field.RequiredIndicator>;export type FieldControlProps = BaseField.Control.Props;export type FieldDescriptionProps = React.ComponentProps<typeof Field.Description>;export type FieldErrorProps = React.ComponentProps<typeof Field.Error>;