All components
Fieldset
Group related fields under a legend with helper text and spacing tuned per size.
1234567891011121314151617181920212223import * as stylex from "@stylexjs/stylex";import { Field } from "#components/ui/ark-ui/field.tsx";import { Fieldset } from "#components/ui/ark-ui/fieldset.tsx";import { inputSizes, inputStyles, inputVariants } from "@stylexblocks/ui/recipes/input.ts";
export function Example() { return ( // size resolves once on Root; Legend and Content inherit it via context <Fieldset.Root size="md"> <Fieldset.Legend>Contact details</Fieldset.Legend> <Fieldset.HelperText>Used for order updates only.</Fieldset.HelperText> <Fieldset.Content> <Field.Root> <Field.Label>Full name</Field.Label> <Field.Input placeholder="Ada Lovelace" {...stylex.props(inputStyles.base, inputSizes.sm, inputVariants.outline)} /> </Field.Root> </Fieldset.Content> </Fieldset.Root> );}Sizes
sm
md
lg
Install
1npx stylexblocks add fieldsetSource
components/ui/ark-ui/fieldset.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168import * as stylex from "@stylexjs/stylex";import { Fieldset as ArkFieldset } from "@ark-ui/react/fieldset";import { createSlotRecipeContext } from "#lib/ark-ui/create-slot-recipe-context.tsx";import { defineSlotRecipe, resolveSlotStyles } 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 { sizes } from '@stylexblocks/ui/tokens/sizes.stylex.ts'import { spacing } from '@stylexblocks/ui/tokens/spacing.stylex.ts'
const fieldsetRoot = stylex.create({ base: { display: 'flex', flexDirection: 'column', width: sizes.full, },})
const fieldsetRootSizes = stylex.create({ sm: {}, md: {}, lg: {},})
const fieldsetContent = stylex.create({ base: { display: 'flex', flexDirection: 'column', width: sizes.full, },})
/* * Chakra spaces the fieldset root with `spaceY` (sibling margins) because a * native <legend> sits outside the fieldset's anonymous flex content box, so * flex `gap` on the root never reaches it. StyleX has no sibling combinators, * so the equivalent margin-block-start is emitted per slot (helperText, * errorText, content) via the size variants below. */const fieldsetContentSizes = stylex.create({ sm: { gap: spacing._1_5, marginBlockStart: spacing._2, }, md: { gap: spacing._4, marginBlockStart: spacing._4, }, lg: { gap: spacing._4, marginBlockStart: spacing._4, },})
const fieldsetLegend = stylex.create({ base: { color: semanticColors.fg, fontWeight: fontWeights.medium, paddingInline: 0, opacity: { default: null, ':is(:disabled, [disabled], [data-disabled])': 0.5, }, },})
const fieldsetLegendSizes = stylex.create({ sm: { fontSize: fontSizes.sm, lineHeight: lineHeights.sm, }, md: { fontSize: fontSizes.sm, lineHeight: lineHeights.sm, }, lg: { fontSize: fontSizes.md, lineHeight: lineHeights.md, },})
const fieldsetHelperText = stylex.create({ base: { color: semanticColors.fgMuted, fontSize: fontSizes.sm, lineHeight: lineHeights.sm, },})
const fieldsetHelperTextSizes = stylex.create({ sm: { marginBlockStart: spacing._2, }, md: { marginBlockStart: spacing._4, }, lg: { marginBlockStart: spacing._4, },})
const fieldsetErrorText = stylex.create({ base: { display: 'inline-flex', alignItems: 'center', color: semanticColors.fgError, gap: spacing._2, fontWeight: fontWeights.medium, fontSize: fontSizes.sm, lineHeight: lineHeights.sm, },})
const fieldsetErrorTextSizes = stylex.create({ sm: { marginBlockStart: spacing._2, }, md: { marginBlockStart: spacing._4, }, lg: { marginBlockStart: spacing._4, },})
export type FieldsetSize = keyof typeof fieldsetRootSizes
const fieldsetRecipe = defineSlotRecipe({ slots: { root: { styles: fieldsetRoot, sizes: fieldsetRootSizes }, content: { styles: fieldsetContent, sizes: fieldsetContentSizes }, legend: { styles: fieldsetLegend, sizes: fieldsetLegendSizes }, helperText: { styles: fieldsetHelperText, sizes: fieldsetHelperTextSizes }, errorText: { styles: fieldsetErrorText, sizes: fieldsetErrorTextSizes }, }, defaultVariants: { size: "md" },});
const chrome = stylex.create({ reset: { borderWidth: 0, borderStyle: "none", padding: 0, margin: 0, minWidth: 0 },});
const { withProvider, withContext } = createSlotRecipeContext(fieldsetRecipe);
/** * Fieldset — Ark UI's fieldset machine wearing the fieldset slot recipe. * `<Fieldset.Root size>` resolves the recipe once; Legend and Content * inherit the size through context. */export const Fieldset = { Root: withProvider( ArkFieldset.Root, (v) => [...resolveSlotStyles(fieldsetRecipe.slots.root, v), chrome.reset], "FieldsetRoot", ), Legend: withContext(ArkFieldset.Legend, "legend", "FieldsetLegend"), HelperText: withContext(ArkFieldset.HelperText, "helperText", "FieldsetHelperText"), ErrorText: withContext(ArkFieldset.ErrorText, "errorText", "FieldsetErrorText"), Content: withContext("div", "content", "FieldsetContent"),};
export type FieldsetRootProps = React.ComponentProps<typeof Fieldset.Root>;export type FieldsetLegendProps = React.ComponentProps<typeof Fieldset.Legend>;export type FieldsetHelperTextProps = React.ComponentProps<typeof Fieldset.HelperText>;export type FieldsetErrorTextProps = React.ComponentProps<typeof Fieldset.ErrorText>;export type FieldsetContentProps = React.ComponentProps<typeof Fieldset.Content>;