15 KiB
Dating App Frontend — Claude Code Master Prompt
Context & Orientation
You are building the complete frontend for a dating application called Daiting. The backend is already implemented. The generated API client lives at src/api/api.ts — use it as the source of truth for all endpoint shapes, DTOs, and types. Do not re-declare types that already exist in the API client; import them.
Project root: C:\MyApps\dating-app-frontend
Package manager: pnpm
Stack: Vue 3 (Composition API + <script setup>) + Vite + Tauri v2
The app must work in three contexts simultaneously:
- Browser (dev/PWA) — desktop viewport
- Browser — mobile viewport (responsive, ≥ 375 px)
- Tauri desktop window (Windows / macOS / Linux)
All user-facing text (button labels, placeholders, error messages, toast notifications, empty states, navigation items, form hints, section headings) must be written in Russian. Brand name, logo, and any decorative/editorial text remain in English.
Design Skills — Read Before Writing a Single Line
Before writing any code, internalize these two skill files in full:
.claude/skills/impeccable/SKILL.md (+ all 7 files in reference/)
.claude/skills/taste-skill/SKILL.md
Apply Impeccable design principles throughout every component. Apply Taste-skill with these parameters:
DESIGN_VARIANCE: 8MOTION_INTENSITY: 6VISUAL_DENSITY: 4
Aesthetic Direction
The visual language is "warm brutalism meets editorial intimacy":
- Dark base (
#0d0d0d) with warm cream (#f0ebe0) as the primary text/accent surface - A single vivid signal color — deep terracotta
#c45c3a— used sparingly for CTAs, likes, active states - Typography: pair
Instrument Serif(display, headers, profile names) withDM Mono(UI labels, metadata, counts). Load both from Google Fonts. - Layouts that feel editorial — asymmetric cards, overlapping text on images, offset grids
- Micro-interactions via CSS transitions + Vue
<Transition>+ GSAP (install it). Nothing gratuitous — motion serves information hierarchy. - Grain texture overlay (SVG
<feTurbulence>) on the app shell background for tactility - NO: purple gradients, Inter/Roboto, glassmorphism clichés, floating hearts, soft pastels, rounded-rectangle cards that look like every other dating app
This should feel like an independent editorial magazine for human connection — not Tinder, not Bumble.
Project Bootstrap
# If scaffolding from scratch:
pnpm create tauri-app@latest dating-app-frontend --template vue-ts
cd dating-app-frontend
# Core dependencies
pnpm add vue-router@4 pinia @vueuse/core axios gsap @vuelidate/core @vuelidate/validators
# UI primitives (no opinionated component library — build custom)
pnpm add @floating-ui/vue
# Dev
pnpm add -D sass tailwindcss @tailwindcss/vite autoprefixer
Configure Tailwind in CSS-only mode (no JIT utility classes in templates — use scoped SCSS instead; Tailwind is for reset and baseline only).
File & Folder Architecture
src/
├── api/
│ └── api.ts # ← generated, do not touch
├── assets/
│ ├── fonts.css # @font-face / Google Fonts import
│ └── grain.svg # noise texture
├── composables/
│ ├── useAuth.ts
│ ├── useFeed.ts
│ ├── useChat.ts
│ ├── useMedia.ts
│ └── useGeolocation.ts
├── stores/
│ ├── auth.store.ts # Pinia
│ ├── profile.store.ts
│ ├── feed.store.ts
│ ├── chat.store.ts
│ └── ui.store.ts
├── router/
│ └── index.ts # vue-router with guards
├── views/
│ ├── auth/
│ │ ├── LoginView.vue
│ │ └── RegisterView.vue
│ ├── onboarding/
│ │ └── ProfileSetupView.vue
│ ├── feed/
│ │ └── FeedView.vue
│ ├── matches/
│ │ └── MatchesView.vue
│ ├── chat/
│ │ ├── ChatsListView.vue
│ │ └── ChatRoomView.vue
│ ├── dates/
│ │ └── DatesView.vue
│ ├── profile/
│ │ ├── MyProfileView.vue
│ │ └── ProfileDetailView.vue
│ └── admin/
│ └── ReportsView.vue
├── components/
│ ├── layout/
│ │ ├── AppShell.vue # grain bg, Tauri titlebar, nav
│ │ ├── BottomNav.vue # mobile navigation
│ │ ├── SideNav.vue # desktop sidebar navigation
│ │ └── TauriTitlebar.vue # custom Tauri drag region
│ ├── feed/
│ │ ├── FeedCard.vue # the main profile card
│ │ ├── FeedCardStack.vue # swipe stack with GSAP
│ │ ├── FeedFilters.vue # filter drawer/panel
│ │ └── LikeButton.vue
│ ├── profile/
│ │ ├── ProfileAvatar.vue
│ │ ├── ProfileBadge.vue # tags rendered as editorial labels
│ │ ├── ProfileEditor.vue # form for create/edit profile
│ │ └── MediaGallery.vue
│ ├── chat/
│ │ ├── ChatBubble.vue
│ │ ├── ChatInput.vue # text + media attach
│ │ ├── VoiceRecorder.vue
│ │ └── MediaMessage.vue
│ ├── dates/
│ │ ├── DateCard.vue
│ │ ├── DateProposalForm.vue
│ │ └── MapPicker.vue # Leaflet for lat/lng picking
│ ├── common/
│ │ ├── AppButton.vue
│ │ ├── AppInput.vue
│ │ ├── AppModal.vue
│ │ ├── AppDrawer.vue # bottom sheet on mobile, side panel on desktop
│ │ ├── AppToast.vue
│ │ ├── LoadingSpinner.vue
│ │ └── EmptyState.vue
│ └── reports/
│ └── ReportModal.vue
└── styles/
├── _variables.scss
├── _typography.scss
├── _animations.scss
└── main.scss
API Integration — Full Coverage
Implement every endpoint from src/api/api.ts. Below is the complete feature map:
Auth (/api/v1/auth/)
POST /register— registration form with phone + password, validation via VuelidatePOST /login— login formPOST /logout— clear store + redirectPOST /refresh— silent token refresh interceptor in Axios (auto-retry once on 401)POST /fcm-token— call after login (stub FCM token for web; use Tauri plugin for desktop)
Store access token in memory (Pinia), refresh token in localStorage. Never store access token in localStorage.
Users (/api/v1/users/)
GET /me— populate auth store on app loadGET /:id— view any user (admin use)PATCH /:id/banand/activate— admin panel actions
Profiles (/api/v1/profiles/)
POST /— profile creation wizard (multi-step: basic info → location → tags → photo)GET /my— list own profiles in MyProfileViewGET /:profileId— public profile detail pagePUT /:profileId— profile editorDELETE /:profileId— with confirmation modal
Media (/api/v1/profiles/:profileId/media/)
POST /upload?type=— drag-and-drop + file picker; show upload progress barGET /— grid gallery with lightboxDELETE /:mediaId— with optimistic UI removal
Feed (/api/v1/feed)
- Infinite scroll OR card-stack swipe (implement BOTH modes, togglable via UI switch)
- Filter panel: cityId, districtId, ageMin/ageMax, keyword, tagIds (multi-select chips)
- Show "search paused" banner when match limit is exceeded (detect via API response or a dedicated flag)
Likes (/api/v1/likes)
POST /— like/dislike with swipe gesture (GSAP drag) or buttonGET /matches?profileId=— matches list with match animation (confetti-lite or editorial flash)
Chat (/api/v1/chats)
POST /— open chat from a match cardGET /?profileId=— chats list; lock icon on inactive chats (only one active at a time)GET /:chatId/messages?profileId=— message history with virtual scrollPOST /:chatId/messages?profileId=— send text/mediaDELETE /:chatId?profileId=— close chat with confirmation- Implement polling (2 s interval) for new messages; note in code where WebSocket would replace this
Dates (/api/v1/dates)
POST /— proposal form: map picker (Leaflet) for lat/lng, datetime picker, optional statusIdGET /?profileId=— list with status chipsPATCH /:id/status?profileId=— accept / decline / complete actionsGET /statuses— fetch on mount, cache in Pinia
Reports (/api/v1/reports)
POST /—ReportModaltriggered from profile or chat bubble context menuGET /— admin table view with pagination
Tags, Cities, Greetings
GET /tags,GET /cities,GET /cities/:cityId/districts,GET /greetings— fetch on app init, cache in Pinia- Admin: create/delete tags, cities, districts, greetings
Component Implementation Standards
FeedCard.vue
The centerpiece. Execute this with exceptional care:
- Full-bleed image background
- Profile name in large Instrument Serif, overlapping bottom of image
- Age, city, tags as DM Mono micro-labels
- Drag-to-like: GSAP Draggable on desktop; touch events on mobile
- Color bleed on drag direction: terracotta tint for right (like), cool grey tint for left (dislike)
- Discard animation: card flies off screen with rotation, next card scales up from behind
- Tap to expand into ProfileDetailView
ChatRoomView.vue
- Messages grouped by date separator
- Own messages: right-aligned, cream background
- Partner messages: left-aligned, dark card with subtle border
- Voice messages: custom waveform player (Web Audio API for visualization)
- Video messages: inline
<video>with poster frame - Photos: open in lightbox modal
- Locked chat state: blurred message list + overlay "You have X other open chats. Close one to unlock."
- Input bar: text area auto-grows, attach button opens media type picker
AppShell.vue
- Desktop: left sidebar (64px collapsed / 240px expanded) + main content area
- Mobile: top header + bottom nav (5 items)
- Tauri: custom titlebar with drag region, traffic lights on macOS (use
data-tauri-drag-region) - Grain SVG filter applied as a pseudo-element over the entire shell
Responsive breakpoints
$mobile: 375px;
$tablet: 768px;
$desktop: 1024px;
$wide: 1440px;
Use @container queries inside card components where possible.
Routing & Guards
/ → redirect → /feed (if authed) or /login
/login
/register
/setup → profile creation wizard (redirect here if user has no profiles)
/feed
/matches
/chats
/chats/:chatId
/dates
/profile/me
/profile/:profileId → public view
/admin/reports → role guard (admin only)
Navigation guard: check auth store on every route change. Refresh token silently if access token expired.
State Management (Pinia)
auth.store.ts
state: { user, accessToken, profiles, activeProfileId }
actions: login, logout, register, refreshToken, setActiveProfile
feed.store.ts
state: { cards[], filters, page, hasMore, searchPaused }
actions: fetchNextPage, applyFilters, likeCard, dislikeCard
chat.store.ts
state: { chats[], activeChat, messages[], polling: NodeJS.Timer | null }
actions: openChat, closeChat, sendMessage, fetchMessages, startPolling, stopPolling
Tauri-Specific
- Use
@tauri-apps/api/windowfor window controls in TauriTitlebar.vue - Detect Tauri context:
window.__TAURI__exists → show custom titlebar, hide browser-nav elements - File upload: use Tauri
dialog.open()for native file picker; fall back to<input type="file">in browser - Deep link handling skeleton (for future push notification routing)
CSS Architecture
src/styles/main.scss imports in order:
_variables.scss— all CSS custom properties_typography.scss— font definitions, heading scale_animations.scss— keyframes, transition utilities- Tailwind base/reset
// _variables.scss
:root {
--color-base: #0d0d0d;
--color-surface: #161614;
--color-surface-2: #1e1e1b;
--color-cream: #f0ebe0;
--color-muted: #6b6860;
--color-signal: #c45c3a;
--color-signal-dim:#7a3822;
--color-border: rgba(240, 235, 224, 0.08);
--font-display: 'Instrument Serif', Georgia, serif;
--font-mono: 'DM Mono', 'Courier New', monospace;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 16px;
--shadow-card: 0 2px 24px rgba(0,0,0,0.6);
--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-base: 280ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-spring: 420ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
Quality Bar
Every component must meet these standards before considered done:
- Works in Chrome mobile emulation (375px) without horizontal scroll
- Works in desktop browser at 1280px+
- Works in Tauri window
- Loading states: skeleton loaders (not spinners) for feed and chat history
- Empty states: custom illustrated SVG + copy (no generic "Nothing here yet")
- Error states: inline error with retry action
- All forms validated client-side via Vuelidate before API call
- API errors surfaced as toasts (not console.error)
- Keyboard navigable (Tab order, Enter to submit, Escape to close modals)
- No
anyTypeScript types — use types from the generated API client - GSAP animations respect
prefers-reduced-motion(wrap inmatchMediacheck)
Implementation Order (follow strictly)
- Project scaffold + Tauri config + pnpm install
- Styles: variables, fonts, grain texture, AppShell skeleton
- Auth stores + Login/Register views + router guards
- API interceptor (Axios instance with token refresh)
- Pinia stores: auth, profile, ui
- Profile setup wizard (onboarding)
- Feed: store, FeedCard, FeedCardStack with swipe
- Feed filters drawer
- Matches view
- Chat: store (with polling), ChatsListView, ChatRoomView, media messages
- Dates: form with Leaflet map, list, status updates
- My Profile view + editor + media gallery
- Public profile view
- Reports modal
- Admin views (reports table, tags/cities management)
- Tauri titlebar + native file picker integration
- Polish pass: animations, transitions, empty states, error states
pnpm tauri buildsmoke test
Commands Reference
pnpm dev # Vite dev server (browser)
pnpm tauri dev # Tauri dev window
pnpm build # Vite build
pnpm tauri build # Tauri production build
Final Note
Do not scaffold placeholder components. Every component you create must be fully implemented — real logic, real styles, real API calls. If a feature is complex, implement it incrementally but completely within each file. The goal is a shippable, visually distinctive dating app that feels nothing like a template.
Start with step 1 and work sequentially. Announce each step as you begin it.