JGuang

Field

Field · Astryx

Preview
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0

Field

Field is a low-level wrapper for custom, native, or third-party controls that do not already provide field label, description, and status UI. Use it when you need the Field shell around a control you own; use styled Astryx inputs like TextInput, Typeahead, and Select directly when they already expose label, description, and validation props.

Installation

npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0

Requires React 19+, base CSS and a Theme provider. See Setup.

Field

Field is a low-level wrapper for custom, native, or third-party controls that do not already provide field label, description, and status UI. Use it when you need the Field shell around a control you own; use styled Astryx inputs like TextInput, Typeahead, and Select directly when they already expose label, description, and validation props.

PropTypeDefaultDescription
label *stringLabel text for the field (always rendered for accessibility).
inputID *stringID for the input element (used for the label htmlFor attribute).
labelIDstringID applied to the label element itself for group accessibility.
isGroupLabelbooleanfalseRenders the label as a span for control groups (radiogroup, checkbox list).
children *ReactNodeThe input or control to render.
isLabelHiddenbooleanfalseVisually hide the label (still accessible to screen readers).
isDisabledbooleanfalseWhether the associated input is disabled. Propagates disabled styling to the label.
descriptionstringDescription text displayed between the label and input.
descriptionIDstringID for the description element (use for aria-describedby on the input).
isOptionalbooleanfalseWhether the field is optional (mutually exclusive with isRequired).
isRequiredbooleanfalseWhether the field is required (mutually exclusive with isOptional).
labelIconIconTypeIcon to display before the label text. See astryx docs icons for valid semantic names.
labelTooltipstringTooltip text to display in an info icon at the end of the label.
status{type: 'warning' | 'error' | 'success', message?: string, messageID?: string}Status indicator with type and optional message. When message is set, displays a colored status box. messageID is for wiring aria-describedby on the input.
statusVariant'attached' | 'detached''attached'How the status message renders relative to the input. Attached overlaps the input border; detached floats below.
widthSizeValueWidth of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned. Prefer this over setting width via xstyle/className/style, which only size the inner control box. Without a width, a row can shrink the whole field below the natural width of its control, so filter bars shrink instead of overflowing.
refReact.Ref<HTMLDivElement>Ref forwarded to the root element.
xstyleStyleXStylesStyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value: not an inline style object like style={{}}.
classNamestringCSS class name(s) appended to the root element. Prefer xstyle for StyleX deduplication.
styleReact.CSSPropertiesInline styles applied to the root element. Takes priority over StyleX inline styles.

Usage guidance

  • Wrap custom controls, native inputs, or third-party widgets that need labeling, helper text, optional/required indicators, or validation status.
  • Always provide a label for accessibility, even if visually hidden with isLabelHidden.
  • Use inputID and descriptionID to connect the label and description to the inner control with htmlFor and aria-describedby.
  • Avoid: Nest Field around styled inputs such as TextInput, Typeahead, Select, DateInput, or TextArea; those components already render their own Field shell.
  • Avoid: Use the attached status variant on non-bordered controls such as sliders, switches, or checkboxes; use detached so the message does not overlap the control.
  • Avoid: Set both isOptional and isRequired on the same field.
  • Avoid: Hide the label without providing an alternative way for the user to understand the field purpose.

Filter bar

import {useState} from 'react';
import {HStack} from '@astryxdesign/core/HStack';
import {Icon} from '@astryxdesign/core/Icon';
import {Selector} from '@astryxdesign/core/Selector';
import {StackItem} from '@astryxdesign/core/Stack';
import {TextInput} from '@astryxdesign/core/TextInput';

// Every Field-based input (TextInput, Selector, DateInput, ...) can be shrunk
// by its row, so a filter bar never pushes the page sideways. Let the row wrap
// so that on a phone the search box keeps a full line and the selectors move
// below it; on wider screens everything stays on one line, with the search box
// taking the free space.
function OrdersFilterBar() {
  const [query, setQuery] = useState('');
  const [status, setStatus] = useState('Awaiting fulfillment');
  const [region, setRegion] = useState('All regions');
  const [sort, setSort] = useState('Newest first');
  return (
    <HStack gap={2} wrap="wrap" vAlign="center">
      <StackItem size="fill">
        <TextInput
          label="Search orders"
          isLabelHidden
          placeholder="Search by order or customer"
          startIcon={<Icon icon="search" />}
          value={query}
          onChange={setQuery}
        />
      </StackItem>
      <Selector
        label="Status"
        isLabelHidden
        options={['All statuses', 'Awaiting fulfillment', 'Shipped', 'Delivered', 'Returned']}
        value={status}
        onChange={setStatus}
      />
      <Selector
        label="Region"
        isLabelHidden
        options={['All regions', 'North America', 'Europe', 'Asia Pacific']}
        value={region}
        onChange={setRegion}
      />
      <Selector
        label="Sort"
        isLabelHidden
        options={['Newest first', 'Oldest first', 'Highest total']}
        value={sort}
        onChange={setSort}
      />
    </HStack>
  );
}

