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
7.5 KiB
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
gentlespring
Board List (BoardList.tsx)
- Board cards stagger in on mount using
staggerContainer - Each BoardCard uses
fadeSlideUpentrance - 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
bouncypreset whileHoverscale 1.02 with shadow elevationwhileTapscale 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
whileHoverandwhileTapmicro-animations - Saving status text fades in/out with AnimatePresence
Toast Notifications (ToastContainer.tsx)
- Migrate to
wobblyspring for extra personality - Exit slides down + fades
Settings Dialog
- Tab content crossfades with AnimatePresence
- Accent color swatches have
whileHoverscale 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 usinglayoutprop - 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:
Minusicon from Lucide →getCurrentWindow().minimize() - Maximize/Restore:
Square/Copyicon →getCurrentWindow().toggleMaximize() - Close:
Xicon →getCurrentWindow().close()
Styling:
- 32x32px hit area, 16x16px icons
- Default:
text-pylon-text-secondary - Hover: Background with accent at 10% opacity
- Close hover:
pylon-dangerat 15% opacity (red highlight — convention) - All have Framer Motion
whileHoverandwhileTapsprings
State tracking:
- Listen to
getCurrentWindow().onResized()for maximize state - Query
isMaximized()on mount for initial icon data-tauri-drag-regionstays 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 transitionssrc/index.css— dark mode color value updatessrc/components/layout/TopBar.tsx— window controls, motion on buttonssrc/components/layout/AppShell.tsx— support page transition wrappersrc/components/boards/BoardList.tsx— stagger animation on board cardssrc/components/boards/BoardCard.tsx— fadeSlideUp entrance, hover/tapsrc/components/board/BoardView.tsx— column stagger, gesture-reactive drag overlaysrc/components/board/KanbanColumn.tsx— card stagger, layout animation for reordersrc/components/board/CardThumbnail.tsx— shared layoutId, bouncy preset, hover/tapsrc/components/board/ColumnHeader.tsx— dropdown animationsrc/components/card-detail/CardDetailModal.tsx— shared layout animation (hero), content staggersrc/components/toast/ToastContainer.tsx— wobbly springsrc/components/settings/SettingsDialog.tsx— tab crossfade, swatch hoversrc/components/command-palette/CommandPalette.tsx— result staggersrc/components/shortcuts/ShortcutHelpModal.tsx— entrance animationsrc-tauri/tauri.conf.json— decorations: false
Implementation Order
- Motion system foundation (
src/lib/motion.ts) - Dark mode CSS variable updates
- Custom titlebar (Tauri config + WindowControls)
- Page transitions (App.tsx + AnimatePresence)
- Board list animations (stagger + BoardCard)
- Board view column stagger + card stagger
- Card thumbnail hover/tap + shared layoutId
- Card detail modal shared layout animation
- Gesture-reactive drag overlay
- Micro-interactions (TopBar, ColumnHeader dropdowns)
- Toast, Settings, Command Palette, ShortcutHelp animations
- Polish pass — verify all springs feel cohesive, test reduced-motion