What Quote Gallery is built on, where each technology lives, and why it was chosen over the alternatives.
App shell, file-based routing, SSR, server API routes, and modules system.
Why: SSR on public pages (authors, quotes, presets) is critical for SEO while the app stays SPA-fast once navigated. Nuxt server routes also host the REST API and payment webhooks without a separate server.
Component model for every UI piece — pages, cards, modals, the slideshow, and the embed widget.
Why: Composition API + `<script setup>` keeps components concise and TypeScript inference works without boilerplate.
Realtime database, serverless functions (queries / mutations / actions), file storage, scheduled crons, and HTTP endpoints — covering quotes, authors, billing, AI, notifications, and admin.
Why: Reactive queries push updates to the UI without writing subscription glue. The schema is TypeScript-first and the same types flow end-to-end from DB to component props.
User sessions, social login (Google, GitHub, Apple), email / password, API key management, and account lifecycle.
Why: Self-hosted, TypeScript-native, and ships a first-party Convex adapter so auth tokens are verified inside Convex functions — no separate auth service to maintain.
The entire component library — dialogs, dropdowns, sheets, tooltips, cards, tables, and more.
Why: Components are copy-owned, not a node_modules dependency. Full control to adjust primitives without fighting a third-party API surface.
Utility classes for every layout, spacing, color, and responsive breakpoint decision.
Why: v4 moves config into CSS itself, making design tokens live alongside styles and removing the JS config file entirely.
Headless primitives that shadcn-vue wraps — handles ARIA roles, keyboard navigation, and focus management.
Why: Accessibility is handled at the primitive layer, so components meet WCAG AAA without manual ARIA authoring on every consumer.
Every icon in the app — navigation, actions, status indicators, and decorative elements.
Why: Consistent stroke-weight icon set that tree-shakes per import, so only used icons ship in the bundle.
Stores covering auth, quotes, playlists, payments, slideshow settings, notifications, API keys, and more.
Why: Vue 3's official store. Composition-API style keeps store logic testable and co-located, and TypeScript inference requires no extra typing wiring.
Composable utilities — localStorage sync, intersection observer, debounce, media queries, clipboard access, and event listeners.
Why: Avoids reimplementing browser-API wrappers that VueUse already handles correctly, including SSR safety.
Quote explanations, category auto-detection, grammar corrections, text-to-speech generation, and content moderation.
Why: API keys never reach the browser — all calls are made server-side with appropriate rate limiting.
Subscription billing across three tiers (Free / Hobby / Supporter) and one-time credit purchases.
Why: EU-first payment provider with iDEAL and Bancontact support — essential for a Dutch-hosted product. Webhook signatures are verified server-side before touching any billing state.
Transactional emails — account notifications, changelog digests, and subscription receipts.
Why: Simple REST API with reliable deliverability and good developer tooling.
Sequenced animations — page transitions, stagger list reveals, and the preloader sequence.
Why: Timeline API gives precise control over multi-step sequences that CSS transitions alone cannot express cleanly.
Vue-idiomatic scroll-triggered reveals and layout animations.
Why: Declarative component and directive API fits into Vue templates without imperative lifecycle hooks.
WebGL background effects — the Silk and Aurora animated backgrounds.
Why: Minimal WebGL abstraction (~10 KB) — enough power for shader-based visuals without the weight of Three.js.
Page views, custom events (preset applied, quote liked, playlist created), and session analytics.
Why: Cookie-free and GDPR-compliant by default — no consent banner required, no PII collected.
Captures and groups runtime errors from production users with stack traces and breadcrumbs.
Why: Surfaces JS errors that would otherwise be invisible post-deployment — with stack traces, breadcrumbs, and user context.
Unit tests for utilities, store logic, and composables.
Why: Runs inside the same Vite pipeline as Nuxt — no separate Jest config, faster cold starts, and native ESM support without transform workarounds.
Rich-text editor for author descriptions and quote editing in the admin panel.
Why: Headless ProseMirror wrapper with Vue 3 support and a clean extension API for custom marks, nodes, and slash commands.
Form validation for auth flows, submission forms, and account settings.
Why: Integrates with shadcn-vue form components out of the box. Schema-based validation keeps rules co-located with types.
Charts for admin analytics — quote view trends, user signups, and error rates over time.
Why: Framework-agnostic core with a thin Vue wrapper; good TypeScript support without pulling in a heavyweight BI library.
Data tables with sorting, filtering, and pagination for quotes, users, and moderation queues.
Why: Headless table logic — the library handles complex state while rendering stays entirely in Vue templates.