# CLAUDE.md — SaaS-PDF Codebase Rules & Design System Reference > Comprehensive rules document for AI-assisted development and Figma-to-code integration via Model Context Protocol (MCP). --- ## 1. Project Overview SaaS-PDF is a full-stack web application offering 16+ free online file-processing tools (PDF, image, video, text). The architecture is a **Python/Flask backend** with **Celery workers** for async processing, and a **React + TypeScript frontend** styled with **Tailwind CSS**. ### Tech Stack Summary | Layer | Technology | |-------------|------------------------------------------------------| | Frontend | React 18, TypeScript 5.5, Vite 5.4 | | Styling | Tailwind CSS 3.4, PostCSS, Autoprefixer | | State | Zustand 4.5 (available but lightweight usage) | | Routing | React Router DOM 6.23 | | i18n | i18next 23 + react-i18next 14 (EN, AR, FR) | | Icons | Lucide React 0.400 | | HTTP | Axios 1.7 | | SEO | react-helmet-async 2.0, JSON-LD structured data | | Analytics | react-ga4 2.1 | | File Upload | react-dropzone 14.2 | | Toasts | sonner 1.5 | | Backend | Flask, Celery, Redis | | Build | Vite (dev + prod), Docker Compose | --- ## 2. Token Definitions (Design Tokens) ### 2.1 Color Tokens Colors are defined in **two places** that must stay in sync: #### Tailwind Extended Colors (`frontend/tailwind.config.js`) ```js // Primary palette (Blue) primary: { 50: '#eff6ff', 100: '#dbeafe', 200: '#bfdbfe', 300: '#93c5fd', 400: '#60a5fa', 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8', 800: '#1e40af', 900: '#1e3a8a', 950: '#172554', } // Accent palette (Purple/Fuchsia) accent: { 50: '#fdf4ff', 100: '#fae8ff', 200: '#f5d0fe', 300: '#f0abfc', 400: '#e879f9', 500: '#d946ef', 600: '#c026d3', 700: '#a21caf', 800: '#86198f', 900: '#701a75', } ``` **Built-in Tailwind colors also in heavy use:** `slate`, `red`, `emerald`, `orange`, `violet`, `pink`, `teal`, `amber`, `lime`, `cyan`, `green`, `sky`, `blue`, `purple`, `indigo`. #### CSS Custom Properties (`frontend/src/styles/global.css`) ```css :root { --color-bg: #ffffff; --color-surface: #f8fafc; --color-text: #0f172a; --color-text-secondary: #64748b; --color-border: #e2e8f0; } .dark { --color-bg: #0f172a; --color-surface: #1e293b; --color-text: #f1f5f9; --color-text-secondary: #94a3b8; --color-border: #334155; } ``` ### 2.2 Typography Tokens Fonts are loaded from Google Fonts in `frontend/index.html`: | Font | Weights | Usage | |-----------|------------------|-----------------------------| | Inter | 300,400,500,600,700 | Default sans-serif (LTR) | | Tajawal | 300,400,500,700 | Arabic/RTL text | ```js // tailwind.config.js fontFamily: { sans: ['Inter', 'Tajawal', 'system-ui', 'sans-serif'], arabic: ['Tajawal', 'Inter', 'sans-serif'], } ``` **RTL switching** happens automatically in `global.css`: ```css body { font-family: 'Inter', 'Tajawal', system-ui, sans-serif; } [dir="rtl"] body { font-family: 'Tajawal', 'Inter', system-ui, sans-serif; } ``` ### 2.3 Spacing & Layout Tokens No custom spacing tokens — uses **Tailwind's default spacing scale** (4px base unit: `p-1` = 4px, `p-2` = 8px, etc.). **Layout constants used throughout:** - Max content width: `max-w-7xl` (header/footer), `max-w-2xl` (tool pages) - Container padding: `px-4 sm:px-6 lg:px-8` - Page vertical padding: `py-8` - Section gap: `space-y-4` - Card padding: `p-6` - Border radius: `rounded-xl` (buttons, inputs), `rounded-2xl` (cards, zones) ### 2.4 Token Transformation **No token transformation pipeline** (e.g., Style Dictionary) is in place. Tokens are hand-coded in Tailwind config and CSS. When integrating Figma variables: - Map Figma color variables → `tailwind.config.js` `theme.extend.colors` - Map Figma text styles → Tailwind `fontFamily`, `fontSize` classes - Map Figma spacing → Tailwind spacing scale (already standard 4px grid) --- ## 3. Component Library ### 3.1 Component Organization ``` frontend/src/components/ ├── layout/ # App-wide structural components │ ├── Header.tsx # Sticky header with nav, dark mode, language switcher │ ├── Footer.tsx # Footer with links │ └── AdSlot.tsx # Google AdSense container ├── shared/ # Reusable UI building blocks │ ├── FileUploader.tsx # Drag & drop file upload (react-dropzone) │ ├── DownloadButton.tsx # Result card with download link │ ├── ProgressBar.tsx # Task processing indicator │ └── ToolCard.tsx # Homepage tool grid card (link) └── tools/ # One component per tool (16 total) ├── PdfCompressor.tsx ├── MergePdf.tsx ├── SplitPdf.tsx ├── PdfToWord.tsx ├── WordToPdf.tsx ├── RotatePdf.tsx ├── PdfToImages.tsx ├── ImagesToPdf.tsx ├── WatermarkPdf.tsx ├── ProtectPdf.tsx ├── UnlockPdf.tsx ├── AddPageNumbers.tsx ├── ImageConverter.tsx ├── VideoToGif.tsx ├── WordCounter.tsx └── TextCleaner.tsx ``` ### 3.2 Component Architecture Pattern **All components are React functional components with default exports.** #### Tool Component Standard Template Every tool page follows this exact pattern: ```tsx import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Helmet } from 'react-helmet-async'; import { SomeIcon } from 'lucide-react'; import FileUploader from '@/components/shared/FileUploader'; import ProgressBar from '@/components/shared/ProgressBar'; import DownloadButton from '@/components/shared/DownloadButton'; import AdSlot from '@/components/layout/AdSlot'; import { useFileUpload } from '@/hooks/useFileUpload'; import { useTaskPolling } from '@/hooks/useTaskPolling'; import { generateToolSchema } from '@/utils/seo'; export default function ToolName() { const { t } = useTranslation(); const [phase, setPhase] = useState<'upload' | 'processing' | 'done'>('upload'); // 1. useFileUpload hook for file selection + upload // 2. useTaskPolling hook for progress tracking // 3. Three-phase UI: upload → processing → done return ( <> {t('tools.toolKey.title')} — {t('common.appName')}
{/* Icon + Title + Description header */}

