Create beautiful, shareable quote images in seconds.
Arabic & RTL support · 14 Google Fonts · Gradient backgrounds · 2× PNG export · Works offline
Warning
~60% of this project was built with AI assistance (Claude Sonnet). The design decisions, feature direction, Arabic content, and final review are human — but a significant portion of the code was AI-generated.
- Free-form quote and author inputs with instant live preview
- 14 fonts — 8 Latin + 6 Arabic, all loaded from Google Fonts
- Independent font size sliders for the quote body (12–48 px) and author line (10–32 px)
- Text alignment — Left, Center, Right
- 4 aspect ratios — Square 1:1 · Landscape 16:9 · Portrait 4:5 · Story 9:16
- Backgrounds — solid color or gradient (7 presets + full custom from/to + 4 angle directions)
- Text color picker with White / Dark quick-set buttons
- 6 Arabic Google Fonts — Amiri, Cairo, Tajawal, Scheherazade New, Noto Naskh Arabic, Reem Kufi
- Auto-detection — typing Arabic characters instantly flips the card to RTL + right-align
- Switching to an Arabic font auto-enables RTL; switching back reverts to LTR
- Manual LTR / RTL toggle always available
- Full Egyptian Arabic UI (عامية مصرية) — every label, button, and placeholder translates when you switch to عربي mode
- EN / عربي toggle lives in the sidebar header
- 60 curated quotes — English icons (Mandela, Einstein, Twain…) and Arabic authors (المتنبي, جبران, نجيب محفوظ, محمود درويش, نزار قباني)
- Respects your active locale — Arabic mode picks an Arabic quote, English mode picks English
- No external API — fully offline capable
- 2× pixel-ratio PNG — crisp on retina screens and social media
- Font pre-loading via
document.fonts.load()before capture — Arabic and Latin fonts render correctly in the exported image every time - Toast notifications for success and failure feedback
- Installable on iOS and Android — works as a standalone app
- Offline support via Workbox service worker (app shell + Google Fonts cached for 1 year)
- State persisted to
localStorage— your card survives a page refresh - Mobile-first layout — sidebar becomes a bottom-sheet drawer on small screens
- Safe-area insets for notched iPhones (
env(safe-area-inset-bottom))
- Framer Motion throughout — entrance animations, quote cross-fade on random, card layout animation on aspect ratio change, staggered sidebar sections,
whileTapon every button
| Framework | React 19 + TypeScript |
| Build | Vite 8 |
| Animations | Framer Motion |
| Image Export | html-to-image |
| PWA | vite-plugin-pwa + Workbox |
| Persistence | localStorage (custom debounced hook) |
| Fonts | Google Fonts — 14 families |
| Styles | Vanilla CSS with custom properties + dark mode |
src/
├── components/
│ ├── Controls.tsx # Sidebar — all inputs, pickers, buttons
│ ├── QuoteCard.tsx # Card preview & export target
│ └── Toast.tsx # Animated notification stack
├── hooks/
│ └── usePersistedConfig.ts # localStorage save/restore (debounced)
├── App.tsx # Root — state, layout, handlers
├── i18n.ts # English + Egyptian Arabic strings
├── quotes.ts # 60 curated quotes (EN + AR)
├── types.ts # Shared TypeScript types
└── index.css # All styles, tokens, responsive layout
Prerequisites: Node.js 18+ · npm 9+
git clone https://github.com/eyadmkv/quote-cards.git
cd quote-cards
npm install
npm run dev
# Production build
npm run build
npm run preview
Deploy the dist/ folder to Vercel, Netlify, or any static host.
Every card setting lives in one typed object, persisted automatically:
interface CardConfig {
quote: string;
author: string;
backgroundType: "solid" | "gradient";
solidColor: string; // hex
gradient: { from, to, direction };
textColor: string; // hex
font: FontFamily; // CSS font-family string
textAlign: "left" | "center" | "right";
direction: "ltr" | "rtl";
aspectRatio: "1/1" | "16/9" | "4/5" | "9/16";
fontSize: number; // 12–48 px
authorFontSize: number; // 10–32 px
locale: "en" | "ar-EG";
}
Append to the QUOTES array in src/quotes.ts:
{ quote: "Your quote.", author: "Author Name", lang: "en" }
// lang: "ar" for Arabic — served automatically in Arabic mode
- Add the key to
Localeinsrc/types.ts - Add a
Stringsobject insrc/i18n.ts - Add a toggle button in the header inside
src/components/Controls.tsx