"use client"; import * as stylex from "@stylexjs/stylex"; import { type ComponentPropsWithoutRef, useId } from "react"; import { colors, fonts, radius, space } from "./tokens.stylex"; export type TextFieldProps = Omit< ComponentPropsWithoutRef<"input">, "className" | "style" | "size" > & { label: string; hint?: string; error?: string; }; export function TextField({ label, hint, error, id, "aria-describedby": describedBy, ...native }: TextFieldProps) { const generatedId = useId(); const fieldId = id ?? generatedId; const note = error ?? hint; const description = [describedBy, note ? `${fieldId}-note` : undefined].filter(Boolean).join(" ") || undefined; return (
{note ? ( {note} ) : null}
); } const styles = stylex.create({ field: { display: "grid", gap: space.s, width: "100%", minWidth: 0, fontFamily: fonts.body, textAlign: "start", }, label: { fontSize: 14, fontWeight: 700, color: colors.text }, input: { minWidth: 0, width: "100%", minHeight: 44, padding: space.m, fontFamily: fonts.body, fontSize: 16, lineHeight: 1.4, backgroundColor: colors.surface, boxShadow: "none", color: colors.text, borderRadius: radius.s, borderWidth: 1, borderStyle: "solid", borderColor: { default: colors.border, ":focus-visible": colors.focus }, // The outline sits on top of the border so focus reads as one line, not a ring around a ring. outline: { default: "none", ":focus-visible": `2px solid ${colors.focus}` }, outlineOffset: -1, opacity: { default: 1, ":disabled": 0.5 }, }, invalid: { borderColor: colors.danger, outlineColor: colors.danger }, note: { fontSize: 13, color: colors.textMuted, lineHeight: 1.4 }, error: { color: colors.danger }, });