{t('tools.toolKey.title')}

{t('tools.toolKey.description')}

{/* Phase-based rendering */} {phase === 'upload' && ( /* FileUploader + options + submit button */ )} {phase === 'processing' && ( )} {phase === 'done' && result && ( )} {phase === 'done' && error && ( /* Error message + retry */ )}
); } ``` #### Key Patterns | Pattern | Implementation | |---------|---------------| | State management | Local `useState` per tool; 3-phase state machine (`upload`/`processing`/`done`) | | File upload | `useFileUpload` hook → wraps `uploadFile()` API call | | Async processing | `useTaskPolling` hook → polls `/api/tasks/{id}/status` every 1.5s | | SEO | `` with title, description, canonical URL, JSON-LD | | i18n | All user-facing strings via `t('key')`, never hardcoded | | Lazy loading | All tool + page components use `React.lazy()` + `Suspense` in `App.tsx` | ### 3.3 Component Documentation **No Storybook or formal component documentation exists.** Components are self-documented through TypeScript interfaces on their props. --- ## 4. Frameworks & Build System ### 4.1 Build & Dev | Tool | Config File | Purpose | |---------|------------|---------| | Vite | `frontend/vite.config.ts` | Dev server (port 5173) + production build | | TypeScript | `frontend/tsconfig.json` | Strict mode, ES2020 target, `@/` path alias | | PostCSS | `frontend/postcss.config.js` | Tailwind CSS + Autoprefixer | ### 4.2 Path Aliases ```ts // vite.config.ts + tsconfig.json '@/*' → './src/*' ``` **Always use `@/` imports**, never relative `../` paths: ```tsx // ✅ Correct import FileUploader from '@/components/shared/FileUploader'; // ❌ Wrong import FileUploader from '../../components/shared/FileUploader'; ``` ### 4.3 Code Splitting Vite is configured with manual chunks: ```ts manualChunks: { vendor: ['react', 'react-dom', 'react-router-dom'], i18n: ['i18next', 'react-i18next'], } ``` All route-level components use `React.lazy()` for automatic code splitting. --- ## 5. Asset Management ### 5.1 Static Assets ``` frontend/public/ # Served at root URL, not processed by Vite ├── favicon.svg # SVG favicon (indigo rounded-rect with doc icon) ├── ads.txt # Google AdSense ads.txt └── robots.txt # SEO robots directive ``` - **No image assets** are stored in the frontend — the app is icon-driven - Uploaded files are stored server-side in Docker volumes (`/tmp/uploads`, `/tmp/outputs`) - Download URLs are served from the backend API ### 5.2 Asset Optimization - **Nginx caching** (production): 1-year cache for `js|css|png|jpg|jpeg|gif|ico|svg|woff2` with `Cache-Control: public, immutable` - **Vite build**: Sourcemaps disabled in production (`sourcemap: false`) - **No CDN** is currently configured — assets are served directly from the Nginx container ### 5.3 Font Loading Fonts are loaded via Google Fonts CDN with `preconnect` hints in `index.html`: ```html ``` --- ## 6. Icon System ### 6.1 Library All icons come from **[Lucide React](https://lucide.dev/)** (`lucide-react` v0.400). ### 6.2 Import Pattern ```tsx import { FileText, Upload, Download, X, Loader2 } from 'lucide-react'; ``` Each icon is imported **individually by name** — Lucide is tree-shakeable. ### 6.3 Sizing Convention | Context | Size Class | Example | |-------------------|-----------------|---------| | Tool page hero | `h-8 w-8` | `` | | Homepage card | `h-6 w-6` | `` | | Header nav | `h-7 w-7` | Logo icon | | Buttons / inline | `h-5 w-5` | `` | | Small indicators | `h-4 w-4` | Chevrons, checkmarks | | Upload zone | `h-12 w-12` | `` | ### 6.4 Icon Color Convention Icons use **Tailwind text-color classes**, typically matching a semantic color: ```tsx // Tool hero icons get a colored background container
``` ### 6.5 Icon-to-Tool Color Mapping | Tool | Icon | Background | Text Color | |------|------|-----------|------------| | PDF to Word | `FileText` | `bg-red-50` | `text-red-600` | | Word to PDF | `FileOutput` | `bg-blue-50` | `text-blue-600` | | Compress PDF | `Minimize2` | `bg-orange-50` | `text-orange-600` | | Merge PDF | `Layers` | `bg-violet-50` | `text-violet-600` | | Split PDF | `Scissors` | `bg-pink-50` | `text-pink-600` | | Rotate PDF | `RotateCw` | `bg-teal-50` | `text-teal-600` | | PDF to Images | `Image` | `bg-amber-50` | `text-amber-600` | | Images to PDF | `FileImage` | `bg-lime-50` | `text-lime-600` | | Watermark PDF | `Droplets` | `bg-cyan-50` | `text-cyan-600` | | Protect PDF | `Lock` | `bg-red-50` | `text-red-600` | | Unlock PDF | `Unlock` | `bg-green-50` | `text-green-600` | | Page Numbers | `ListOrdered` | `bg-sky-50` | `text-sky-600` | | Image Converter | `ImageIcon` | `bg-purple-50` | `text-purple-600` | | Video to GIF | `Film` | `bg-emerald-50` | `text-emerald-600` | | Word Counter | `Hash` | `bg-blue-50` | `text-blue-600` | | Text Cleaner | `Eraser` | `bg-indigo-50` | `text-indigo-600` | ### 6.6 No Custom SVG Icons The project uses **zero custom SVG icons** — everything is Lucide. If a Figma design introduces custom icons, they should be converted to React components following Lucide's pattern (24x24 viewBox, `currentColor` stroke, configurable `className`). --- ## 7. Styling Approach ### 7.1 Methodology: Utility-First Tailwind CSS - **Primary approach**: Tailwind utility classes applied directly in JSX - **No CSS Modules**, no Styled Components, no CSS-in-JS - **No BEM** or other class naming convention ### 7.2 Reusable Component Classes (`@layer components`) Defined in `frontend/src/styles/global.css`: ```css /* Buttons */ .btn-primary /* Blue filled button, rounded-xl, primary-600 bg */ .btn-secondary /* White/outlined button, slate ring */ .btn-success /* Green filled button, emerald-600 bg */ /* Cards */ .card /* White card, rounded-2xl, ring-1 slate, shadow-sm */ .tool-card /* Card + cursor-pointer + hover effects */ /* Form */ .input-field /* Full-width input, rounded-xl, ring-1 slate */ /* Typography */ .section-heading /* text-2xl/3xl bold tracking-tight */ /* Upload */ .upload-zone /* Dashed border, centered content, hover state */ /* Ads */ .ad-slot /* Centered container for AdSense units */ ``` ### 7.3 Dark Mode - Strategy: **Tailwind `class` mode** (`darkMode: 'class'` in config) - Toggled via `dark` class on `` element - Persisted in `localStorage` under key `theme` - Respects `prefers-color-scheme` on first visit - Implementation: `useDarkMode()` hook in `Header.tsx` **Dark mode pattern — every visual element needs both variants:** ```tsx // ✅ Always pair light + dark className="bg-white text-slate-900 dark:bg-slate-800 dark:text-slate-100" // ❌ Never omit dark variant for visible elements className="bg-white text-slate-900" ``` ### 7.4 Responsive Design - **Mobile-first** approach using Tailwind breakpoints - Breakpoints: `sm:` (640px), `md:` (768px), `lg:` (1024px), `xl:` (1280px) - Common patterns: ```tsx // Responsive grid className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3" // Show/hide className="hidden md:flex" // Desktop nav className="md:hidden" // Mobile menu button // Responsive text className="text-4xl sm:text-5xl" // Responsive padding className="px-4 sm:px-6 lg:px-8" ``` ### 7.5 RTL Support - Automatic direction switching via `useDirection()` hook - Arabic (`ar`) triggers `dir="rtl"` on `` - Uses Tailwind RTL-aware logical properties: - `ms-auto` (margin-inline-start) instead of `ml-auto` - `end-0` instead of `right-0` - `[dir="rtl"] .ltr-only` utility for forcing LTR on specific elements - Font family switches to Tajawal-first for Arabic ### 7.6 Animations Defined in `global.css`: ```css @keyframes progress-pulse { /* Pulsing opacity for progress bars */ } @keyframes fadeSlideIn { /* Fade + slide for dropdowns */ } ``` Used via classes: `.progress-bar-animated`, `.animate-in` --- ## 8. Internationalization (i18n) ### 8.1 Setup - **Library**: i18next + react-i18next + i18next-browser-languagedetector - **Config**: `frontend/src/i18n/index.ts` - **Translation files**: - `frontend/src/i18n/en.json` (English — source of truth) - `frontend/src/i18n/ar.json` (Arabic) - `frontend/src/i18n/fr.json` (French) ### 8.2 Key Structure ```json { "common": { "appName", "upload", "download", "processing", ... }, "home": { "hero", "heroSub", "popularTools", ... }, "tools": { "toolKey": { "title", "description", "shortDesc", ...toolSpecificKeys } }, "result": { "conversionComplete", "downloadReady", "linkExpiry", ... } } ``` ### 8.3 Usage Rules ```tsx // ✅ Always use translation keys

