All components
Input
Text fields with outline, subtle, and flushed variants that inherit your palette and radius.
123456789101112131415161718import { Input } from "#components/ui/base-ui/input.tsx";import { accentPalette } from "@stylexblocks/ui/themes/palettes.ts";
export function Example() { return ( <> {/* variant + size props resolve through the StyleX recipe and are exposed as data-variant / data-size attributes */} <Input variant="outline" size="md" placeholder="Email address" palette={accentPalette} />
{/* native props merge through mergeProps */} <Input variant="subtle" type="email" required aria-label="Email" />
{/* composition: the render prop replaces the underlying element */} <Input variant="flushed" render={<input inputMode="numeric" />} /> </> );}Variants
outline
subtle
flushed
Sizes
xs
sm
md
lg
xl
Install
1npx stylexblocks add inputSource
components/ui/base-ui/input.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175import { createComponent } from "#lib/base-ui/create-component.tsx";import { defineRecipe } from "#lib/recipe-props.ts";import { accentPalette } from "@stylexblocks/ui/themes/palettes.ts";import * as stylex from "@stylexjs/stylex";import { colorPalette } from '@stylexblocks/ui/color-palette.stylex.ts'import { semanticColors } from '@stylexblocks/ui/semantic-tokens/colors.stylex.ts'import { semanticRadii } from '@stylexblocks/ui/semantic-tokens/radii.stylex.ts'import { sizes } from '@stylexblocks/ui/tokens/sizes.stylex.ts'import { spacing } from '@stylexblocks/ui/tokens/spacing.stylex.ts'import { textStyles } from '@stylexblocks/ui/text-styles.ts'
const inputStyles = stylex.create({ base: { width: '100%', minWidth: 'var(--input-height)', outline: 'none', position: 'relative', appearance: 'none', textAlign: 'start', opacity: { default: null, ':is(:disabled, [disabled], [data-disabled])': 0.5, }, cursor: { default: null, ':is(:disabled, [disabled], [data-disabled])': 'not-allowed', }, height: 'var(--input-height)', '--focus-color': colorPalette.focusRing, '--error-color': semanticColors.borderError, '--focus-ring-color': { default: null, ':is([data-invalid], [aria-invalid="true"])': 'var(--error-color)', }, borderColor: { default: null, ':is([data-invalid], [aria-invalid="true"])': 'var(--error-color)', }, },})
const inputSizes = stylex.create({ xs: { borderRadius: semanticRadii.control, paddingInline: spacing._2, '--input-height': sizes.controlXs, }, sm: { borderRadius: semanticRadii.control, paddingInline: spacing._2_5, '--input-height': sizes.controlSm, }, md: { borderRadius: semanticRadii.control, paddingInline: spacing._3, '--input-height': sizes.controlMd, }, lg: { borderRadius: semanticRadii.control, paddingInline: spacing._4_5, '--input-height': sizes.controlLg, }, xl: { borderRadius: semanticRadii.control, paddingInline: spacing._6, '--input-height': sizes.controlXl, },})
const inputVariants = stylex.create({ outline: { backgroundColor: semanticColors.bg, borderWidth: '1px', borderColor: { default: semanticColors.border, ':is([data-invalid], [aria-invalid="true"])': 'var(--error-color)', ':hover': semanticColors.borderEmphasized, }, outlineWidth: { default: null, ':focus-visible': 'var(--focus-ring-width, 0)', }, outlineOffset: { default: null, ':focus-visible': '0px', }, outlineStyle: { default: null, ':focus-visible': 'solid', }, outlineColor: { default: null, ':focus-visible': colorPalette.focusRing, }, focusRingWidth: 0, }, subtle: { borderWidth: '1px', borderColor: { default: 'transparent', ':is([data-invalid], [aria-invalid="true"])': 'var(--error-color)', }, backgroundColor: semanticColors.bgMuted, outlineWidth: { default: null, ':focus-visible': 'var(--focus-ring-width, 0)', }, outlineOffset: { default: null, ':focus-visible': '0px', }, outlineStyle: { default: null, ':focus-visible': 'solid', }, outlineColor: { default: null, ':focus-visible': colorPalette.focusRing, }, }, flushed: { backgroundColor: 'transparent', borderBottomWidth: '1px', borderBottomColor: semanticColors.border, borderRadius: 0, paddingInline: 0, borderColor: { default: null, ':is([data-invalid], [aria-invalid="true"])': 'var(--error-color)', ':focus-visible': 'var(--focus-color)', }, boxShadow: { default: null, ':focus-visible': '0px 1px 0px 0px var(--focus-color)', }, },})
const inputTextStyles = { xs: textStyles.xs, sm: textStyles.sm, md: textStyles.sm, lg: textStyles.md, xl: textStyles.md,} as const
export type InputSize = keyof typeof inputSizes
export type InputVariant = keyof typeof inputVariants
/** * Input — recipe and component colocated in one file. Variant/size props * resolve mechanically from the axis maps (exposed as data-variant / * data-size); `extend` carries the per-size text styles. Composition via * `render`. */const inputRecipe = defineRecipe({ styles: inputStyles, axes: { variant: inputVariants, size: inputSizes }, defaultVariants: { variant: "outline", size: "md" }, extend: (v) => [inputTextStyles[v.size]],});
const StyledInput = createComponent("input", inputRecipe, "Input");
export function Input({ palette = accentPalette, ...props}: React.ComponentProps<typeof StyledInput>) { return <StyledInput palette={palette} {...props} />;}Input.displayName = "Input";
export type InputProps = React.ComponentProps<typeof Input>;