All components
Textarea
Multi-line text entry with the same outline, subtle, and flushed variants as Input.
1234567891011121314import { Textarea } from "#components/ui/base-ui/textarea.tsx";
export function Example() { return ( <> {/* variant + size props resolve through the StyleX recipe and are exposed as data-variant / data-size attributes */} <Textarea variant="outline" size="md" rows={3} placeholder="Your message" />
{/* native props merge through mergeProps */} <Textarea variant="subtle" required aria-label="Notes" /> </> );}Variants
outline
subtle
flushed
Sizes
sm
md
lg
Install
1npx stylexblocks add textareaSource
components/ui/base-ui/textarea.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168import * as stylex from "@stylexjs/stylex";import { createComponent } from "#lib/base-ui/create-component.tsx";import { defineRecipe } from "#lib/recipe-props.ts";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 { spacing } from '@stylexblocks/ui/tokens/spacing.stylex.ts'import { textStyles } from '@stylexblocks/ui/text-styles.ts'
const textareaStyles = stylex.create({ base: { width: '100%', minWidth: 0, 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', }, '--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 textareaSizes = stylex.create({ xs: { borderRadius: semanticRadii.control, paddingInline: spacing._2, paddingBlock: spacing._1, scrollPaddingBottom: 1, }, sm: { borderRadius: semanticRadii.control, paddingInline: spacing._2_5, paddingBlock: spacing._1_5, scrollPaddingBottom: 1.5, }, md: { borderRadius: semanticRadii.control, paddingInline: spacing._3, paddingBlock: spacing._1_5, scrollPaddingBottom: 1.5, }, lg: { borderRadius: semanticRadii.control, paddingInline: spacing._4, paddingBlock: spacing._2, scrollPaddingBottom: 2, }, xl: { borderRadius: semanticRadii.control, paddingInline: spacing._4_5, paddingBlock: spacing._3_5, scrollPaddingBottom: 3.5, },})
const textareaVariants = stylex.create({ outline: { backgroundColor: semanticColors.bg, borderWidth: '1px', borderColor: { default: semanticColors.border, ':is([data-invalid], [aria-invalid="true"])': 'var(--error-color)', }, 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 textareaTextStyles = { xs: textStyles.xs, sm: textStyles.sm, md: textStyles.sm, lg: textStyles.md, xl: textStyles.md,} as const
export type TextareaSize = keyof typeof textareaSizes
export type TextareaVariant = keyof typeof textareaVariants
/** * Textarea — 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 textareaRecipe = defineRecipe({ styles: textareaStyles, axes: { variant: textareaVariants, size: textareaSizes }, defaultVariants: { variant: "outline", size: "md" }, extend: (v) => [textareaTextStyles[v.size]],});
export const Textarea = createComponent("textarea", textareaRecipe, "Textarea");
export type TextareaProps = React.ComponentProps<typeof Textarea>;