{t('tools.compressPdf.title')}

// ✅ Interpolation {t('common.maxSize', { size: maxSizeMB })} // ❌ Never hardcode user-facing strings

Compress PDF

``` --- ## 9. Project Structure Rules ### 9.1 Directory Layout ``` SaaS-PDF/ ├── frontend/ # React SPA │ ├── src/ │ │ ├── components/ # UI Components (layout/, shared/, tools/) │ │ ├── hooks/ # Custom React hooks │ │ ├── i18n/ # Translation files │ │ ├── pages/ # Route-level page components │ │ ├── services/ # API client (axios) │ │ ├── styles/ # Global CSS (Tailwind layers) │ │ └── utils/ # Pure utility functions │ └── public/ # Static assets ├── backend/ # Flask + Celery │ ├── app/ │ │ ├── routes/ # API endpoints (Flask blueprints) │ │ ├── services/ # Business logic │ │ ├── tasks/ # Celery async tasks │ │ ├── utils/ # Backend utilities │ │ └── middleware/ # Rate limiting, etc. │ ├── config/ # Flask configuration │ └── tests/ # pytest tests ├── nginx/ # Reverse proxy configuration ├── scripts/ # Deployment & maintenance scripts ├── docs/ # Specifications & planning docs ├── docker-compose.yml # Development environment └── docker-compose.prod.yml ``` ### 9.2 File Naming Conventions | Type | Convention | Example | |------|-----------|---------| | React components | PascalCase | `PdfCompressor.tsx`, `FileUploader.tsx` | | Hooks | camelCase with `use` prefix | `useFileUpload.ts`, `useDirection.ts` | | Utils | camelCase | `textTools.ts`, `seo.ts` | | Services | camelCase | `api.ts` | | Translation files | lowercase ISO code | `en.json`, `ar.json` | | CSS files | lowercase | `global.css` | | Pages | PascalCase + `Page` suffix | `HomePage.tsx`, `AboutPage.tsx` | ### 9.3 Export Conventions - **Components**: `export default function ComponentName()` (default export) - **Hooks**: `export function useHookName()` (named export) - **Utils**: `export function utilName()` (named export) - **Services**: named exports + `export default api` for the axios instance --- ## 10. Figma Integration Guidelines (MCP) ### 10.1 Mapping Figma to Code | Figma Element | Maps To | |---------------|---------| | Color Variables | `tailwind.config.js` → `theme.extend.colors` | | Text Styles | Tailwind `text-*`, `font-*` classes + `fontFamily` config | | Spacing | Tailwind spacing scale (4px grid: `p-1`=4px, `p-4`=16px) | | Border Radius | `rounded-xl` (12px) for buttons/inputs, `rounded-2xl` (16px) for cards | | Shadow | `shadow-sm`, `shadow-md`, `shadow-lg` (Tailwind defaults) | | Icons | Map to Lucide React icon names | | Components | React functional components with Tailwind utility classes | ### 10.2 When Creating New Components from Figma 1. **Place the file** in the correct directory (`layout/`, `shared/`, or `tools/`) 2. **Use `@/` path alias** for all imports 3. **Apply dark mode** classes to every visual element 4. **Add i18n keys** to all three translation files (`en.json`, `ar.json`, `fr.json`) 5. **Use Lucide icons** — do not add custom SVG files unless absolutely necessary 6. **Follow the tool template** pattern for any new tool page 7. **Add the route** to `App.tsx` with `React.lazy()` import 8. **Add SEO** via `` + `generateToolSchema()` 9. **Support RTL** — use logical properties (`ms-`, `me-`, `ps-`, `pe-`, `start-`, `end-`) ### 10.3 Color Translation Cheat Sheet | Figma Token | Tailwind Class | |-------------|---------------| | Primary / Brand Blue | `primary-600` (buttons), `primary-500` (dark mode) | | Background | `white` / `dark:slate-950` (page), `slate-50` / `dark:slate-800` (surface) | | Text Primary | `slate-900` / `dark:slate-100` | | Text Secondary | `slate-500` / `dark:slate-400` | | Border | `slate-200` / `dark:slate-700` | | Success | `emerald-600` | | Error | `red-600` / `red-700` | | Warning | `orange-600` | ### 10.4 Spacing Translation | Figma px | Tailwind class | |----------|---------------| | 4px | `1` | | 8px | `2` | | 12px | `3` | | 16px | `4` | | 20px | `5` | | 24px | `6` | | 32px | `8` | | 40px | `10` | | 48px | `12` | | 64px | `16` | | 80px | `20` | | 96px | `24` | --- ## 11. Code Quality Rules ### 11.1 TypeScript - **Strict mode** enabled - All component props must have an `interface` (not inline types) - Use `type` imports when importing only types: `import type { TaskResult } from '@/services/api'` ### 11.2 Component Rules - Every component must be a **default export** function - No class components - Use `useTranslation()` for all user-facing text - Use `` for page-level SEO on every route-level component ### 11.3 Styling Rules - **No inline styles** except for dynamic values (e.g., `style={{ width: '${percent}%' }}`) - **No external CSS files** per component — everything in Tailwind utilities or `global.css` `@layer` - Always include both light and dark mode variants - Always include responsive breakpoints for layout-affecting properties ### 11.4 Accessibility - Use semantic HTML (`
`, `
`, `