Files
aragrp/docs/design-system/forms.md
T
ar.badieiandCursor c2376b3b9e
Build & Push Docker Image / build (push) Has been cancelled
feat: redesign landing page with brand system and section blocks
Refresh the homepage around new hero/services/roadmap sections, shared design tokens, and updated brand assets.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-23 17:00:55 +03:30

3.0 KiB

Forms

Form UI must use shared primitives so controls match buttons, cards, and focus rings.

Building blocks

Piece Import
Input @/components/ui/input
Textarea @/components/ui/textarea
Label @/components/ui/label
Checkbox / Radio / Switch / Select @/components/ui/*
Field layout @/components/ui/field (Field, FieldGroup, FieldSet, …)
Form (RHF) @/components/ui/form + react-hook-form + zod resolvers
Button submit @/components/ui/button

Input spec

From components/ui/input.tsx:

Property Value
Height h-9 (36px) default; align to buttons with h-[var(--btn-height-lg)] when paired with size="lg" submit
Radius rounded-md (--radius-md)
Border border-input
Background bg-background; dark: dark:bg-surface/60
Text text-base md:text-sm (16px on mobile avoids iOS zoom)
Padding px-3 py-1
Shadow shadow-xs
Transition duration-200 on color/border/shadow
Focus border-primary + ring-[3px] ring-primary/25
Disabled opacity-50, no pointer
Invalid aria-invalid → destructive border/ring

Field layout

  • Vertical (default): label above control, gap-3 within field; groups use gap-7.
  • Label → control spacing: prefer typeSpacing.labelToControl (gap-1.5) or Field defaults.
  • Checkbox/radio groups: tighter gap-3.
  • Errors: use text-destructive and wire aria-invalid on the control.
import { Field, FieldLabel, FieldDescription, FieldError } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";

<Field>
  <FieldLabel htmlFor="email">ایمیل</FieldLabel>
  <Input id="email" type="email" autoComplete="email" />
  <FieldDescription>برای پیگیری درخواست با شما تماس می‌گیریم.</FieldDescription>
  <FieldError>ایمیل معتبر نیست.</FieldError>
</Field>

<div className="flex flex-col gap-3 sm:flex-row">
  <Input type="email" className="h-[var(--btn-height-lg)]" placeholder="آدرس ایمیل شما" />
  <Button type="submit" size="lg">عضویت</Button>
</div>

Validation & a11y

  1. Associate every control with a visible <Label> / FieldLabel via htmlFor + id.
  2. Announce errors with aria-invalid and descriptive error text (not color alone).
  3. Required fields: use aria-required or native required plus clear copy.
  4. Keep touch targets ≥ 36px height (inputs h-9; prefer lg button pairing for marketing forms).
  5. Do not disable paste or obscure password managers.

Newsletter / compact patterns

Footer pattern: email input height matches Button size="lg" via --btn-height-lg.

Forbidden

  • Unstyled native inputs next to themed buttons.
  • outline-none without a visible focus replacement.
  • Hardcoded border colors (border-gray-300).
  • Mixing different input heights in the same form row without intent.