feat: redesign landing page with brand system and section blocks
Build & Push Docker Image / build (push) Has been cancelled
Build & Push Docker Image / build (push) Has been cancelled
Refresh the homepage around new hero/services/roadmap sections, shared design tokens, and updated brand assets. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
# Amin Rayan Design System
|
||||
|
||||
Single source of truth for layout, components, tokens, motion, responsive behavior, accessibility, and coding conventions across the Amin Rayan (aminrayan) Next.js site.
|
||||
|
||||
This documentation does **not** redesign the product. It codifies what already exists in code so every future UI change stays visually and technically consistent.
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
- Prevent ad-hoc containers, colors, spacing, and typography.
|
||||
- Make Shadcn / Tailark imports adapt to project tokens before shipping.
|
||||
- Give developers and Cursor Agents one place to look before writing UI.
|
||||
- Keep light/dark themes, RTL Persian readability, and accessibility as non-negotiable defaults.
|
||||
|
||||
---
|
||||
|
||||
## Source of truth (code)
|
||||
|
||||
| Concern | Canonical location |
|
||||
| --- | --- |
|
||||
| CSS tokens (color, radius, button metrics) | `app/globals.css` |
|
||||
| Layout container & section rhythm | `lib/layout.ts`, `components/container.tsx` |
|
||||
| Typography scale | `lib/typography.ts`, `components/ui/typography.tsx` |
|
||||
| Button system | `components/ui/button.tsx` |
|
||||
| Card system | `components/ui/card.tsx` |
|
||||
| Scroll motion | `lib/block-motion.ts`, `components/motion-reveal.tsx` |
|
||||
| Theme | `components/theme-provider.tsx` (`next-themes`, class strategy) |
|
||||
| Cursor enforcement | `.cursor/rules/*.mdc` |
|
||||
|
||||
When docs and code disagree, **update the docs to match code** (or fix code via an intentional design change)—never invent a second system.
|
||||
|
||||
---
|
||||
|
||||
## Documentation hierarchy
|
||||
|
||||
```
|
||||
docs/design-system/
|
||||
├── README.md ← you are here (entry point)
|
||||
├── layout.md ← page structure
|
||||
├── grid.md
|
||||
├── container.md
|
||||
├── spacing.md
|
||||
├── colors.md
|
||||
├── typography.md
|
||||
├── buttons.md
|
||||
├── forms.md
|
||||
├── cards.md
|
||||
├── navbar.md
|
||||
├── hero.md
|
||||
├── sections.md
|
||||
├── footer.md
|
||||
├── icons.md
|
||||
├── images.md
|
||||
├── motion.md
|
||||
├── gradients.md
|
||||
├── glassmorphism.md
|
||||
├── dark-mode.md
|
||||
├── responsive.md
|
||||
├── accessibility.md
|
||||
├── shadcn.md
|
||||
├── coding-rules.md
|
||||
└── quality-checklist.md ← gate before every UI PR
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Priority order
|
||||
|
||||
When two guidelines conflict, follow this order:
|
||||
|
||||
1. **Accessibility** (`accessibility.md`) — contrast, focus, keyboard, reduced motion, touch targets.
|
||||
2. **Layout & container** (`layout.md`, `container.md`, `grid.md`) — never invent a second content width.
|
||||
3. **Semantic tokens** (`colors.md`, `spacing.md`, `typography.md`) — never hardcode hex or arbitrary spacing.
|
||||
4. **Component systems** (`buttons.md`, `cards.md`, `forms.md`, …) — reuse existing primitives.
|
||||
5. **Visual effects** (`gradients.md`, `glassmorphism.md`, `motion.md`) — decorative only; never break layout.
|
||||
6. **Imported blocks** (`shadcn.md`) — adapt third-party UI to the above; never ship vendor styles as-is.
|
||||
|
||||
---
|
||||
|
||||
## How developers should use this
|
||||
|
||||
1. Before any UI task, open this README and the relevant topic docs.
|
||||
2. Reuse `Container`, `layout.*`, `type.*` / `<Typography>`, `Button`, and `Card`.
|
||||
3. Prefer semantic Tailwind classes (`bg-primary`, `text-muted-foreground`, `border-border`) over raw values.
|
||||
4. For new sections, copy the section shell pattern from `sections.md`.
|
||||
5. Run through `quality-checklist.md` before opening a PR.
|
||||
|
||||
### Quick imports
|
||||
|
||||
```tsx
|
||||
import { Container } from "@/components/container";
|
||||
import { layout } from "@/lib/layout";
|
||||
import { type, typeSpacing } from "@/lib/typography";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||
import { cn } from "@/lib/utils";
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How Cursor should follow this
|
||||
|
||||
Cursor Rules under `.cursor/rules/` enforce the same standards automatically:
|
||||
|
||||
| Rule file | Scope |
|
||||
| --- | --- |
|
||||
| `layout-container.mdc` | Containers & section width |
|
||||
| `frontend.mdc` | Tokens, reuse, dark mode, coding conventions |
|
||||
| `ui-design.mdc` | Visual consistency, spacing, motion, components |
|
||||
| `accessibility.mdc` | A11y non-negotiables |
|
||||
| `shadcn.mdc` | Adapting imported Shadcn/Tailark blocks |
|
||||
|
||||
Agents must:
|
||||
|
||||
- Read these docs when building or changing UI.
|
||||
- Never invent a second container, color palette, spacing scale, or button style.
|
||||
- Always support light and dark themes via semantic tokens.
|
||||
- Always preserve RTL-friendly typography and responsive behavior.
|
||||
|
||||
---
|
||||
|
||||
## Brand snapshot
|
||||
|
||||
| Token | Value | Role |
|
||||
| --- | --- | --- |
|
||||
| Primary | `#29a19c` | CTAs, focus ring, links |
|
||||
| Primary dark / secondary | `#044343` | Deep brand panels, secondary surfaces |
|
||||
| Neutral | `#222222` | Body text (light) |
|
||||
| Gray | `#999999` | Muted accents |
|
||||
| Light | `#e4e4e4` | Borders / muted fills (light) |
|
||||
| Font | IRANSansX | Site-wide Persian UI |
|
||||
|
||||
---
|
||||
|
||||
## Related skills (optional)
|
||||
|
||||
Project skills under `.cursor/skills/` (design-system, ui-styling, brand) can assist generation, but **this folder wins** for Amin Rayan product UI.
|
||||
@@ -0,0 +1,87 @@
|
||||
# Accessibility
|
||||
|
||||
Accessibility is priority #1 in the design system hierarchy. Visual polish never overrides perceivable, operable UI.
|
||||
|
||||
## Color contrast
|
||||
|
||||
- Body text (`foreground` on `background`) and muted text must remain readable in both themes.
|
||||
- Do not place `muted-foreground` on muted backgrounds without checking contrast.
|
||||
- Primary buttons use white label on `#29a19c`—do not lower primary opacity on labels.
|
||||
- Never convey meaning by color alone (pair with text/icon).
|
||||
|
||||
## Focus ring
|
||||
|
||||
Visible focus is mandatory for keyboard users.
|
||||
|
||||
| Control | Pattern |
|
||||
| --- | --- |
|
||||
| Button | `focus-visible:ring-[3px] focus-visible:ring-ring/40 focus-visible:border-ring` |
|
||||
| Input | `focus-visible:ring-[3px] focus-visible:ring-primary/25 focus-visible:border-primary` |
|
||||
| Generic | Prefer ring tokens; never `outline-none` without replacement |
|
||||
|
||||
Do not remove focus styles from cards/links that are interactive.
|
||||
|
||||
## Keyboard navigation
|
||||
|
||||
- All buttons, links, and controls reachable via Tab.
|
||||
- Menus / sheets / dialogs use Radix primitives (focus trap + Escape).
|
||||
- Maintain logical RTL tab order; avoid positive `tabIndex` except when necessary.
|
||||
- Icon-only controls need accessible names.
|
||||
|
||||
## ARIA expectations
|
||||
|
||||
| Case | Requirement |
|
||||
| --- | --- |
|
||||
| Icon button | `aria-label` |
|
||||
| Decorative icon/image | `aria-hidden` / empty `alt` |
|
||||
| Expandable | `aria-expanded` + controls id |
|
||||
| Dialogs / sheets | Titled (`SheetTitle`, `DialogTitle`) — `sr-only` if visually hidden |
|
||||
| Invalid fields | `aria-invalid` + error text |
|
||||
| Landmark nav | `aria-label` on primary `<nav>` |
|
||||
| Decorative sections | `aria-hidden` on `.section-decor` |
|
||||
|
||||
Prefer native semantics (`button`, `a`, `label`) before ARIA.
|
||||
|
||||
## Reduced motion
|
||||
|
||||
- Honor `prefers-reduced-motion`.
|
||||
- `MotionReveal` already disables motion.
|
||||
- New CSS animations need a reduced-motion override.
|
||||
- Do not autoplay motion-critical content.
|
||||
|
||||
## Touch targets
|
||||
|
||||
| Control | Minimum |
|
||||
| --- | --- |
|
||||
| Default button | 40×40 (`h-10`) |
|
||||
| Large button | 48×48 |
|
||||
| Icon button | 40×40 |
|
||||
| Compact button | 32×32 — avoid for primary mobile actions |
|
||||
| Input | ≥ 36px height (`h-9`); 48px when paired with lg submit |
|
||||
|
||||
## Forms
|
||||
|
||||
- Visible labels (not placeholder-only).
|
||||
- Error text associated with fields.
|
||||
- Preserve browser autofill / zoom-safe 16px text on mobile inputs.
|
||||
|
||||
## Images & media
|
||||
|
||||
- Meaningful `alt`.
|
||||
- Do not flash high-contrast animations.
|
||||
|
||||
## Checklist snippet
|
||||
|
||||
- [ ] Keyboard only can complete the flow
|
||||
- [ ] Focus visible on all interactive elements
|
||||
- [ ] Contrast OK in light and dark
|
||||
- [ ] Reduced motion OK
|
||||
- [ ] SR labels on icon buttons
|
||||
- [ ] No keyboard trap outside modals
|
||||
|
||||
## Related
|
||||
|
||||
- [forms.md](./forms.md)
|
||||
- [motion.md](./motion.md)
|
||||
- [dark-mode.md](./dark-mode.md)
|
||||
- Cursor rule: `.cursor/rules/accessibility.mdc`
|
||||
@@ -0,0 +1,114 @@
|
||||
# Buttons
|
||||
|
||||
Every interactive CTA uses `@/components/ui/button`. Do not create parallel button styles, raw `<button className="bg-…">` marketing CTAs, or one-off rounded pills.
|
||||
|
||||
Canonical implementation: `components/ui/button.tsx` + CSS tokens in `app/globals.css`.
|
||||
|
||||
## Tokens
|
||||
|
||||
| CSS variable | Value | Role |
|
||||
| --- | --- | --- |
|
||||
| `--btn-radius` | `var(--radius)` → `0.625rem` | Corner radius |
|
||||
| `--btn-height-sm` | `2rem` (32px) | Compact / cards |
|
||||
| `--btn-height-md` | `2.5rem` (40px) | Default |
|
||||
| `--btn-height-lg` | `3rem` (48px) | Hero / marketing |
|
||||
| `--btn-px-sm` | `0.75rem` | Horizontal padding sm |
|
||||
| `--btn-px-md` | `1.25rem` | Horizontal padding md |
|
||||
| `--btn-px-lg` | `1.5rem` | Horizontal padding lg |
|
||||
| `--btn-gap-sm/md/lg` | `0.375–0.5rem` | Icon ↔ label gap |
|
||||
|
||||
## Variants
|
||||
|
||||
| Variant | Prop | Visual | When to use |
|
||||
| --- | --- | --- | --- |
|
||||
| Primary | `default` | Solid `primary`, white label, soft shadow | Main conversion CTA |
|
||||
| Secondary | `secondary` | Transparent + border | Supporting action |
|
||||
| Outline | `outline` | Alias of secondary (Shadcn compat) | Imported blocks |
|
||||
| Ghost | `ghost` | No border, accent wash on hover | Nav / tertiary |
|
||||
| Destructive | `destructive` | Danger fill | Delete / irreversible |
|
||||
| Link | `link` | Text link, underline on hover | Inline actions |
|
||||
|
||||
## Surfaces
|
||||
|
||||
| Surface | Prop | Use |
|
||||
| --- | --- | --- |
|
||||
| Default | `surface="default"` | Normal page canvas |
|
||||
| Brand | `surface="brand"` | Teal / dark marketing panels (inverse contrast) |
|
||||
|
||||
On `brand` surfaces:
|
||||
|
||||
- Primary → white fill, dark text
|
||||
- Secondary/outline → white border, white text
|
||||
- Ghost → white text, white/10 hover
|
||||
|
||||
## Sizes
|
||||
|
||||
| Size | Prop | Height | Text | Icon size |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Small | `sm` | 32px | `text-sm` | 0.875rem |
|
||||
| Medium | `default` | 40px | `text-sm` | 1rem |
|
||||
| Large | `lg` | 48px | `text-base` | 1.25rem |
|
||||
| Icon sm | `icon-sm` | 32×32 | — | 0.875rem |
|
||||
| Icon | `icon` | 40×40 | — | 1rem |
|
||||
| Icon lg | `icon-lg` | 48×48 | — | 1.25rem |
|
||||
|
||||
## States
|
||||
|
||||
| State | Behavior |
|
||||
| --- | --- |
|
||||
| Hover | Primary → `primary-hover` + `shadow-glow-primary`; secondary → border primary tint + accent fill + `shadow-glow-sm` |
|
||||
| Active | `active:scale-[0.98]` + darker fill |
|
||||
| Focus | `focus-visible:ring-[3px] ring-ring/40` + `border-ring` |
|
||||
| Disabled | `opacity-50`, no pointer, no shadow |
|
||||
| Invalid | Destructive ring/border via `aria-invalid` |
|
||||
|
||||
## Motion
|
||||
|
||||
- Transition: `duration-200 ease-out`
|
||||
- Properties: color, background, border, box-shadow, opacity, transform
|
||||
- Optional micro-lift on header CTA: `hover:-translate-y-px` (sparingly)
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Link from "next/link";
|
||||
import { ChevronLeft } from "lucide-react";
|
||||
|
||||
<Button size="lg">درخواست مشاوره</Button>
|
||||
|
||||
<Button variant="secondary" size="default">مشاهده نمونه کارها</Button>
|
||||
|
||||
<Button variant="ghost" size="icon" aria-label="باز کردن منو">
|
||||
<Menu className="size-5" />
|
||||
</Button>
|
||||
|
||||
<Button asChild size="lg">
|
||||
<Link href="/contact-us">
|
||||
ادامه
|
||||
<ChevronLeft className="size-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
{/* On dark teal CTA band */}
|
||||
<Button surface="brand" size="lg">شروع کنید</Button>
|
||||
```
|
||||
|
||||
## Hierarchy in a section
|
||||
|
||||
1. At most **one** primary button per viewport section.
|
||||
2. Secondary/outline for the alternate path.
|
||||
3. Ghost for low-emphasis actions.
|
||||
4. Do not place two equal primary buttons side by side.
|
||||
|
||||
## Forbidden
|
||||
|
||||
- Custom `rounded-full` CTA capsules that ignore `--btn-radius`.
|
||||
- Hardcoded `bg-[#29a19c]` buttons.
|
||||
- Re-implementing button styles in WordPress/HTML blocks without mapping to tokens (WP buttons are themed in `globals.css` to approximate primary).
|
||||
|
||||
## Related
|
||||
|
||||
- [colors.md](./colors.md)
|
||||
- [motion.md](./motion.md)
|
||||
- [forms.md](./forms.md)
|
||||
@@ -0,0 +1,84 @@
|
||||
# Cards
|
||||
|
||||
Canonical card: `@/components/ui/card`.
|
||||
|
||||
Use cards for **interactive or grouped content units** (settings panels, post previews, pricing cells). Do not wrap every section in a card. Do not put cards in the hero.
|
||||
|
||||
## Anatomy
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardAction,
|
||||
} from "@/components/ui/card";
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>عنوان</CardTitle>
|
||||
<CardDescription>توضیح کوتاه</CardDescription>
|
||||
<CardAction>{/* optional */}</CardAction>
|
||||
</CardHeader>
|
||||
<CardContent>…</CardContent>
|
||||
<CardFooter>…</CardFooter>
|
||||
</Card>
|
||||
```
|
||||
|
||||
## Spec
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Background | `bg-card` |
|
||||
| Text | `text-card-foreground` |
|
||||
| Radius | `rounded-xl` (≈ `--radius-xl`) |
|
||||
| Border | `border border-border` |
|
||||
| Shadow | `shadow-glow-sm` → hover `shadow-glow-primary` (dark hover `shadow-glow-lg`) |
|
||||
| Padding | Vertical `py-6`; header/content/footer `px-6` |
|
||||
| Internal stack | `flex flex-col gap-6` |
|
||||
| Title type | `type["card-title"]` |
|
||||
| Description type | `type["card-desc"]` |
|
||||
| Transition | `duration-200` on border + shadow |
|
||||
|
||||
## Hover
|
||||
|
||||
| Mode | Behavior |
|
||||
| --- | --- |
|
||||
| Light | `hover:border-primary/30 hover:shadow-glow-primary` |
|
||||
| Dark | `dark:hover:border-primary/40 dark:hover:shadow-glow-lg` |
|
||||
|
||||
Hover is subtle—cards should not jump (`translate`) or scale aggressively. Timeline roadmap cards are a special case with their own CSS in `globals.css`.
|
||||
|
||||
## Dark mode
|
||||
|
||||
Cards use semantic `card` tokens (`#1c2424` in dark). Do not force `bg-white` on cards.
|
||||
|
||||
## Spacing with grids
|
||||
|
||||
```tsx
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{items.map((item) => (
|
||||
<Card key={item.id}>…</Card>
|
||||
))}
|
||||
</div>
|
||||
```
|
||||
|
||||
## Blog / media cards
|
||||
|
||||
Blog surfaces may use `rounded-blog` (`--radius-blog: 0.9375rem`) on images/figures. Prefer shared `Card` for structured panels; media-heavy tiles may compose `Card` + `Image` with `object-cover` and consistent aspect ratios ([images.md](./images.md)).
|
||||
|
||||
## Forbidden
|
||||
|
||||
- Nested cards inside cards for decoration.
|
||||
- Heavy multi-layer drop shadows.
|
||||
- Random `rounded-2xl` / `rounded-md` overrides without reason.
|
||||
- Cards as the only way to create section background (use `bg-section-*` instead).
|
||||
|
||||
## Related
|
||||
|
||||
- [colors.md](./colors.md)
|
||||
- [spacing.md](./spacing.md)
|
||||
- [glassmorphism.md](./glassmorphism.md)
|
||||
@@ -0,0 +1,93 @@
|
||||
# Coding Rules
|
||||
|
||||
Conventions for UI code in this Next.js (App Router) + Tailwind v4 + Shadcn codebase.
|
||||
|
||||
## Stack assumptions
|
||||
|
||||
- React 19 + Next.js 16 App Router
|
||||
- Tailwind CSS v4 (`@import "tailwindcss"` in `globals.css`)
|
||||
- CVA + `cn()` (`clsx` + `tailwind-merge`)
|
||||
- Radix-based Shadcn primitives in `components/ui/`
|
||||
- Motion library: `motion/react`
|
||||
- Themes: `next-themes` class strategy
|
||||
- Locale: Persian RTL by default
|
||||
|
||||
## Always
|
||||
|
||||
1. Follow `docs/design-system/*` and `.cursor/rules/*`.
|
||||
2. Reuse design tokens (CSS variables + Tailwind semantic colors).
|
||||
3. Use `<Container>` / `layout.*` for page sections.
|
||||
4. Use `type.*` or `<Typography>` for text styles.
|
||||
5. Use shared `Button` and `Card` components.
|
||||
6. Support light and dark themes without hardcoding surfaces.
|
||||
7. Preserve responsive behavior (mobile first).
|
||||
8. Preserve accessibility (focus, labels, reduced motion).
|
||||
9. Prefer semantic HTML and Radix patterns already in the repo.
|
||||
10. Keep components focused; extract when a pattern repeats twice+.
|
||||
|
||||
## Never
|
||||
|
||||
1. Create another content container / max-width system.
|
||||
2. Hardcode colors (`#29a19c`, `rgb(…)`, `text-gray-700`) in UI.
|
||||
3. Invent spacing outside the scale.
|
||||
4. Duplicate Button/Card/Input styles.
|
||||
5. Ship unadapted Shadcn/Tailark blocks.
|
||||
6. Introduce inconsistent radii/shadows/hover timings.
|
||||
7. Animate layout properties when transform suffices.
|
||||
8. Commit secrets or environment files.
|
||||
9. Add heavy dependencies for trivial UI.
|
||||
10. Bypass ESLint without cause.
|
||||
|
||||
## File organization
|
||||
|
||||
| Kind | Location |
|
||||
| --- | --- |
|
||||
| Page routes | `app/**` |
|
||||
| Marketing sections | `components/*.tsx` |
|
||||
| Primitives | `components/ui/*` |
|
||||
| Layout tokens | `lib/layout.ts` |
|
||||
| Typography tokens | `lib/typography.ts` |
|
||||
| Global CSS / theme | `app/globals.css` |
|
||||
|
||||
Name section files descriptively (`services-14.tsx`) or by domain (`footer.tsx`). Avoid `NewSection2.tsx`.
|
||||
|
||||
## Class composition
|
||||
|
||||
```tsx
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
className={cn(
|
||||
"section-shell bg-section-canvas",
|
||||
layout.sectionY,
|
||||
className
|
||||
)}
|
||||
```
|
||||
|
||||
- Put conditional classes in `cn`.
|
||||
- Do not string-concatenate Tailwind classes manually.
|
||||
- Prefer extending variants via CVA for reusable primitives.
|
||||
|
||||
## Imports
|
||||
|
||||
Use `@/` alias. Keep import groups tidy: external → `@/components` → `@/lib` → local.
|
||||
|
||||
## Client vs server
|
||||
|
||||
- Default Server Components.
|
||||
- Add `"use client"` only for hooks, browser APIs, or motion/theme interactivity.
|
||||
- Keep presentational children server-friendly when possible.
|
||||
|
||||
## State & data
|
||||
|
||||
- Follow existing form patterns (`react-hook-form` + zod).
|
||||
- Do not fetch in deeply nested presentational components if a parent already owns data.
|
||||
|
||||
## Comments
|
||||
|
||||
- Comment only non-obvious intent (animation thresholds, RTL quirks, token constraints).
|
||||
- Do not narrate obvious JSX.
|
||||
|
||||
## Related
|
||||
|
||||
- [shadcn.md](./shadcn.md)
|
||||
- [quality-checklist.md](./quality-checklist.md)
|
||||
@@ -0,0 +1,79 @@
|
||||
# Colors
|
||||
|
||||
Never hardcode hex/rgb in components. Use semantic CSS variables exposed as Tailwind theme colors.
|
||||
|
||||
Canonical definitions: `app/globals.css` (`:root` and `.dark`).
|
||||
|
||||
## Brand primitives
|
||||
|
||||
| CSS variable | Light value | Role |
|
||||
| --- | --- | --- |
|
||||
| `--brand-primary` | `#29a19c` | Brand teal |
|
||||
| `--brand-primary-dark` | `#044343` | Deep teal |
|
||||
| `--brand-neutral` | `#222222` | Near-black neutral |
|
||||
| `--brand-gray` | `#999999` | Mid gray |
|
||||
| `--brand-light` | `#e4e4e4` | Light gray |
|
||||
| `--brand-primary-hover` | `#32b5af` | Primary hover |
|
||||
| `--brand-primary-active` | `#218a86` | Primary active |
|
||||
| `--brand-glow` | `rgb(41 161 156 / 0.12)` | Soft glow |
|
||||
| `--brand-highlight` | mix of primary + white | Timeline / highlight accents |
|
||||
|
||||
Tailwind aliases: `brand`, `brand-dark`, `brand-neutral`, `brand-gray`, `brand-light`, `glow`.
|
||||
|
||||
## Semantic palette (prefer these in UI)
|
||||
|
||||
| Token | Tailwind | Light | Dark | Use |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Background | `bg-background` | `#ffffff` | `#141818` | Page canvas |
|
||||
| Foreground | `text-foreground` | `#222222` | `#f4f4f4` | Primary text |
|
||||
| Card | `bg-card` / `text-card-foreground` | white / `#222` | `#1c2424` / `#f4f4f4` | Cards, elevated surfaces |
|
||||
| Surface | `bg-surface` / `text-surface-foreground` | `#f7f7f7` | `#1a2222` | Soft bands, inputs (dark) |
|
||||
| Primary | `bg-primary` / `text-primary-foreground` | `#29a19c` / white | same | Main CTA |
|
||||
| Primary hover | `bg-primary-hover` | `#32b5af` | same | Hover |
|
||||
| Primary active | `bg-primary-active` | `#218a86` | same | Pressed |
|
||||
| Secondary | `bg-secondary` / `text-secondary-foreground` | `#044343` / white | `#243535` / `#e4e4e4` | Deep brand / secondary panels |
|
||||
| Muted | `bg-muted` / `text-muted-foreground` | `#e4e4e4` / `#5c5c5c` | `#243030` / `#999999` | Subtle fills, secondary text |
|
||||
| Accent | `bg-accent` / `text-accent-foreground` | teal-tinted white / `#044343` | teal mix / `#e4e4e4` | Hover washes, chips |
|
||||
| Destructive | `bg-destructive` / `text-destructive-foreground` | `#c44747` / white | `#e06a6a` / white | Errors / danger |
|
||||
| Border | `border-border` | `#e4e4e4` | white @ 12% | Dividers, outlines |
|
||||
| Input | `border-input` | `#e4e4e4` | white @ 14% | Form controls |
|
||||
| Ring | `ring-ring` | `#29a19c` | `#29a19c` | Focus rings |
|
||||
| Popover | `bg-popover` | white | `#1c2424` | Menus, dropdowns |
|
||||
|
||||
## Semantic intent map
|
||||
|
||||
| Intent | Tokens |
|
||||
| --- | --- |
|
||||
| Page background | `background` |
|
||||
| Primary text | `foreground` |
|
||||
| Secondary text | `muted-foreground` |
|
||||
| Interactive primary | `primary` → `primary-hover` → `primary-active` |
|
||||
| Borders | `border` |
|
||||
| Focus | `ring` + `border-ring` / `border-primary` |
|
||||
| Disabled | lower opacity on control (`opacity-50`) — do not invent a gray hex |
|
||||
| Danger | `destructive` |
|
||||
| Charts | `chart-1` … `chart-5` |
|
||||
|
||||
## Hover / focus / disabled
|
||||
|
||||
| State | Pattern |
|
||||
| --- | --- |
|
||||
| Hover (primary button) | `hover:bg-primary-hover` |
|
||||
| Active (primary button) | `active:bg-primary-active` + slight `scale-[0.98]` |
|
||||
| Focus visible | `focus-visible:ring-[3px] focus-visible:ring-ring/40` (buttons) or `ring-primary/25` (inputs) |
|
||||
| Disabled | `disabled:opacity-50 disabled:pointer-events-none` |
|
||||
| Invalid | `aria-invalid:border-destructive` + destructive ring |
|
||||
|
||||
## Rules
|
||||
|
||||
1. **Never** use raw `#29a19c`, `#044343`, etc. in JSX/className. Use `primary`, `brand`, or CSS variables.
|
||||
2. Prefer semantic names (`primary`, `muted-foreground`) over brand primitives in app UI.
|
||||
3. Dark mode must work by swapping CSS variables—not by separate hard-coded dark hex in components.
|
||||
4. Selection color is already themed in `globals.css` (`::selection`).
|
||||
5. Glow utilities: `bg-glow-primary`, `shadow-glow-primary` — see [gradients.md](./gradients.md).
|
||||
|
||||
## Related
|
||||
|
||||
- [dark-mode.md](./dark-mode.md)
|
||||
- [buttons.md](./buttons.md)
|
||||
- [accessibility.md](./accessibility.md)
|
||||
@@ -0,0 +1,64 @@
|
||||
# Container
|
||||
|
||||
The site has **one** content container. All sections, imported blocks, and future features must use it.
|
||||
|
||||
## API
|
||||
|
||||
```tsx
|
||||
import { Container } from "@/components/container";
|
||||
import { layout, containerClassName } from "@/lib/layout";
|
||||
```
|
||||
|
||||
| Export | What it does |
|
||||
| --- | --- |
|
||||
| `<Container>` | Renders `div` with `layout.container` |
|
||||
| `layout.container` | Class string for rare cases without the component |
|
||||
| `containerClassName(extra?)` | Merges extras via `cn` |
|
||||
|
||||
## Spec
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Display | Block, full width of parent |
|
||||
| Max width | `76.8rem` (1228.8px) — 20% wider than former `max-w-5xl` |
|
||||
| Horizontal padding | `1.5rem` (`px-6`) at all breakpoints |
|
||||
| Centering | `mx-auto` |
|
||||
| Vertical padding | **None** — vertical rhythm belongs on the `<section>` |
|
||||
|
||||
## Full-bleed vs constrained
|
||||
|
||||
```tsx
|
||||
{/* Background spans viewport; content stays aligned */}
|
||||
<section className={cn("section-shell bg-gradient-cta", layout.sectionY)}>
|
||||
<Container className="section-content">
|
||||
…
|
||||
</Container>
|
||||
</section>
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- Backgrounds, borders, and decorative washes may be full viewport width.
|
||||
- Text, forms, card grids, and CTAs stay inside `<Container>`.
|
||||
- Edge-to-edge imagery (hero photos, marquees) may break out **only** when the design intentionally requires it; any overlaid copy still uses `Container`.
|
||||
|
||||
## Replacing imported containers
|
||||
|
||||
When adapting Shadcn / Tailark / marketing blocks:
|
||||
|
||||
1. Delete their `container`, `mx-auto max-w-*`, and conflicting `px-*`.
|
||||
2. Wrap content with `<Container>` (or apply `layout.container`).
|
||||
3. Move vertical padding to `layout.sectionY` on the outer `<section>`.
|
||||
4. Keep inner `max-w-*` only on prose columns.
|
||||
|
||||
## Do not
|
||||
|
||||
- Create `ContainerSm`, `ContainerWide`, or page-specific max-widths.
|
||||
- Use `px-4` / `px-8` as section gutters (always `px-6` via the shared token).
|
||||
- Apply `layout.container` twice (nested containers).
|
||||
|
||||
## Related
|
||||
|
||||
- [layout.md](./layout.md)
|
||||
- [shadcn.md](./shadcn.md)
|
||||
- Cursor rule: `.cursor/rules/layout-container.mdc`
|
||||
@@ -0,0 +1,52 @@
|
||||
# Dark Mode
|
||||
|
||||
Dark mode is a first-class theme, not an invert filter. It is a deep teal-neutral palette defined under `.dark` in `app/globals.css`.
|
||||
|
||||
## Mechanism
|
||||
|
||||
| Piece | Detail |
|
||||
| --- | --- |
|
||||
| Strategy | `class` on `html` via `next-themes` |
|
||||
| Provider | `components/theme-provider.tsx` |
|
||||
| Default | `light` |
|
||||
| System | `enableSystem` |
|
||||
| Variant | `@custom-variant dark (&:is(.dark *));` |
|
||||
| Toggle | `components/theme-toggle.tsx` |
|
||||
|
||||
## Palette intent
|
||||
|
||||
| Role | Light | Dark |
|
||||
| --- | --- | --- |
|
||||
| Canvas | `#ffffff` | `#141818` |
|
||||
| Card / popover | `#ffffff` | `#1c2424` |
|
||||
| Surface | `#f7f7f7` | `#1a2222` |
|
||||
| Primary | `#29a19c` (unchanged) | `#29a19c` |
|
||||
| Secondary | `#044343` | `#243535` |
|
||||
| Borders | `#e4e4e4` | white @ ~12% alpha |
|
||||
| Muted text | `#5c5c5c` | `#999999` |
|
||||
|
||||
Primary brand teal stays constant for recognition; surrounding neutrals shift.
|
||||
|
||||
## Implementation rules
|
||||
|
||||
1. Style with semantic tokens only (`bg-background`, `text-muted-foreground`, `border-border`, …).
|
||||
2. Use `dark:` variants only when semantic tokens are insufficient (e.g. `dark:hover:shadow-glow-primary`, header glass tweaks).
|
||||
3. Never gate entire components behind duplicated light/dark markup trees unless media differs (`next/image` theme backgrounds).
|
||||
4. Test both themes before shipping UI.
|
||||
5. Charts and decorative strokes should use `chart-*` or primary mixes that remain visible on dark surfaces.
|
||||
|
||||
## Transitions
|
||||
|
||||
Body background/color transitions use `0.3s ease`. Do not disable theme transitions globally (`disableTransitionOnChange={false}`).
|
||||
|
||||
## Forbidden
|
||||
|
||||
- `bg-white text-black` hardcodes.
|
||||
- `dark:bg-black` overrides that fight the teal-neutral system.
|
||||
- Assuming pure inversion of light colors will look correct.
|
||||
|
||||
## Related
|
||||
|
||||
- [colors.md](./colors.md)
|
||||
- [accessibility.md](./accessibility.md)
|
||||
- [gradients.md](./gradients.md)
|
||||
@@ -0,0 +1,58 @@
|
||||
# Footer
|
||||
|
||||
Canonical footer: `components/footer.tsx` (`FooterSection`).
|
||||
|
||||
## Structure
|
||||
|
||||
1. **Newsletter band** — headline + short copy + email form.
|
||||
2. **Link columns** — site maps (pages, services, contact, legal).
|
||||
3. **Brand / social / legal meta** — logo mark, social icon buttons, copyright.
|
||||
|
||||
## Layout
|
||||
|
||||
```tsx
|
||||
<footer className={cn("section-shell bg-section-muted", layout.sectionY)}>
|
||||
<div className="section-decor" aria-hidden>…</div>
|
||||
<Container className="section-content">
|
||||
{/* newsletter */}
|
||||
{/* link grid */}
|
||||
{/* bottom bar */}
|
||||
</Container>
|
||||
</footer>
|
||||
```
|
||||
|
||||
| Region | Grid / spacing |
|
||||
| --- | --- |
|
||||
| Newsletter | `grid-cols-1 lg:grid-cols-2 gap-10`, bottom border, `mb-14 pb-14` |
|
||||
| Links | `grid-cols-2 sm:grid-cols-4 gap-x-6 gap-y-10`, `py-10 lg:py-16` |
|
||||
| Column titles | `type.h4` + `mb-5` |
|
||||
| Links | `type.link` + `space-y-4` |
|
||||
|
||||
## Form
|
||||
|
||||
- `Input` height aligned to `Button size="lg"` via `h-[var(--btn-height-lg)]`.
|
||||
- Stack vertically on mobile (`flex-col`), horizontal from `sm`.
|
||||
|
||||
## Social icons
|
||||
|
||||
- Use `Button variant="ghost" size="icon"` with accessible `aria-label`.
|
||||
- Icons from `lucide-react`.
|
||||
|
||||
## Visual
|
||||
|
||||
- Background: `bg-section-muted` (not a hard gray box).
|
||||
- Optional top wash gradient in `.section-decor` using primary at ≤ 4% opacity.
|
||||
- Borders: semantic `border-border`.
|
||||
|
||||
## Rules
|
||||
|
||||
1. Stay on the shared `Container`—footer columns must align with header/content.
|
||||
2. Do not introduce a second footer component for marketing pages.
|
||||
3. Keep link lists scannable; avoid paragraph walls.
|
||||
4. Dark mode comes from tokens automatically.
|
||||
|
||||
## Related
|
||||
|
||||
- [sections.md](./sections.md)
|
||||
- [forms.md](./forms.md)
|
||||
- [icons.md](./icons.md)
|
||||
@@ -0,0 +1,83 @@
|
||||
# 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)
|
||||
@@ -0,0 +1,78 @@
|
||||
# Glassmorphism
|
||||
|
||||
Glass is used sparingly for **floating chrome** (header, menus, sheets, blog tables)—not as a general card style.
|
||||
|
||||
## When to use
|
||||
|
||||
| Allowed | Not allowed |
|
||||
| --- | --- |
|
||||
| Scrolled header shell | Every content card |
|
||||
| Nav dropdown / mobile sheet | Full page backgrounds |
|
||||
| Blog table header treatments | Text-heavy panels that hurt readability |
|
||||
|
||||
## Canonical recipe (header scrolled)
|
||||
|
||||
```txt
|
||||
border-border/50
|
||||
bg-background/75
|
||||
supports-[backdrop-filter]:bg-background/65
|
||||
backdrop-blur-xl
|
||||
backdrop-saturate-150
|
||||
shadow-sm
|
||||
rounded-2xl
|
||||
```
|
||||
|
||||
Mobile sheet:
|
||||
|
||||
```txt
|
||||
bg-background/95
|
||||
backdrop-blur-xl
|
||||
backdrop-saturate-150
|
||||
shadow-xl
|
||||
border-border/50
|
||||
```
|
||||
|
||||
Dropdown:
|
||||
|
||||
```txt
|
||||
bg-popover/95
|
||||
backdrop-blur-xl
|
||||
shadow-md
|
||||
border-border/50
|
||||
rounded-xl
|
||||
```
|
||||
|
||||
## Spec ranges
|
||||
|
||||
| Property | Range |
|
||||
| --- | --- |
|
||||
| Blur | `backdrop-blur-md` (12px) to `backdrop-blur-xl` (24px) |
|
||||
| Fill opacity | 65%–95% of `background` / `popover` |
|
||||
| Border | `border-border/40`–`/50` or translucent mixes |
|
||||
| Saturation | `backdrop-saturate-150` for chrome |
|
||||
|
||||
## Readability rules
|
||||
|
||||
1. Text on glass must still meet contrast—prefer `foreground` / `muted-foreground`, not low-alpha white text on light glass.
|
||||
2. Provide opaque fallbacks when `backdrop-filter` is unsupported (`supports-[backdrop-filter]:…` pattern).
|
||||
3. Do not place long paragraphs on heavy blur without a solid scrim.
|
||||
|
||||
## Dark mode
|
||||
|
||||
Glass uses the same semantic tokens; dark backgrounds already sit on `#141818` / `#1c2424`. Do not switch to pure `bg-white/10` hacks unless matching an existing pattern (timeline idle cards are a special dark UI).
|
||||
|
||||
## Motion
|
||||
|
||||
Blur/background changes on the header animate with `duration-300 ease-out`. Avoid animating blur on scroll for large content regions (expensive).
|
||||
|
||||
## Forbidden
|
||||
|
||||
- Frosted cards stacked across an entire landing page.
|
||||
- Extra noise textures + glass + glow simultaneously.
|
||||
- Glass without border or shadow that disappears on light backgrounds.
|
||||
|
||||
## Related
|
||||
|
||||
- [navbar.md](./navbar.md)
|
||||
- [gradients.md](./gradients.md)
|
||||
- [cards.md](./cards.md)
|
||||
@@ -0,0 +1,67 @@
|
||||
# Gradients
|
||||
|
||||
Gradients create continuous atmosphere across the page. They must feel like one canvas—not disconnected colorful patches.
|
||||
|
||||
Canonical utilities live in `app/globals.css` `@layer utilities`.
|
||||
|
||||
## Allowed utilities
|
||||
|
||||
| Class | Role |
|
||||
| --- | --- |
|
||||
| `bg-section-canvas` | Default section continuity (very soft primary tint) |
|
||||
| `bg-section-muted` | Alternate muted band |
|
||||
| `bg-gradient-hero` | Hero radial + vertical wash |
|
||||
| `bg-gradient-featured` | Featured band (corner radials + wash) |
|
||||
| `bg-gradient-cta` | Strong 135° brand CTA panel (`brand-primary-dark` → `brand-primary`) |
|
||||
| `bg-glow-primary` | Soft radial glow using `--brand-glow` |
|
||||
| `shadow-glow-sm` | Neon rim for primary buttons / resting cards |
|
||||
| `shadow-glow-primary` | Mid neon for card hover / button hover |
|
||||
| `shadow-glow-lg` | Stronger neon (dark hover / media hover) |
|
||||
| `shadow-glow-media` | Neon + depth for image frames |
|
||||
| `media-frame` | Shared image wrapper: surface border + media neon |
|
||||
|
||||
## Opacity limits
|
||||
|
||||
| Effect | Light | Dark |
|
||||
| --- | --- | --- |
|
||||
| Section tint washes | ~3–6% primary mixed into background | ~4–6% |
|
||||
| Hero radial glow | ~0.12 alpha | ~0.10 alpha |
|
||||
| Featured radials | ~0.08–0.10 | ~0.08 + dark brand mix |
|
||||
| Footer top wash | ~0.03–0.04 | ~0.03 |
|
||||
| Brand glow token | `0.12` | `0.10` |
|
||||
|
||||
If a glow is obvious as a “blob,” it is too strong.
|
||||
|
||||
## Layer ordering
|
||||
|
||||
Inside a section:
|
||||
|
||||
1. Section background utility (`bg-section-*` / gradient).
|
||||
2. `.section-decor` absolute layer (glows, grids).
|
||||
3. `.section-content` real UI.
|
||||
|
||||
Never place glows above content (`z-index`) or with pointer events.
|
||||
|
||||
## Glow usage
|
||||
|
||||
- Spotlights behind headings or cards: `bg-glow-primary` on a blurred circle in `.section-decor`.
|
||||
- Interactive emphasis: `shadow-glow-sm` → `shadow-glow-primary` / `shadow-glow-lg` on buttons and cards.
|
||||
- Image frames: prefer `media-frame` (or `shadow-glow-media`) over plain `shadow-surface-*`.
|
||||
- Timeline active nodes may use highlight mixes—keep confined to that component.
|
||||
|
||||
## CTA panels
|
||||
|
||||
Use `bg-gradient-cta` for conversion bands. Pair with `Button surface="brand"` for correct contrast.
|
||||
|
||||
## Forbidden
|
||||
|
||||
- One-off `bg-gradient-to-r from-purple-500 to-pink-500` (or any non-brand palette).
|
||||
- Hard rectangular gradient blocks that do not blend into neighbors.
|
||||
- Multiple competing radials in one small card.
|
||||
- Fixed-position gradient overlays spanning the whole site.
|
||||
|
||||
## Related
|
||||
|
||||
- [sections.md](./sections.md)
|
||||
- [glassmorphism.md](./glassmorphism.md)
|
||||
- [dark-mode.md](./dark-mode.md)
|
||||
@@ -0,0 +1,89 @@
|
||||
# Grid
|
||||
|
||||
Grid rules for marketing sections, card layouts, and footers. Grids always live **inside** `<Container>`.
|
||||
|
||||
## Defaults
|
||||
|
||||
| Concern | Standard |
|
||||
| --- | --- |
|
||||
| Framework | Tailwind CSS Grid / Flexbox |
|
||||
| Gap scale | Spacing tokens only (`gap-4`, `gap-6`, `gap-8`, `gap-10` …) — see [spacing.md](./spacing.md) |
|
||||
| Columns | Mobile-first: 1 → 2 → 3/4 as viewport grows |
|
||||
| Alignment | Start-aligned in RTL; use logical properties (`ms-`, `me-`, `ps-`, `pe-`) when offsetting |
|
||||
|
||||
## Common patterns
|
||||
|
||||
### Two-column feature / split
|
||||
|
||||
```tsx
|
||||
<div className="grid grid-cols-1 gap-10 lg:grid-cols-2 lg:gap-16">
|
||||
<div>…copy…</div>
|
||||
<div>…media…</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Card grid (services / posts)
|
||||
|
||||
```tsx
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{items.map(…)}
|
||||
</div>
|
||||
```
|
||||
|
||||
### Footer link columns
|
||||
|
||||
```tsx
|
||||
<nav className="grid grid-cols-2 gap-x-6 gap-y-10 sm:grid-cols-4">
|
||||
…
|
||||
</nav>
|
||||
```
|
||||
|
||||
### Newsletter / CTA split
|
||||
|
||||
```tsx
|
||||
<div className="grid grid-cols-1 gap-10 lg:grid-cols-2">
|
||||
…
|
||||
</div>
|
||||
```
|
||||
|
||||
## Breakpoint guidance
|
||||
|
||||
| Viewport | Typical columns |
|
||||
| --- | --- |
|
||||
| < 640px (`default`) | 1 |
|
||||
| `sm` ≥ 640px | 2 for dense cards |
|
||||
| `md` ≥ 768px | 2–3 |
|
||||
| `lg` ≥ 1024px | 2–4 marketing grids |
|
||||
| `xl` ≥ 1280px | Same as `lg`; prefer more whitespace over more columns |
|
||||
|
||||
Exact breakpoints: [responsive.md](./responsive.md).
|
||||
|
||||
## Gap rules
|
||||
|
||||
Prefer these gaps:
|
||||
|
||||
| Context | Gap |
|
||||
| --- | --- |
|
||||
| Dense UI (forms, chips) | `gap-2` / `gap-3` (8 / 12) |
|
||||
| Card grids | `gap-6` (24) |
|
||||
| Section split layouts | `gap-10` / `gap-16` (40 / 64) |
|
||||
| Footer columns | `gap-x-6 gap-y-10` |
|
||||
|
||||
Do not use arbitrary gaps (`gap-[13px]`, `gap-[1.1rem]`).
|
||||
|
||||
## Alignment & RTL
|
||||
|
||||
- Site is Persian / RTL. Prefer `start` / `end` over `left` / `right`.
|
||||
- For asymmetric hero layouts, mirror with logical classes (`lg:ms-auto`, not `lg:ml-auto` unless intentional LTR islands).
|
||||
|
||||
## Forbidden
|
||||
|
||||
- CSS Grid that sets `width` wider than the container without clipping.
|
||||
- Mixing Bootstrap / custom 12-column frameworks.
|
||||
- Absolute-positioned “fake grids” for primary content.
|
||||
|
||||
## Related
|
||||
|
||||
- [layout.md](./layout.md)
|
||||
- [spacing.md](./spacing.md)
|
||||
- [cards.md](./cards.md)
|
||||
@@ -0,0 +1,85 @@
|
||||
# Hero
|
||||
|
||||
Hero guidelines for marketing landings. Reference implementations: `components/hero-section.tsx`, `hero-50.tsx`, `hero-213.tsx`.
|
||||
|
||||
This design system does **not** invent a new hero—it constrains future heroes to the same layout, token, and motion language.
|
||||
|
||||
## First-viewport budget
|
||||
|
||||
Prefer:
|
||||
|
||||
1. Brand / logo presence (via global header).
|
||||
2. One headline (`type.display` or `type.h1`).
|
||||
3. One short supporting sentence.
|
||||
4. One CTA group (primary + optional secondary).
|
||||
5. One dominant visual (background or product image).
|
||||
|
||||
Avoid packing stats, schedules, address blocks, or promo chips into the first viewport.
|
||||
|
||||
## Layout
|
||||
|
||||
```tsx
|
||||
<section className="bg-background"> {/* or bg-gradient-hero */}
|
||||
<div className="relative pt-24 md:pt-36">
|
||||
<Container>
|
||||
<div className={typeSpacing.heroStack}>
|
||||
<h1 className={type.display}>…</h1>
|
||||
<p className={cn(type["body-lg"], "text-muted-foreground")}>…</p>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<Button size="lg">…</Button>
|
||||
<Button size="lg" variant="secondary">…</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- Clearance under fixed header: at least `pt-24` / `md:pt-36` (or `layout.sectionTopWithHeader` on simpler pages).
|
||||
- Content width: site `<Container>` — do not invent `max-w-7xl` shells for new work (legacy heroes being migrated should move to `Container`).
|
||||
- Full-bleed **background** images are encouraged; foreground copy stays constrained.
|
||||
- No detached floating badges/stickers on top of hero media.
|
||||
|
||||
## Backgrounds
|
||||
|
||||
Allowed:
|
||||
|
||||
- `bg-gradient-hero` utility.
|
||||
- Theme-aware `next/image` backgrounds (`dark:block` / `hidden dark:…`).
|
||||
- Soft radial washes inside a clipped absolute layer.
|
||||
|
||||
See [gradients.md](./gradients.md). Decorative layers must not cause horizontal scroll.
|
||||
|
||||
## Motion
|
||||
|
||||
- Entrance: opacity + translateY (+ optional blur) via `AnimatedGroup` / `TextEffect` / `MotionReveal`.
|
||||
- Prefer spring or `blockEase` from `lib/block-motion.ts`.
|
||||
- Respect `prefers-reduced-motion` (MotionReveal already does).
|
||||
- Do not animate layout width/height for entrance—use transform/opacity.
|
||||
|
||||
## CTA rules
|
||||
|
||||
- Primary = `Button` default `size="lg"`.
|
||||
- Secondary = `variant="secondary"` or `outline`.
|
||||
- On dark brand panels use `surface="brand"`.
|
||||
|
||||
## Imagery
|
||||
|
||||
- Provide explicit `width` / `height` or `fill` + sized parent to avoid CLS.
|
||||
- Use `sizes` appropriate to viewport (`100vw` for full-bleed backgrounds).
|
||||
- `object-cover` for atmospheric photos; keep focal point sensible for RTL crops.
|
||||
|
||||
## Forbidden
|
||||
|
||||
- Card-wrapped heroes.
|
||||
- Inset rounded media as the only hero treatment when a full-bleed treatment is the site norm.
|
||||
- Extra containers that misalign with the header.
|
||||
|
||||
## Related
|
||||
|
||||
- [navbar.md](./navbar.md)
|
||||
- [buttons.md](./buttons.md)
|
||||
- [images.md](./images.md)
|
||||
- [motion.md](./motion.md)
|
||||
@@ -0,0 +1,55 @@
|
||||
# Icons
|
||||
|
||||
## Libraries
|
||||
|
||||
| Library | Use |
|
||||
| --- | --- |
|
||||
| `lucide-react` | Default UI icons (nav, actions, social) |
|
||||
| `@iconify/react` | Occasional extended sets when Lucide lacks a glyph |
|
||||
|
||||
Do not mix many icon libraries in one view. Prefer Lucide for consistency.
|
||||
|
||||
## Sizing
|
||||
|
||||
| Context | Size |
|
||||
| --- | --- |
|
||||
| Inline with `text-sm` | `size-4` (16px) |
|
||||
| Buttons (default) | Controlled by `--btn-icon-size` (0.875–1.25rem) |
|
||||
| Icon buttons | Match button box; glyph ~20px (`size-5`) for menu |
|
||||
| Cards / features | `size-5` / `size-6` |
|
||||
| Decorative empty states | `size-8`–`size-10` max |
|
||||
|
||||
Use Tailwind `size-*` from the spacing scale. Avoid arbitrary `w-[18px]`.
|
||||
|
||||
## Color
|
||||
|
||||
- Inherit from parent text (`currentColor`) whenever possible.
|
||||
- Semantic: `text-muted-foreground`, `text-primary`, `text-destructive`.
|
||||
- Never hardcode icon hex fills in JSX.
|
||||
|
||||
## Alignment
|
||||
|
||||
- Pair icons with labels using flex + `gap-2` / button gaps.
|
||||
- Decorative icons: `aria-hidden`.
|
||||
- Standalone icon buttons: mandatory `aria-label` (or visible text).
|
||||
|
||||
```tsx
|
||||
<Button variant="ghost" size="icon" aria-label="باز کردن منو">
|
||||
<Menu aria-hidden className="size-5" />
|
||||
</Button>
|
||||
```
|
||||
|
||||
## Stroke
|
||||
|
||||
Lucide defaults are fine. Do not globally remount icons with custom stroke widths that fight the set.
|
||||
|
||||
## Forbidden
|
||||
|
||||
- Emoji as UI icons.
|
||||
- PNG icons for simple glyphs.
|
||||
- Different visual weights in the same toolbar.
|
||||
|
||||
## Related
|
||||
|
||||
- [buttons.md](./buttons.md)
|
||||
- [accessibility.md](./accessibility.md)
|
||||
@@ -0,0 +1,71 @@
|
||||
# Images
|
||||
|
||||
## Principles
|
||||
|
||||
1. Prefer `next/image` for all meaningful raster images.
|
||||
2. Reserve space (width/height or sized `fill` parent) to prevent layout shift.
|
||||
3. Lazy-load below-the-fold images; prioritize true LCP hero assets.
|
||||
4. Stay within brand radius tokens.
|
||||
|
||||
## Aspect ratios
|
||||
|
||||
| Use | Ratio | Notes |
|
||||
| --- | --- | --- |
|
||||
| Hero atmosphere | fluid / full bleed | `object-cover`, tall crops OK |
|
||||
| Blog / content figures | intrinsic | `w-full h-auto` in `.blog-content` |
|
||||
| Cards / thumbnails | `aspect-video` or `aspect-[4/3]` | Keep consistent within a grid |
|
||||
| Avatars | `1:1` | `rounded-full` |
|
||||
| Logos (marquee) | intrinsic height | Use `LogoItem` patterns |
|
||||
|
||||
Use `AspectRatio` from `@/components/ui/aspect-ratio` when the box must stay stable before load.
|
||||
|
||||
## Object-fit
|
||||
|
||||
| Value | When |
|
||||
| --- | --- |
|
||||
| `object-cover` | Photos filling a fixed frame |
|
||||
| `object-contain` | Logos, diagrams that must not crop |
|
||||
| `object-center` / logical positions | Adjust focal point; prefer `object-center` unless art-directed |
|
||||
|
||||
## Radius
|
||||
|
||||
| Token | Class | Use |
|
||||
| --- | --- | --- |
|
||||
| Default | `rounded-md` / `rounded-xl` | UI media |
|
||||
| Blog | `rounded-blog` | WP images / blockquotes |
|
||||
| Pill | `rounded-full` | Avatars only |
|
||||
|
||||
Marketing image frames use `media-frame` (surface border + brand neon via `--elevation-glow-media`). Prefer that over ad-hoc `border` + `shadow-surface-*` on homepage media.
|
||||
|
||||
`.wp-block-image` already applies `rounded-blog overflow-hidden shadow-sm`.
|
||||
|
||||
## Lazy loading & placeholders
|
||||
|
||||
- Default `next/image` lazy loading for non-priority images.
|
||||
- Set `priority` only on the primary LCP image.
|
||||
- Always pass meaningful `sizes` (e.g. `100vw`, `(max-width: 768px) 100vw, 33vw`).
|
||||
- Blur placeholder: use when a `blurDataURL` exists; otherwise rely on solid surface behind the image.
|
||||
|
||||
## Alt text
|
||||
|
||||
- Informative images: concise Persian (or English in `/en`) alt describing content.
|
||||
- Decorative images: `alt=""` and/or `aria-hidden` on wrapper.
|
||||
- Never put SEO keyword stuffing in alt.
|
||||
|
||||
## Floating / decorative images
|
||||
|
||||
- Absolute decorative photos must live inside a clipped section (`section-shell` / `overflow-hidden`).
|
||||
- Do not float images that overlap interactive controls.
|
||||
- Avoid random rotated polaroid stacks unless an existing block already defines that pattern.
|
||||
|
||||
## Forbidden
|
||||
|
||||
- Unsized `<img>` causing CLS.
|
||||
- Inconsistent radii in the same card grid.
|
||||
- Full-quality 4K backgrounds without `sizes` / quality tuning.
|
||||
|
||||
## Related
|
||||
|
||||
- [hero.md](./hero.md)
|
||||
- [cards.md](./cards.md)
|
||||
- [responsive.md](./responsive.md)
|
||||
@@ -0,0 +1,95 @@
|
||||
# Layout
|
||||
|
||||
Page structure rules for Amin Rayan. Every marketing page, blog surface, and product block must share the same content column, gutters, and vertical rhythm.
|
||||
|
||||
## Principles
|
||||
|
||||
1. One content width for the whole site.
|
||||
2. Full-bleed backgrounds are allowed; content is never full-bleed unless intentionally edge-to-edge media.
|
||||
3. Section vertical rhythm comes from `layout.sectionY`—not one-off `py-*` values.
|
||||
4. Decorative layers never participate in document flow or cause horizontal overflow.
|
||||
5. The fixed header defines safe top offsets and scroll margins.
|
||||
|
||||
## Canonical tokens
|
||||
|
||||
Defined in `lib/layout.ts`:
|
||||
|
||||
| Token | Class / value | Use |
|
||||
| --- | --- | --- |
|
||||
| `layout.gutter` | `px-6` (24px) | Horizontal padding |
|
||||
| `layout.maxWidth` | `max-w-[76.8rem]` (1228.8px) | Content cap |
|
||||
| `layout.container` | `mx-auto w-full max-w-[76.8rem] px-6` | Standard shell |
|
||||
| `layout.sectionY` | `py-16 md:py-20` | Section vertical padding (adjacent gap = 8rem / md:10rem) |
|
||||
| `layout.sectionTopWithHeader` | `pt-28 pb-16 md:pt-32 md:pb-20` | First section under fixed header |
|
||||
| `layout.scrollMargin` | `scroll-mt-24` | In-page anchors |
|
||||
|
||||
Component wrapper: `<Container>` from `@/components/container`.
|
||||
|
||||
## Section anatomy
|
||||
|
||||
```tsx
|
||||
<section className={cn("section-shell bg-section-canvas", layout.sectionY)}>
|
||||
<div className="section-decor" aria-hidden>
|
||||
{/* optional absolute glow / wash — never in flow */}
|
||||
</div>
|
||||
<Container className="section-content">
|
||||
{/* real content */}
|
||||
</Container>
|
||||
</section>
|
||||
```
|
||||
|
||||
Utility classes (from `app/globals.css`):
|
||||
|
||||
| Class | Role |
|
||||
| --- | --- |
|
||||
| `.section-shell` | `relative isolation-isolate overflow-x-clip` |
|
||||
| `.section-decor` | Absolute decorative layer, `pointer-events-none`, `z-0` |
|
||||
| `.section-content` | Content above decor, `z-1` |
|
||||
|
||||
## Alignment rules
|
||||
|
||||
- Left and right edges of text/CTA columns must align with header content and footer content (same `Container`).
|
||||
- Do not nest another `max-w-*` shell around an entire section. Inner measures (`max-w-xl`, `max-w-2xl`, `max-w-3xl`) are allowed only on **text blocks** for readability.
|
||||
- Grids live **inside** `Container`. Never place a grid that escapes the container unless it is intentionally full-bleed media.
|
||||
|
||||
## Safe areas
|
||||
|
||||
- Header uses `pt-[env(safe-area-inset-top,0px)]` for notched devices.
|
||||
- First content under the fixed header: use `layout.sectionTopWithHeader` or explicit `pt-24 md:pt-36` (hero) so titles are not obscured.
|
||||
- Anchor targets (`id="…"`) must include `layout.scrollMargin` (`scroll-mt-24`).
|
||||
|
||||
## Overflow rules
|
||||
|
||||
- `html` and `body` use `overflow-x: clip`.
|
||||
- Sections use `section-shell` / `overflow-x: clip` so glows cannot create horizontal scroll.
|
||||
- Never use `position: fixed` decorative blobs for section atmosphere—keep them inside `.section-decor`.
|
||||
- Prefer `transform` / absolute positioning for decorations; do not widen the document with large negative margins without clipping.
|
||||
|
||||
## Forbidden patterns
|
||||
|
||||
```tsx
|
||||
// ❌ Second container width
|
||||
<div className="mx-auto max-w-5xl px-4">…
|
||||
|
||||
// ❌ Section without shared rhythm
|
||||
<section className="py-12 px-8">…
|
||||
|
||||
// ❌ Full-bleed text without Container
|
||||
<section className="w-full px-4">
|
||||
<h2>…</h2>
|
||||
</section>
|
||||
|
||||
// ✅ Correct
|
||||
<section className={cn("section-shell bg-section-muted", layout.sectionY)}>
|
||||
<Container>
|
||||
<h2 className={type.h2}>…</h2>
|
||||
</Container>
|
||||
</section>
|
||||
```
|
||||
|
||||
## Related docs
|
||||
|
||||
- [container.md](./container.md)
|
||||
- [grid.md](./grid.md)
|
||||
- [sections.md](./sections.md)
|
||||
- [responsive.md](./responsive.md)
|
||||
@@ -0,0 +1,85 @@
|
||||
# Motion
|
||||
|
||||
Motion should clarify hierarchy and presence—not decorate every pixel.
|
||||
|
||||
Canonical scroll motion: `lib/block-motion.ts` + `components/motion-reveal.tsx`.
|
||||
Header / micro-interactions use CSS transitions documented below.
|
||||
|
||||
## Duration scale
|
||||
|
||||
| ms | Tailwind / CSS | Use |
|
||||
| --- | --- | --- |
|
||||
| 150 | `duration-150` | Micro feedback (rarely needed) |
|
||||
| 200 | `duration-200` | Buttons, inputs, cards, links (default UI) |
|
||||
| 250 | `duration-250` | Base heading/paragraph color transitions |
|
||||
| 300 | `duration-300` | Header chrome, nav, sheets, larger reveals |
|
||||
| 500 | `duration-500` | Timeline / emphasis state changes, section reveals |
|
||||
|
||||
Do not invent `duration-[350ms]` or multi-second loops for marketing UI.
|
||||
|
||||
## Easing
|
||||
|
||||
| Token | Value | Use |
|
||||
| --- | --- | --- |
|
||||
| UI default | `ease-out` | Hovers, chrome |
|
||||
| Block reveal | `blockEase = [0.22, 1, 0.36, 1]` | Scroll entrances |
|
||||
| Spring (hero) | `type: "spring", bounce: ~0.3` | Occasional hero text only |
|
||||
|
||||
## Hover transitions
|
||||
|
||||
Prefer transitioning:
|
||||
|
||||
- `color`, `background-color`, `border-color`
|
||||
- `box-shadow`, `opacity`
|
||||
- `transform` (`translate`, `scale`)
|
||||
|
||||
**Never animate layout** (`top`/`left`/`width`/`height`/`margin`) when `transform` or opacity can achieve the effect.
|
||||
|
||||
Buttons already include `active:scale-[0.98]` and `duration-200`.
|
||||
|
||||
## Scroll animations
|
||||
|
||||
```tsx
|
||||
import { MotionReveal, MotionRevealItem } from "@/components/motion-reveal";
|
||||
|
||||
<MotionReveal stagger>
|
||||
<MotionRevealItem>…</MotionRevealItem>
|
||||
<MotionRevealItem>…</MotionRevealItem>
|
||||
</MotionReveal>
|
||||
```
|
||||
|
||||
Defaults from `block-motion.ts`:
|
||||
|
||||
| Option | Value |
|
||||
| --- | --- |
|
||||
| Duration | 0.5s |
|
||||
| Offset | `y: 24` → `0` |
|
||||
| Stagger | 0.08s children |
|
||||
| Viewport | `once: true`, `amount: 0.2`, bottom margin `-10%` |
|
||||
|
||||
Keep stagger counts small (≤ ~8).
|
||||
|
||||
## Reduced motion
|
||||
|
||||
- `MotionReveal` short-circuits to static markup when `useReducedMotion()` is true.
|
||||
- Timeline and header animations disable under `@media (prefers-reduced-motion: reduce)` in `globals.css`.
|
||||
- New animations **must** honor reduced motion.
|
||||
|
||||
## Performance rules
|
||||
|
||||
1. Prefer `transform` + `opacity`.
|
||||
2. Avoid animating `blur` on large surfaces in loops (hero entrance blur is OK once).
|
||||
3. Do not use `will-change` everywhere—only hot paths (e.g. timeline progress).
|
||||
4. No infinite attention-grabbing pulses on primary CTAs.
|
||||
|
||||
## Forbidden
|
||||
|
||||
- Layout thrashing animations.
|
||||
- Autoplaying large Lottie stacks on every section.
|
||||
- Ignoring `prefers-reduced-motion`.
|
||||
|
||||
## Related
|
||||
|
||||
- [navbar.md](./navbar.md)
|
||||
- [accessibility.md](./accessibility.md)
|
||||
- [buttons.md](./buttons.md)
|
||||
@@ -0,0 +1,87 @@
|
||||
# Navbar
|
||||
|
||||
Canonical header: `HeroHeader` in `components/header.tsx`.
|
||||
|
||||
Do not create a second site header. Extend the existing component when navigation changes.
|
||||
|
||||
## Structure
|
||||
|
||||
- Fixed to the top (`fixed inset-x-0 top-0 z-50`).
|
||||
- Content constrained with `<Container>`.
|
||||
- Safe area: `pt-[env(safe-area-inset-top,0px)]`.
|
||||
- RTL reading order: Logo → primary links → utilities (English, theme, CTA, menu).
|
||||
|
||||
## Heights
|
||||
|
||||
| State | Height |
|
||||
| --- | --- |
|
||||
| Default (top) | `h-14` mobile / `h-16` desktop |
|
||||
| Scrolled glass | `h-12` |
|
||||
|
||||
Logo scales slightly on scroll (`scale-[0.97]`).
|
||||
|
||||
## States
|
||||
|
||||
### Default (scrollY ≤ 24px)
|
||||
|
||||
- Transparent background, no border, no blur.
|
||||
- Full height bar flush with top (`mt-0`).
|
||||
|
||||
### Sticky / scrolled (scrollY > 24px)
|
||||
|
||||
Threshold: `SCROLL_THRESHOLD = 24`.
|
||||
|
||||
- Same vertical position as default — **no** top margin / vertical shift of chrome.
|
||||
- Glass: `bg-background/75` (or `/65` when backdrop-filter supported).
|
||||
- Blur: `backdrop-blur-xl backdrop-saturate-150`.
|
||||
- Border: `border-border/50`.
|
||||
- Shadow: `shadow-sm`.
|
||||
- Radius: `rounded-2xl`.
|
||||
- Width: identical to unscrolled — never a narrower/wider max-width.
|
||||
- Horizontal inset only: `px-4 md:px-6` so logo / nav / CTA move inward and the glass rim shows.
|
||||
|
||||
Transition: `duration-300 ease-out` across padding, background, border, shadow, backdrop-filter, radius.
|
||||
|
||||
## Desktop (≥ xl)
|
||||
|
||||
- Horizontal `NavigationMenu` with underline affordance on links.
|
||||
- Dropdown panels: `rounded-xl`, `bg-popover/95`, `backdrop-blur-xl`, `shadow-md`.
|
||||
- Primary CTA: `Button size="sm"`.
|
||||
- English link + `ThemeToggle` visible.
|
||||
|
||||
## Tablet / mobile (< xl)
|
||||
|
||||
- Hamburger `Button variant="ghost" size="icon"`.
|
||||
- `Sheet` from the right (`side="right"`), width `min(100%, 20rem)`.
|
||||
- Sheet glass: `bg-background/95 backdrop-blur-xl shadow-xl`.
|
||||
- Accordion for grouped links (e.g. خدمات).
|
||||
- Staggered enter animation (`header-nav-in`, ~320ms) — disabled under `prefers-reduced-motion`.
|
||||
|
||||
## Padding & alignment
|
||||
|
||||
- Always inside site `Container` (`layout.container`: `max-w-[76.8rem]` + `px-6`).
|
||||
- **Top of page:** inner padding `px-0` — logo and CTA align with section content edges.
|
||||
- **Scrolled:** glass fills the container; inner padding `px-4 md:px-6` insets chrome toward the center so the glass rim shows.
|
||||
- Do not use a different `max-w-*` for the sticky state.
|
||||
- Nav link padding ≈ `px-2 py-1.5`; mobile rows `px-3 py-2.5`.
|
||||
- Keep CTA and icon buttons on the trailing side; do not center-float disconnected controls.
|
||||
|
||||
## Accessibility
|
||||
|
||||
- `nav` has `aria-label="ناوبری اصلی"`.
|
||||
- Mobile toggle: `aria-expanded`, `aria-controls="mobile-nav"`, `aria-label`.
|
||||
- Sheet title available to SR (`sr-only`).
|
||||
- Active routes reflected visually (underline / foreground) without relying on color alone when possible.
|
||||
|
||||
## Forbidden
|
||||
|
||||
- Second fixed header on inner pages.
|
||||
- Opaque solid bar that ignores glass tokens when scrolled.
|
||||
- Breaking out of `Container` alignment (e.g. `max-w-[68rem]` or `max-w-[90rem]` shells).
|
||||
- Animating layout width with JS reflow—use transform/opacity/height transitions already defined.
|
||||
|
||||
## Related
|
||||
|
||||
- [glassmorphism.md](./glassmorphism.md)
|
||||
- [motion.md](./motion.md)
|
||||
- [container.md](./container.md)
|
||||
@@ -0,0 +1,64 @@
|
||||
# Quality Checklist
|
||||
|
||||
Run this checklist before finishing any UI task or opening a PR that touches presentation.
|
||||
|
||||
## Layout
|
||||
|
||||
- [ ] ✓ Container consistency — uses `<Container>` / `layout.container`
|
||||
- [ ] ✓ Grid consistency — grids sit inside the container; gaps from the spacing scale
|
||||
- [ ] ✓ Section rhythm — `layout.sectionY` (or documented hero top padding)
|
||||
- [ ] ✓ Alignment — matches header/footer edges
|
||||
- [ ] ✓ No overflow — no accidental horizontal scroll; decor clipped
|
||||
|
||||
## Tokens
|
||||
|
||||
- [ ] ✓ Same spacing scale — no arbitrary `px-[N]` / `gap-[N]`
|
||||
- [ ] ✓ Same typography — `type.*` / `<Typography>`
|
||||
- [ ] ✓ Semantic colors only — no hardcoded hex
|
||||
- [ ] ✓ Same radius — `--radius` scale / `rounded-blog` where intended
|
||||
- [ ] ✓ Same shadows — `shadow-xs/sm/md` or `shadow-glow-primary`
|
||||
|
||||
## Components
|
||||
|
||||
- [ ] ✓ Same button system — `@/components/ui/button`
|
||||
- [ ] ✓ Same cards — `@/components/ui/card` (when a card is warranted)
|
||||
- [ ] ✓ No duplicated primitives — no second Button/Card/Input
|
||||
- [ ] ✓ Forms use shared Input/Field/Label patterns
|
||||
|
||||
## Interaction
|
||||
|
||||
- [ ] ✓ Same hover behavior — matches neighboring components
|
||||
- [ ] ✓ Same transition timing — 200ms UI / 300ms chrome / 500ms reveals
|
||||
- [ ] ✓ Transform/opacity for motion — no layout animation abuse
|
||||
- [ ] ✓ Reduced motion honored
|
||||
|
||||
## Theme & responsive
|
||||
|
||||
- [ ] ✓ Same dark mode — verified with theme toggle
|
||||
- [ ] ✓ Responsive — mobile / tablet / desktop checked
|
||||
- [ ] ✓ Touch targets adequate
|
||||
|
||||
## Accessibility
|
||||
|
||||
- [ ] ✓ Focus rings visible
|
||||
- [ ] ✓ Keyboard operable
|
||||
- [ ] ✓ Icon buttons labeled
|
||||
- [ ] ✓ Contrast acceptable in light and dark
|
||||
- [ ] ✓ Images have correct alt / decorative handling
|
||||
|
||||
## Stability
|
||||
|
||||
- [ ] ✓ No layout shift — images sized; fonts not causing CLS spikes
|
||||
- [ ] ✓ No duplicated styles — no copy-pasted one-off CSS competing with tokens
|
||||
- [ ] ✓ Imported Shadcn/Tailark blocks fully adapted ([shadcn.md](./shadcn.md))
|
||||
|
||||
## Final gate
|
||||
|
||||
If any item fails, fix before merge. When unsure, re-read [README.md](./README.md) priority order:
|
||||
|
||||
1. Accessibility
|
||||
2. Layout & container
|
||||
3. Semantic tokens
|
||||
4. Component systems
|
||||
5. Visual effects
|
||||
6. Imported blocks
|
||||
@@ -0,0 +1,67 @@
|
||||
# Responsive
|
||||
|
||||
Mobile-first layout using Tailwind default breakpoints.
|
||||
|
||||
## Breakpoints
|
||||
|
||||
| Name | Min width | Typical device |
|
||||
| --- | --- | --- |
|
||||
| default | 0 | Mobile phones |
|
||||
| `sm` | 640px | Large phones / small tablets |
|
||||
| `md` | 768px | Tablets |
|
||||
| `lg` | 1024px | Laptops |
|
||||
| `xl` | 1280px | Desktops |
|
||||
| `2xl` | 1536px | Large desktops |
|
||||
|
||||
Project usage notes:
|
||||
|
||||
- **Header desktop nav** appears at `xl` (not `lg`) to protect crowded Persian labels.
|
||||
- **Section padding** steps at `md` (`py-16` → `md:py-20`).
|
||||
- **Hero top padding** steps at `md` (`pt-24` → `md:pt-36`).
|
||||
|
||||
## Layout adaptations
|
||||
|
||||
| Concern | Mobile | Desktop |
|
||||
| --- | --- | --- |
|
||||
| Container gutter | `px-6` | same |
|
||||
| Max width | `76.8rem` | same |
|
||||
| Section Y | 64px | 80px |
|
||||
| Grids | 1 col | 2–4 cols |
|
||||
| Nav | Sheet menu | Inline `NavigationMenu` |
|
||||
| Forms (newsletter) | Stacked | Horizontal `sm:flex-row` |
|
||||
|
||||
## Typography scaling
|
||||
|
||||
Scale variants already encode responsive steps (e.g. `h2`: `text-2xl md:text-3xl lg:text-4xl`). Prefer those over manual overrides.
|
||||
|
||||
Inputs use `text-base md:text-sm` to prevent iOS focus zoom.
|
||||
|
||||
## Spacing adaptations
|
||||
|
||||
- Increase gaps at larger breakpoints (`gap-10 lg:gap-16`) using the spacing scale.
|
||||
- Do not shrink container padding on mobile below `px-6`.
|
||||
- Collapse multi-column footers to `grid-cols-2` before `sm:grid-cols-4`.
|
||||
|
||||
## Touch & pointer
|
||||
|
||||
- Minimum target ~40px for primary icon buttons (`size="icon"` → 40px).
|
||||
- Keep tap areas separated (`gap-1.5`–`gap-2` in toolbars).
|
||||
|
||||
## Overflow
|
||||
|
||||
- Test at 320–360px widths for Persian word wrapping.
|
||||
- No horizontal page scroll; clip section decorations.
|
||||
- Tables: wrap in horizontal scroll regions (blog tables already do).
|
||||
|
||||
## QA matrix (minimum)
|
||||
|
||||
- 375×812 (mobile)
|
||||
- 768×1024 (tablet)
|
||||
- 1280×800 (desktop)
|
||||
- Light + dark on each
|
||||
|
||||
## Related
|
||||
|
||||
- [layout.md](./layout.md)
|
||||
- [typography.md](./typography.md)
|
||||
- [navbar.md](./navbar.md)
|
||||
@@ -0,0 +1,92 @@
|
||||
# Sections
|
||||
|
||||
Every major page band is a **section** with shared shell, rhythm, and background language.
|
||||
|
||||
## Required pattern
|
||||
|
||||
```tsx
|
||||
import { Container } from "@/components/container";
|
||||
import { layout } from "@/lib/layout";
|
||||
import { type, typeSpacing } from "@/lib/typography";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
<section
|
||||
id="services"
|
||||
className={cn("section-shell bg-section-canvas", layout.sectionY, layout.scrollMargin)}
|
||||
>
|
||||
<div className="section-decor" aria-hidden>
|
||||
{/* optional ambient glow */}
|
||||
</div>
|
||||
<Container className="section-content">
|
||||
<div className={cn("mx-auto max-w-2xl text-center", typeSpacing.sectionStack)}>
|
||||
<p className={type.eyebrow}>…</p>
|
||||
<h2 className={type.h2}>…</h2>
|
||||
<p className={type["section-intro"]}>…</p>
|
||||
</div>
|
||||
{/* section body */}
|
||||
</Container>
|
||||
</section>
|
||||
```
|
||||
|
||||
## One job per section
|
||||
|
||||
- One purpose, one headline, usually one supporting sentence.
|
||||
- Do not combine unrelated marketing goals in a single band.
|
||||
- Secondary actions belong in the CTA group, not as parallel headline systems.
|
||||
|
||||
## Background variants
|
||||
|
||||
| Utility | Intent |
|
||||
| --- | --- |
|
||||
| `bg-section-canvas` | Default continuous wash (subtle primary tint) |
|
||||
| `bg-section-muted` | Soft muted band (replaces hard gray patches) |
|
||||
| `bg-gradient-hero` | Hero atmosphere |
|
||||
| `bg-gradient-featured` | Featured / highlight band |
|
||||
| `bg-gradient-cta` | Strong brand CTA panel |
|
||||
| `bg-background` | Flat canvas when wash is unnecessary |
|
||||
|
||||
Alternate canvas ↔ muted between adjacent sections to create rhythm without hard blocks.
|
||||
|
||||
## Vertical rhythm
|
||||
|
||||
- Use `layout.sectionY` (`py-16 md:py-20`) so every block-to-block gap is the same (8rem / md:10rem).
|
||||
- First section under header: `layout.sectionTopWithHeader` (homepage hero uses this too).
|
||||
- Anchors: always add `layout.scrollMargin`.
|
||||
|
||||
## Intro block
|
||||
|
||||
Prefer centered or start-aligned intro using `typeSpacing.sectionStack`:
|
||||
|
||||
- Eyebrow → Heading → Section intro
|
||||
- Constrain measure with `max-w-2xl` / `max-w-3xl` on the **intro only**
|
||||
|
||||
## Decorative layers
|
||||
|
||||
```tsx
|
||||
<div className="section-decor" aria-hidden>
|
||||
<div className="absolute start-1/2 top-0 size-[min(32rem,80vw)] -translate-x-1/2 rounded-full bg-glow-primary" />
|
||||
</div>
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- Must sit in `.section-decor`.
|
||||
- `pointer-events-none`, no layout participation.
|
||||
- Never `position: fixed` for section atmosphere.
|
||||
- Opacity kept low (see [gradients.md](./gradients.md)).
|
||||
|
||||
## Motion
|
||||
|
||||
Wrap section bodies with `MotionReveal` / `MotionRevealItem` for scroll entrance. Keep stagger ≤ ~8 children.
|
||||
|
||||
## Forbidden
|
||||
|
||||
- Random `py-12` / `py-24` section padding.
|
||||
- Hard `bg-gray-100` patches disconnected from `bg-section-*`.
|
||||
- Multiple competing H2s in one section.
|
||||
|
||||
## Related
|
||||
|
||||
- [layout.md](./layout.md)
|
||||
- [gradients.md](./gradients.md)
|
||||
- [footer.md](./footer.md)
|
||||
@@ -0,0 +1,82 @@
|
||||
# Shadcn / Tailark Adaptation
|
||||
|
||||
Imported blocks are starting points—not finished UI. Every Shadcn UI or Tailark block must be adapted to Amin Rayan tokens before merge.
|
||||
|
||||
## Goals
|
||||
|
||||
- Visual continuity with existing sections.
|
||||
- One container, one spacing scale, one button/card system.
|
||||
- Dark mode and RTL friendliness preserved.
|
||||
|
||||
## Adaptation checklist (mandatory)
|
||||
|
||||
When adding or regenerating a block:
|
||||
|
||||
1. **Container** — Replace `container`, `mx-auto max-w-*`, and foreign `px-*` with `<Container>` / `layout.container`.
|
||||
2. **Section rhythm** — Outer `<section>` uses `layout.sectionY` (+ `section-shell` / `bg-section-*` as appropriate).
|
||||
3. **Colors** — Remap to semantic tokens (`bg-background`, `text-foreground`, `primary`, `muted-foreground`, `border`). Delete purple/zinc hardcodes.
|
||||
4. **Typography** — Swap ad-hoc heading classes for `type.*` / `<Typography>`.
|
||||
5. **Buttons** — Use `@/components/ui/button` only; map sizes to `sm | default | lg`.
|
||||
6. **Cards** — Prefer `@/components/ui/card`; remove stacked shadow recipes that fight `shadow-sm`.
|
||||
7. **Radius** — Align to `--radius` scale (`rounded-md`, `rounded-xl`, `rounded-blog` where relevant).
|
||||
8. **Shadows** — Prefer `shadow-xs` / `shadow-sm` / `shadow-md` / `shadow-glow-primary`.
|
||||
9. **Motion** — Prefer `MotionReveal` or existing duration scale; strip flashy infinite animations unless product-required.
|
||||
10. **Dark mode** — Verify `.dark` with semantic tokens; remove `dark:` hacks that reintroduce foreign palettes.
|
||||
11. **A11y** — Keep Radix behaviors; ensure labels, focus, and reduced motion.
|
||||
12. **Alignment QA** — Compare left/right edges with header/footer.
|
||||
|
||||
## Never allow after import
|
||||
|
||||
| Banned | Replace with |
|
||||
| --- | --- |
|
||||
| Random containers / `max-w-5xl` shells | `<Container>` |
|
||||
| Random hex colors | Semantic tokens |
|
||||
| Random typography stacks | `type.*` |
|
||||
| Parallel button CSS | `Button` |
|
||||
| Arbitrary spacing (`p-[1.125rem]`) | Spacing scale |
|
||||
| Purple / indigo marketing gradients | Brand gradients |
|
||||
| Extra global CSS for one block | Local Tailwind + existing utilities |
|
||||
|
||||
## Workflow
|
||||
|
||||
```bash
|
||||
npx shadcn@latest add button
|
||||
# or paste a Tailark section into components/
|
||||
```
|
||||
|
||||
Then immediately:
|
||||
|
||||
1. Open [container.md](./container.md) + [quality-checklist.md](./quality-checklist.md).
|
||||
2. Diff the block against a known-good section (`services-14`, `cta-16`, `footer`).
|
||||
3. Delete unused vendor CSS variables.
|
||||
4. Wire copy to Persian where the surface is fa-IR.
|
||||
|
||||
## Example transform
|
||||
|
||||
```tsx
|
||||
// ❌ Vendor
|
||||
<section className="py-24">
|
||||
<div className="mx-auto max-w-5xl px-4">
|
||||
<h2 className="text-4xl font-bold tracking-tight">…</h2>
|
||||
<button className="rounded-full bg-indigo-600 px-6 py-3 text-white">…</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
// ✅ Adapted
|
||||
<section className={cn("section-shell bg-section-canvas", layout.sectionY)}>
|
||||
<Container>
|
||||
<h2 className={type.h2}>…</h2>
|
||||
<Button size="lg">…</Button>
|
||||
</Container>
|
||||
</section>
|
||||
```
|
||||
|
||||
## Existing UI primitives
|
||||
|
||||
Prefer composing from `components/ui/*` already in the repo (59+ primitives) instead of re-adding duplicates.
|
||||
|
||||
## Related
|
||||
|
||||
- [coding-rules.md](./coding-rules.md)
|
||||
- [quality-checklist.md](./quality-checklist.md)
|
||||
- Cursor rule: `.cursor/rules/shadcn.mdc`
|
||||
@@ -0,0 +1,70 @@
|
||||
# Spacing
|
||||
|
||||
All spacing must come from the scale below. Arbitrary values (`p-[13px]`, `mt-[22px]`, `gap-[0.9rem]`) are forbidden unless fixing a temporary third-party bug—and must be replaced before merge.
|
||||
|
||||
## Scale (px)
|
||||
|
||||
| Token | px | Rem | Tailwind |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | 4 | 0.25 | `1` |
|
||||
| 2 | 8 | 0.5 | `2` |
|
||||
| 3 | 12 | 0.75 | `3` |
|
||||
| 4 | 16 | 1 | `4` |
|
||||
| 5 | 20 | 1.25 | `5` |
|
||||
| 6 | 24 | 1.5 | `6` |
|
||||
| 8 | 32 | 2 | `8` |
|
||||
| 10 | 40 | 2.5 | `10` |
|
||||
| 12 | 48 | 3 | `12` |
|
||||
| 16 | 64 | 4 | `16` |
|
||||
| 20 | 80 | 5 | `20` |
|
||||
| 24 | 96 | 6 | `24` |
|
||||
|
||||
Use as `p-4`, `gap-6`, `mt-8`, `space-y-4`, etc.
|
||||
|
||||
## Layout-level spacing
|
||||
|
||||
| Role | Token / class | Notes |
|
||||
| --- | --- | --- |
|
||||
| Container gutter | `px-6` (24) | Via `layout.container` only |
|
||||
| Section Y (mobile) | `py-16` (64) | `layout.sectionY` — adjacent gap = 8rem |
|
||||
| Section Y (md+) | `md:py-20` (80) | `layout.sectionY` — adjacent gap = 10rem |
|
||||
| Header clearance | `pt-28` / `md:pt-32` | `layout.sectionTopWithHeader` |
|
||||
| Scroll margin | `scroll-mt-24` (96) | Anchors under fixed header |
|
||||
|
||||
## Typography-linked spacing
|
||||
|
||||
From `lib/typography.ts` → `typeSpacing`:
|
||||
|
||||
| Token | Class | Use |
|
||||
| --- | --- | --- |
|
||||
| `eyebrowToHeading` | `mt-2` | Eyebrow → title |
|
||||
| `headingToBody` | `mt-3` | Title → body |
|
||||
| `sectionStack` | `space-y-4` | Section intro stack |
|
||||
| `heroStack` | `space-y-6` | Hero copy stack |
|
||||
| `cardStack` | `gap-1.5` | Card title → description |
|
||||
| `paragraphStack` | `space-y-4` | Stacked paragraphs |
|
||||
| `labelToControl` | `gap-1.5` | Label → input |
|
||||
|
||||
Prefer `typeSpacing.*` over reinventing the same stacks.
|
||||
|
||||
## Component spacing norms
|
||||
|
||||
| Component | Internal spacing |
|
||||
| --- | --- |
|
||||
| Card | `gap-6`, horizontal `px-6`, vertical `py-6` |
|
||||
| Button | Tokenized via `--btn-px-*` / `--btn-gap-*` — do not override casually |
|
||||
| Field groups | `gap-7` between fields; `gap-3` for checkbox/radio groups |
|
||||
| Footer blocks | `mb-14`, `pb-14`, column `gap-10` |
|
||||
|
||||
## Rules
|
||||
|
||||
1. Never invent spacing outside the scale.
|
||||
2. Prefer consistency with nearest sibling sections over “optical” one-off tweaks.
|
||||
3. Vertical rhythm between major sections comes from `layout.sectionY` alone — do not add extra section margins that change the shared gap.
|
||||
4. Icon-to-label gaps inside buttons use button tokens; elsewhere prefer `gap-2` / `gap-3`.
|
||||
|
||||
## Related
|
||||
|
||||
- [layout.md](./layout.md)
|
||||
- [typography.md](./typography.md)
|
||||
- [coding-rules.md](./coding-rules.md)
|
||||
@@ -0,0 +1,119 @@
|
||||
# Typography
|
||||
|
||||
Persian-first typography system using **IRANSansX** (FaNum — Persian digits baked into the font). Canonical scale: `lib/typography.ts`. Prefer `type.*` classes or `<Typography variant="…">` over ad-hoc `text-*` stacks.
|
||||
|
||||
## Font family
|
||||
|
||||
| Role | Stack |
|
||||
| --- | --- |
|
||||
| Sans (UI) | `IRANSansX`, system-ui, sans-serif |
|
||||
| Mono | `ui-monospace`, SF Mono, Menlo, Consolas, monospace |
|
||||
|
||||
Loaded via `@font-face` in `app/globals.css`. Body applies `font-family: "IRANSansX"` with `text-align: start` (right in RTL).
|
||||
|
||||
## Global base
|
||||
|
||||
| Element | Line height | Letter spacing | Alignment |
|
||||
| --- | --- | --- | --- |
|
||||
| `body` | `1.75` | `normal` | `start` |
|
||||
| `h1–h6` | `1.45` | `normal` | `start` |
|
||||
| `p` | `1.8` | `normal` | `start` |
|
||||
|
||||
Tracking is almost always `tracking-normal` (Persian). Avoid `tracking-tight` except decorative mono display (`display-mono`).
|
||||
|
||||
**Prefer** `text-start` (right in RTL). Section captions (`section-intro`) use `text-justify` with `text-align-last: start` so multi-line captions justify while the last line stays right-aligned. Avoid justify on body paragraphs and titles.
|
||||
|
||||
## Scale
|
||||
|
||||
Use via:
|
||||
|
||||
```tsx
|
||||
import { type, typeSpacing, typeMeasure } from "@/lib/typography";
|
||||
import { Typography, SectionTitle } from "@/components/ui/typography";
|
||||
import { toPersianDigits } from "@/lib/persian-digits";
|
||||
|
||||
<h2 className={type.h2}>عنوان بخش</h2>
|
||||
<Typography variant="section-intro" className={typeMeasure.prose}>
|
||||
توضیح کوتاه
|
||||
</Typography>
|
||||
<span>{toPersianDigits(24)}</span>
|
||||
```
|
||||
|
||||
| Requested level | Variant | Size / weight | Default element | Use |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Heading 1 | `h1` / `display` | 3xl→5xl, extrabold / bold | `h1` | Page / hero titles |
|
||||
| Heading 2 | `h2` / `cta-title` | 2xl→4xl, bold | `h2` | Major sections |
|
||||
| Heading 3 | `h3` | lg→xl, bold | `h3` | Subsections / features |
|
||||
| Body Large | `body-lg` | lg, leading 1.8 | `p` | Prominent intros (same size as captions) |
|
||||
| Body | `body` | base, leading 1.8 | `p` | Standard copy |
|
||||
| Small Text | `body-sm` | sm, leading 1.75 | `p` | Dense / secondary |
|
||||
| Caption | `caption` | xs, muted | `span` | Meta, footnotes |
|
||||
| Micro | `micro` | 11px (0.6875rem) | `span` | Dense chrome / mockups / step badges |
|
||||
| Button Text | `button` / `button-lg` | sm / base, medium, leading-none | `span` | Wired into `Button` sizes |
|
||||
| Navigation | `nav` / `nav-mobile` | sm / base, medium | `span` | Header / mobile menu |
|
||||
| — | `eyebrow` | sm medium, muted | `p` | Section labels |
|
||||
| — | `label` | sm medium | `span` | Form labels |
|
||||
| — | `card-title` / `card-desc` | base bold / sm | `h3` / `p` | Cards |
|
||||
| — | `section-intro` | lg muted, justify | `p` | Section captions under headings |
|
||||
| — | `stat` | 4xl→5xl bold primary | `span` | KPI numbers |
|
||||
|
||||
## Reading measure
|
||||
|
||||
| Token | Class | Use |
|
||||
| --- | --- | --- |
|
||||
| `typeMeasure.narrow` | `max-w-md` | Short support under a heading |
|
||||
| `typeMeasure.prose` | `max-w-2xl` | Standard paragraph / intro |
|
||||
| `typeMeasure.wide` | `max-w-3xl` | Longer multi-paragraph blocks |
|
||||
|
||||
## Alignment
|
||||
|
||||
- Titles: **right-aligned** (`text-start` in RTL).
|
||||
- Section captions (`section-intro`): justify with last line start (right in RTL) when 2+ lines.
|
||||
- Body paragraphs: right-aligned; do not justify.
|
||||
- Hero marketing headlines may use `text-center` when the composition requires it — override with `className="text-center"`.
|
||||
- Do not inherit `text-center` onto multi-line section captions.
|
||||
|
||||
## Numbers
|
||||
|
||||
- Prefer explicit Persian digits in copy (`۲۴`) or `toPersianDigits()` / `formatPersianNumber()`.
|
||||
- IRANSansXFaNum also shapes Latin digits visually; still prefer explicit Persian for dates, counters, stats, and steps.
|
||||
- Keep Latin only for technical IDs, versions, emails, and code.
|
||||
|
||||
## Button text
|
||||
|
||||
`Button` sizes pull from `type.button` (sm/default) and `type["button-lg"]` (lg). Horizontal padding tokens in `globals.css` are slightly wider for Persian glyphs. Do not wrap button labels in `<Typography>` unless building a non-`Button` control.
|
||||
|
||||
## Hierarchy rules
|
||||
|
||||
1. One `h1` per page (or `display` acting as the page title).
|
||||
2. Section titles use `h2` / `cta-title`; subsections use `h3`/`h4`.
|
||||
3. Pair titles with at most one short supporting sentence (`section-intro` or `body` + `text-muted-foreground`).
|
||||
4. Use `text-balance` on large headings (already in scale) for better wrap in Persian.
|
||||
5. Blog article prose uses `.blog-content` rules in `globals.css`—do not override casually.
|
||||
|
||||
## Stacking
|
||||
|
||||
```tsx
|
||||
<div className={typeSpacing.sectionStack}>
|
||||
<p className={type.eyebrow}>خدمات</p>
|
||||
<h2 className={cn(type.h2, typeSpacing.eyebrowToHeading)}>…</h2>
|
||||
<p className={cn(type["section-intro"], typeSpacing.headingToBody, typeMeasure.prose)}>…</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Forbidden
|
||||
|
||||
- Inter / Roboto / Arial as primary UI fonts.
|
||||
- Random `text-[15px]` / `text-[1.05rem]` (use `type.micro` for dense chrome).
|
||||
- `tracking-widest` on Persian body copy.
|
||||
- `text-justify` on body paragraphs / titles (section captions are the exception).
|
||||
- Using `font-bold` everywhere—follow the scale weights.
|
||||
- Physical `text-left` / `mr-` / `ml-` / `pr-` / `pl-` when logical `text-start` / `ms-` / `me-` / `ps-` / `pe-` work.
|
||||
- Overriding scale sizes with `sm:text-5xl` / `lg:text-6xl` on `type.display` / `type.h*`.
|
||||
- Using `type.h2` on an `h3` (or vice versa) — keep element level and scale token aligned.
|
||||
|
||||
## Related
|
||||
|
||||
- [buttons.md](./buttons.md)
|
||||
- [responsive.md](./responsive.md)
|
||||
- [accessibility.md](./accessibility.md)
|
||||
Reference in New Issue
Block a user