6.9 KiB
6.9 KiB
Subtitle Preset Grid Redesign - Design Document
Date: 2026-04-06
Scope: Redesign preset preview cards in Caption Settings step to match uploaded video aspect ratio with modern visual refresh
Overview
Redesign the subtitle preset selection grid to:
- Display preset previews with the same aspect ratio as the uploaded video
- Apply a modern visual refresh consistent with the app's design language
- Show style characteristics (font, colors) as subtle hints
- Maintain responsive layout across screen sizes
Core Functionality
Dynamic Aspect Ratio
Data Flow:
- Fetch video metadata via
GET /api/media/mediafiles/{media_file_id}/usingprimaryFileIdfrom WizardContext - Extract
widthandheightfromMediaFileReadresponse - Calculate aspect ratio:
width / height - Apply as CSS
aspect-ratioto preset cards via inline style or CSS variable - Handle loading state while fetching metadata
- Fallback to 16:9 if no video is uploaded or API error occurs
Implementation Notes:
- Store aspect ratio in WizardContext alongside other video metadata
- Update ratio when
primaryFileIdchanges - Cards use container queries for responsive sizing
Visual Design (5 Pillars Applied)
1. Typography with Character
- Keep existing font system (consistent with app)
- Style name:
font-weight: 500,font-size: 14px - Characteristic labels:
font-size: 12px, muted color (--gray-10)
2. Committed Color & Theme
- Uses Catppuccin Mocha palette matching the project:
- Canvas:
--bg-canvas: #11111b - Cards:
--bg-default: #1e1e2e - Surfaces:
--bg-surface: #313244 - Borders:
--border-default: #45475a,--border-subtle: #313244 - Text:
--text-primary: #cdd6f4,--text-secondary: #bac2de,--text-tertiary: #9399b2
- Canvas:
- Selected state: purple accent (
--purple-400: #cba6f7) with glow shadow - Card hover: border transitions to purple accent
- System badge: purple-100 background with purple-400 text
- Checkmark indicator on selected card (top-right corner)
3. Purposeful Motion
- Cards fade in with staggered animation (50ms delay per card)
- Smooth border-color transition on hover (150ms ease)
- Selection change: immediate border color change
- Loading skeleton: shimmer animation
4. Brave Spatial Composition
- CSS Grid with
auto-fillandminmax(200px, 1fr) - Consistent 16px gap between cards
- Cards maintain video aspect ratio without stretching
- Responsive: more columns on wide screens, fewer on narrow
5. Atmosphere & Depth
- Card background: subtle gradient overlay for depth
- Selected card: elevated with
box-shadow+ accent glow - Dark preview background (
#0c0a1a) preserved from existing StylePreview - Rounded corners:
border-radius: 12px
Component Structure
PresetCard
┌──────────────────────────────────────┐
│ │
│ [StylePreview Component] │ ← Dynamic aspect-ratio
│ "Пример субтитров" │ based on video
│ │
├──────────────────────────────────────┤
│ Style Name [Системный] │ ← Footer
│ Lobster · Yellow accent │ ← Characteristics (subtle)
└──────────────────────────────────────┘
Props:
preset: CaptionPresetReadisSelected: booleanaspectRatio: number(width/height, e.g., 1.777 for 16:9)onSelect: () => voidonEdit: () => voidonDelete: () => void
StylePreview Updates
New Props:
aspectRatio?: number- overrides default 9/16
Behavior:
- Uses passed
aspectRatiofor container sizing - Falls back to 9/16 if not provided
- Maintains all existing text styling logic
PresetGrid Updates
New Behavior:
- Fetches video metadata via
useVideoMetadata()hook - Passes
aspectRatioto all PresetCard children - Shows skeleton loading state while fetching
- Responsive grid layout
Style Characteristics Display
Each card footer shows:
- Font family (e.g., "Lobster", "Inter") - extracted from
preset.style_config.text.font_family - Accent color - small color dot + name if distinct from default
- Hidden on cards narrower than 180px (responsive)
Format:
{font_family} · {accent_color_name}
Example: Lobster · Желтый or Inter · Неоновый
Loading State
Skeleton Card:
- Same aspect ratio as target (default 16:9 while loading)
- Shimmer animation on preview area
- Gray placeholder for text
- 4-6 skeleton cards shown while loading
Responsive Behavior
| Screen Width | Grid Columns | Card Min Width |
|---|---|---|
| < 480px | 2 | 140px |
| 480-768px | 3 | 160px |
| 768-1200px | 4 | 180px |
| > 1200px | 5-6 | 200px |
API Integration
New Hook: useVideoMetadata
function useVideoMetadata(fileId: string | null) {
return api.useQuery(
"get",
"/api/media/mediafiles/{media_file_id}/",
{ params: { path: { media_file_id: fileId ?? "" } } },
{ enabled: !!fileId }
)
}
Aspect Ratio Calculation
const aspectRatio = useMemo(() => {
if (!mediaFile?.width || !mediaFile?.height) return 16 / 9
return mediaFile.width / mediaFile.height
}, [mediaFile])
Edge Cases
- No video uploaded: Fall back to 16:9 aspect ratio
- Video metadata unavailable: Show error toast, fall back to 16:9
- Very wide video (>21:9): Cap max card width to prevent overflow
- Very tall video (9:16+): Limit max height, allow scrolling if needed
- No presets: Show empty state with "Создать пресет" card only
Files Modified
src/features/project/CaptionSettingsStep/PresetGrid.tsx- Grid logic, aspect ratio distributionsrc/features/project/CaptionSettingsStep/PresetGrid.module.scss- Grid styles, responsive layoutsrc/features/project/CaptionSettingsStep/StylePreview.tsx- Accept aspect ratio propsrc/features/project/CaptionSettingsStep/StylePreview.module.scss- Dynamic sizingsrc/features/project/CaptionSettingsStep/useVideoMetadata.ts- New hook (or inline in PresetGrid)
Acceptance Criteria
- Preset cards display with uploaded video's aspect ratio
- Grid is responsive and works on mobile/desktop
- Loading state shows skeleton cards
- Style characteristics (font, color) visible on cards
- Selected state clearly visible with accent border
- Hover effects smooth and purposeful
- Fallback to 16:9 when no video available
- All existing functionality preserved (select, edit, delete, create)