Wrap a custom control

function CustomSliderField() {
  return (
    <Field
      label="Confidence"
      inputID="confidence-slider"
      description="Choose how strict the review should be."
      descriptionID="confidence-help"
      status={{type: 'success', message: 'Recommended default'}}
      statusVariant="detached">
      <input
        id="confidence-slider"
        type="range"
        min={0}
        max={100}
        defaultValue={60}
        aria-describedby="confidence-help"
      />
    </Field>
  );
}

Theming API

{
  "targets": [
    {
      "className": "astryx-field",
      "visualProps": [
        "layout"
      ]
    },
    {
      "className": "astryx-field-label"
    },
    {
      "className": "astryx-field-status",
      "visualProps": [
        "type",
        "variant"
      ]
    },
    {
      "className": "astryx-input-status-icon",
      "visualProps": [
        "size",
        "status"
      ]
    },
    {
      "className": "astryx-input-clear-button"
    },
    {
      "className": "astryx-input-clear-icon"
    }
  ],
  "vars": [
    {
      "name": "--_field-radius",
      "description": "Border radius of input fields",
      "default": "var(--radius-element)",
      "private": true
    },
    {
      "name": "--_field-status-overlap",
      "description": "Amount an attached FieldStatus extends behind the lower half of the control. Set from the rendered control size.",
      "default": "calc(var(--size-element-md) / 2)",
      "private": true
    },
    {
      "name": "--_input-clear-hit-inset",
      "description": "Outset of the clear (✕) button's invisible hit area, applied to a ::after overlay. 0 on a fine pointer; negative on a coarse one, which grows the 20px button to the 24px touch target without changing what is drawn.",
      "default": "0px",
      "private": true
    },
    {
      "name": "--_input-clear-hit-content",
      "description": "Whether the clear (✕) button's invisible hit overlay exists. `none` on a fine pointer, so no ::after is generated and hover still reaches the glyph; `\"\"` on a coarse one, where the overlay provides the 24px touch target.",
      "default": "none",
      "private": true
    }
  ],
  "derived": [
    {
      "property": "borderRadius",
      "vars": [
        "--_field-radius"
      ]
    }
  ]
}

FieldLabel

FieldStatus

Anatomy

  • Label: Text identifying the field. Always rendered for accessibility, optionally hidden visually.
  • Description: Helper text between the label and input explaining what to enter.
  • Control slot: A custom, native, or third-party control that does not already render a field shell.
  • Status message: Inline validation feedback showing error, warning, or success with a message.
  • Optional/Required indicator: Badge next to the label showing whether the field is optional or required.
  • Label tooltip: Info icon at the end of the label with a tooltip explaining the field.

Field Label

Standalone label component with optional/required indicators and tooltip support.

PropTypeDefaultDescription
label *stringLabel text.
inputID *stringID of the input this label is for.
isLabelHiddenbooleanfalseVisually hide the label.
isDisabledbooleanfalseWhether the associated input is disabled.
isOptionalbooleanfalseShow "Optional" indicator.
isRequiredbooleanfalseShow "Required" indicator.
labelIconIconTypeIcon before the label text. See astryx docs icons for valid semantic names.
labelTooltipstringTooltip text for info icon at end of label.

Examples

  • Field Label: Standalone field labels demonstrating required, optional, tooltip, and icon variations.
  • Field: A form field wrapping a text input with a label, description, and validation status.
  • FieldLabel — Basic: Standalone labels with required and optional indicators and a helper description. Use when labeling a custom control that does not render its own label.
  • Field — Required & Optional: Required and optional field indicators side by side. Use isRequired on fields the user must fill in, and isOptional to clarify which fields can be skipped.
  • Field — Validation States: All three validation states: error, warning, and success. Use error for invalid input, warning for potential issues like reserved names, and success to confirm valid entries like API keys.
  • Field — Description: Fields with helper text below the label. Use descriptions to explain format requirements, constraints, or what happens with the data, like "At least 8 characters" or "We will send a confirmation link".