5.5 KiB
DESIGN_SYSTEM — anwarpy.ai
Canonical tokens live in
web/src/app/design.css. This file documents them and the rules for using them. If CSS and this file disagree, fix one of them in the same PR.
1. Design personality
Editorial, calm, premium-dark. Ink surfaces, champagne accents, generous whitespace, Arabic-first typography. Restraint is the brand: one accent color, used sparingly; motion is short and soft; nothing blinks, nothing bounces.
2. Color tokens
| Token | Value | Role |
|---|---|---|
--ink |
#131519 |
page background (--bg) |
--ink-2 |
#17191d |
raised background |
--basalt |
#1c1f24 |
card/surface (--surface) |
--seam |
#2b2f36 |
hairline borders (--border) |
--seam-2 |
#3a3f47 |
hover border, stronger seam |
--mist |
#ece9e3 |
primary text (--fg) |
--stone |
#a3a09a |
muted text (--muted) |
--dim |
#7b7974 |
faintest text (meta only) |
--champagne |
#d4b47c |
THE accent (--accent) — badges, CTAs, highlights |
--champagne-ink |
#1a150c |
text/icons on champagne (--accent-ink) |
Rules:
- Never introduce a new hue. Semantic aliases (
--bg,--surface,--fg,--muted,--border,--accent) are the only tokens components may use — raw hex is fordesign.cssonly. - Champagne on ink or champagne-ink on champagne. Never champagne text on basalt for body copy (contrast), never stone below 14px.
- Success/warning/error states (form errors, honest 503s) use
--mist/--stone+ wording first; reserve red only where the CSS already defines it. Do not invent traffic-light colors.
3. Typography
- Readex Pro (Arabic + Latin, single family), loaded in
layout.tsxvianext/font/google, weights 300–700.--font-display: var(--font-sans), "Segoe UI", sans-serif. - Scale: hero H1 ~2.5rem/700; section H2 ~1.5rem/600; body 1rem/400 with line-height ≥1.8 for Arabic; meta/captions 0.875rem
--stone. - Arabic-first:
direction: rtlat the page root; Arabic quotes «»; numerals viaformatNumber(Arabic-Indic where the site does). - LTR islands inside RTL text use
<bdi dir="ltr">:anwarpy.ai,$10, code, identifiers, agent names (GPTBot…). Never let a raw Latin token break RTL flow. - No
<br>for spacing; use margins. No font-size below 0.75rem.
4. Spacing & shape
- Space scale: 4 / 8 / 12 / 16 / 24 / 32 / 48 / 64px. Section padding 48–64px; card padding 16–24px; page max-width container per existing layout (centered, ≥24px gutters).
- Radii: cards/pills 8–12px; badges/buttons pill (999px) where the existing CSS does; inputs match buttons.
- Borders: 1px
--seameverywhere; hover →--seam-2. No heavy shadows; elevation =--ink-2/--basaltlayering, not blur.
5. Component inventory (build from these, extend the list, never fork it)
Existing, in src/components/ + route CSS: topnav.tsx, site-menu.tsx, nav-links.tsx, footer.tsx, anwarpi-background.tsx, set-body-attr.tsx.
Recurring patterns (CSS classes are the contract):
- Buttons:
.btn,.btn.btn-primary(champagne bg,--accent-inktext), disabled/coming-soon state = «قريباً» pill, wired tosubscribe-button.tsx comingSoonprop. Disabled buttons are visually disabled from first paint and explain why in Arabic. - Badges/pills: access badge «مجاني» / «للأعضاء»; content-type badge (note/tutorial/case_study/comparison); «وصول مبكر مجاني» champagne strip.
- Notice strip: the early-access notice on articles — champagne-tinted band, verbatim copy from PRD §3.2, never a modal.
- Guide card: surface card, dek line, meta row (category · reading minutes · «حُدّث»), champagne hover seam. Topic hubs and «ابدأ من هنا» reuse it.
- Article body: headings, paragraphs, lists, blockquote, code (LTR island, scrollable), real
<table>for tabular data (never screenshot/ASCII), links--champagneunderline. - Forms: label above input, input bg
--ink-2, border--seam, error text = the API's Arabic sentence verbatim, loading state disables + keeps label, success replaces form state («تم! سنراسلك عند تفعيل العضوية.»). - Tables renderer: for Lexical table nodes — mobile-safe overflow wrapper.
New components: reuse an existing class first, then extend design.css (or the route's CSS) — do not add a component library (no shadcn/radui/MUI) and no Tailwind (decision, 2026-10-11).
6. Motion
- Durations 150–250ms, ease-out. Hover: border color + 1–2px translate. Entrances: fade/rise ≤8px.
- No parallax, no infinite loops, no scroll-hijack. Reduced-motion: honor
prefers-reduced-motionfor any added animation. - Micro-interactions (like button) may pulse once on success — once, not looping.
7. Accessibility floor (enforced by npx hyperframes check on the brag video; treat as the site bar too)
- Contrast: body text ≥ 4.5:1, large text ≥ 3:1 (
--stoneon--basaltpasses;--dimonly ≥1.5rem or decorative). - Focus visible: 2px
--champagneoutline on interactive elements. - Every image has meaningful Arabic
alt(covers: the guide title); icon buttons havearia-label. - Landmarks: header/nav/main/footer; one H1 per page; heading order never skips.
- Forms:
<label for>,aria-live="polite"status regions (existing pattern:#notify-done).
8. Cover images
Generated via npm run covers (scripts/generate-covers.mjs) — 1200×630, ink/champagne system, Arabic title shaping checked. og:image dimensions are asserted in tests/evidence (1200×630). Don't hand-drop covers of other sizes.