docs: add motion, dark mode & custom titlebar design
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
This commit is contained in:
188
docs/plans/2026-02-15-motion-darkmode-titlebar-design.md
Normal file
188
docs/plans/2026-02-15-motion-darkmode-titlebar-design.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user