Design document covering three visual improvements: - Playful bouncy Framer Motion animations for every interaction - Subtle dark mode lift for HDR monitors (18-22% → 25-30% lightness) - Custom window titlebar merged into TopBar with accent-colored controls
189 lines
7.5 KiB
Markdown
189 lines
7.5 KiB
Markdown
# Motion, Dark Mode & Custom Titlebar Design
|
||
|
||
**Date:** 2026-02-15
|
||
**Goal:** Add playful bouncy animations everywhere, lighten dark mode for HDR monitors, and implement custom window decorations merged into the TopBar
|
||
**Approach:** Centralized motion system + CSS variable tuning + Tauri decoration override
|
||
|
||
---
|
||
|
||
## 1. Motion System Foundation
|
||
|
||
Create `src/lib/motion.ts` — shared animation config imported by all components.
|
||
|
||
### Spring Presets (Bouncy Profile)
|
||
- **bouncy** — stiffness: 400, damping: 15, mass: 0.8 (main preset, visible overshoot)
|
||
- **snappy** — stiffness: 500, damping: 20 (micro-interactions — buttons, toggles)
|
||
- **gentle** — stiffness: 200, damping: 20 (larger elements — modals, page transitions)
|
||
- **wobbly** — stiffness: 300, damping: 10 (playful emphasis — toasts, notifications)
|
||
|
||
### Reusable Variants
|
||
- `fadeSlideUp` — enters from below with opacity fade (cards, list items)
|
||
- `fadeSlideDown` — enters from above (dropdowns, menus)
|
||
- `scaleIn` — scales from 0.9 to 1 with bounce (modals, popovers)
|
||
- `staggerContainer` — parent variant that staggers children
|
||
|
||
### Stagger Helper
|
||
`staggerChildren(delay = 0.04)` — generates parent transition variants for cascading entrances.
|
||
|
||
---
|
||
|
||
## 2. Component-by-Component Motion Rollout
|
||
|
||
### Page Transitions (App.tsx)
|
||
- Wrap view switch in `AnimatePresence mode="wait"`
|
||
- Board-list exits with fade+slide-left, board enters with fade+slide-right
|
||
- Uses `gentle` spring
|
||
|
||
### Board List (BoardList.tsx)
|
||
- Board cards stagger in on mount using `staggerContainer`
|
||
- Each BoardCard uses `fadeSlideUp` entrance
|
||
- Empty state fades in
|
||
|
||
### Board View (BoardView.tsx)
|
||
- Columns stagger in from left to right on mount (0.06s delay each)
|
||
- Cards within each column stagger in (0.03s delay)
|
||
|
||
### Card Thumbnails (CardThumbnail.tsx)
|
||
- Migrate existing spring to shared `bouncy` preset
|
||
- `whileHover` scale 1.02 with shadow elevation
|
||
- `whileTap` scale 0.98
|
||
|
||
### Card Detail Modal (CardDetailModal.tsx)
|
||
- **Shared layout animation** — CardThumbnail gets `layoutId={card-${card.id}}`, modal wrapper gets same layoutId
|
||
- Card morphs into the modal on open — hero transition
|
||
- Backdrop blurs in with animated opacity + backdropFilter
|
||
- Modal content sections stagger in after layout animation
|
||
|
||
### Column Header (ColumnHeader.tsx)
|
||
- Dropdown menu items stagger in with `fadeSlideDown`
|
||
|
||
### TopBar
|
||
- Buttons have `whileHover` and `whileTap` micro-animations
|
||
- Saving status text fades in/out with AnimatePresence
|
||
|
||
### Toast Notifications (ToastContainer.tsx)
|
||
- Migrate to `wobbly` spring for extra personality
|
||
- Exit slides down + fades
|
||
|
||
### Settings Dialog
|
||
- Tab content crossfades with AnimatePresence
|
||
- Accent color swatches have `whileHover` scale pulse
|
||
|
||
### Command Palette
|
||
- Results stagger in as you type
|
||
|
||
---
|
||
|
||
## 3. Gesture-Reactive Drag & Drop
|
||
|
||
Override dnd-kit's default drag overlay with Framer Motion-powered custom overlay.
|
||
|
||
- **On drag start:** Card lifts with `scale: 1.05`, box-shadow, slight rotate based on grab offset
|
||
- **During drag:** Card tilts based on pointer velocity (useMotionValue + useTransform). Max tilt: ~5 degrees
|
||
- **On drop:** Spring back to `scale: 1, rotate: 0`. Target column cards spring apart using `layout` prop
|
||
- dnd-kit handles position/sorting logic; we layer gesture transforms on top
|
||
|
||
---
|
||
|
||
## 4. Dark Mode — Subtle Lift for HDR
|
||
|
||
### Pylon Dark Variables (in `.dark {}`)
|
||
|
||
| Variable | Current | New |
|
||
|----------|---------|-----|
|
||
| `--pylon-bg` | `oklch(18% 0.01 50)` | `oklch(25% 0.012 50)` |
|
||
| `--pylon-surface` | `oklch(22% 0.01 50)` | `oklch(29% 0.012 50)` |
|
||
| `--pylon-column` | `oklch(20% 0.012 50)` | `oklch(27% 0.014 50)` |
|
||
| `--pylon-text` | `oklch(90% 0.01 50)` | `oklch(92% 0.01 50)` |
|
||
| `--pylon-text-secondary` | `oklch(55% 0.01 50)` | `oklch(58% 0.01 50)` |
|
||
| `--pylon-accent` | `oklch(60% 0.12 160)` | `oklch(62% 0.13 160)` |
|
||
| `--pylon-danger` | `oklch(60% 0.18 25)` | `oklch(62% 0.18 25)` |
|
||
|
||
### Shadcn Dark Variables
|
||
|
||
| Variable | Current | New |
|
||
|----------|---------|-----|
|
||
| `--background` | `oklch(0.145 0 0)` | `oklch(0.22 0 0)` |
|
||
| `--card`, `--popover` | `oklch(0.205 0 0)` | `oklch(0.27 0 0)` |
|
||
| `--secondary`, `--muted`, `--accent` | `oklch(0.269 0 0)` | `oklch(0.32 0 0)` |
|
||
| `--border` | `oklch(1 0 0 / 10%)` | `oklch(1 0 0 / 12%)` |
|
||
| `--input` | `oklch(1 0 0 / 15%)` | `oklch(1 0 0 / 18%)` |
|
||
| `--sidebar` | `oklch(0.205 0 0)` | `oklch(0.27 0 0)` |
|
||
| `--sidebar-accent` | `oklch(0.269 0 0)` | `oklch(0.32 0 0)` |
|
||
|
||
Color scheme (hue 50 warmth) preserved. Slight chroma bump for HDR vibrancy.
|
||
|
||
---
|
||
|
||
## 5. Custom Window Titlebar
|
||
|
||
### Tauri Configuration
|
||
Set `"decorations": false` in `tauri.conf.json` to remove native OS titlebar.
|
||
|
||
### TopBar Integration
|
||
Window controls added to far right of existing TopBar, after a thin vertical separator:
|
||
|
||
```
|
||
[Back] .......... [Board Title] .......... [Undo][Redo][Settings][Save][Search][Gear] | [—][□][×]
|
||
```
|
||
|
||
### WindowControls Component
|
||
Inline in TopBar or extracted to `src/components/layout/WindowControls.tsx`.
|
||
|
||
**Buttons:**
|
||
- Minimize: `Minus` icon from Lucide → `getCurrentWindow().minimize()`
|
||
- Maximize/Restore: `Square` / `Copy` icon → `getCurrentWindow().toggleMaximize()`
|
||
- Close: `X` icon → `getCurrentWindow().close()`
|
||
|
||
**Styling:**
|
||
- 32x32px hit area, 16x16px icons
|
||
- Default: `text-pylon-text-secondary`
|
||
- Hover: Background with accent at 10% opacity
|
||
- Close hover: `pylon-danger` at 15% opacity (red highlight — convention)
|
||
- All have Framer Motion `whileHover` and `whileTap` springs
|
||
|
||
**State tracking:**
|
||
- Listen to `getCurrentWindow().onResized()` for maximize state
|
||
- Query `isMaximized()` on mount for initial icon
|
||
- `data-tauri-drag-region` stays on header; window buttons do NOT propagate drag
|
||
|
||
---
|
||
|
||
## Files Affected
|
||
|
||
### New Files
|
||
- `src/lib/motion.ts` — shared spring presets, variants, helpers
|
||
|
||
### Modified Files
|
||
- `src/App.tsx` — AnimatePresence page transitions
|
||
- `src/index.css` — dark mode color value updates
|
||
- `src/components/layout/TopBar.tsx` — window controls, motion on buttons
|
||
- `src/components/layout/AppShell.tsx` — support page transition wrapper
|
||
- `src/components/boards/BoardList.tsx` — stagger animation on board cards
|
||
- `src/components/boards/BoardCard.tsx` — fadeSlideUp entrance, hover/tap
|
||
- `src/components/board/BoardView.tsx` — column stagger, gesture-reactive drag overlay
|
||
- `src/components/board/KanbanColumn.tsx` — card stagger, layout animation for reorder
|
||
- `src/components/board/CardThumbnail.tsx` — shared layoutId, bouncy preset, hover/tap
|
||
- `src/components/board/ColumnHeader.tsx` — dropdown animation
|
||
- `src/components/card-detail/CardDetailModal.tsx` — shared layout animation (hero), content stagger
|
||
- `src/components/toast/ToastContainer.tsx` — wobbly spring
|
||
- `src/components/settings/SettingsDialog.tsx` — tab crossfade, swatch hover
|
||
- `src/components/command-palette/CommandPalette.tsx` — result stagger
|
||
- `src/components/shortcuts/ShortcutHelpModal.tsx` — entrance animation
|
||
- `src-tauri/tauri.conf.json` — decorations: false
|
||
|
||
## Implementation Order
|
||
|
||
1. Motion system foundation (`src/lib/motion.ts`)
|
||
2. Dark mode CSS variable updates
|
||
3. Custom titlebar (Tauri config + WindowControls)
|
||
4. Page transitions (App.tsx + AnimatePresence)
|
||
5. Board list animations (stagger + BoardCard)
|
||
6. Board view column stagger + card stagger
|
||
7. Card thumbnail hover/tap + shared layoutId
|
||
8. Card detail modal shared layout animation
|
||
9. Gesture-reactive drag overlay
|
||
10. Micro-interactions (TopBar, ColumnHeader dropdowns)
|
||
11. Toast, Settings, Command Palette, ShortcutHelp animations
|
||
12. Polish pass — verify all springs feel cohesive, test reduced-motion
|