Files
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

84 lines
3.0 KiB
Markdown

# 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.
```tsx
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.
## Related
- [buttons.md](./buttons.md)
- [accessibility.md](./accessibility.md)
- [colors.md](./colors.md)