feat: add micro-animations to TopBar, toasts, settings, and shortcut help

This commit is contained in:
Your Name
2026-02-15 21:01:10 +02:00
parent 436f8fecb9
commit a226eabba4
4 changed files with 49 additions and 15 deletions

View File

@@ -1,4 +1,6 @@
import { useState, useRef, useEffect, useCallback } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { springs } from "@/lib/motion";
import { ArrowLeft, Settings, Search, Undo2, Redo2, SlidersHorizontal } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
@@ -199,11 +201,20 @@ export function TopBar() {
</DropdownMenuContent>
</DropdownMenu>
)}
<AnimatePresence mode="wait">
{savingStatus && (
<span className="mr-2 font-mono text-xs text-pylon-text-secondary">
<motion.span
key={savingStatus}
className="mr-2 font-mono text-xs text-pylon-text-secondary"
initial={{ opacity: 0, y: -4 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 4 }}
transition={springs.snappy}
>
{savingStatus}
</span>
</motion.span>
)}
</AnimatePresence>
<Tooltip>
<TooltipTrigger asChild>

View File

@@ -1,4 +1,6 @@
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { springs, scaleIn, microInteraction } from "@/lib/motion";
import {
Sun, Moon, Monitor, RotateCcw,
} from "lucide-react";
@@ -120,7 +122,16 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
</div>
{/* Tab content */}
<div className="flex flex-col gap-5 pt-1">
<AnimatePresence mode="wait">
<motion.div
key={tab}
className="flex flex-col gap-5 pt-1"
variants={scaleIn}
initial="hidden"
animate="visible"
exit="exit"
transition={springs.snappy}
>
{tab === "appearance" && (
<>
{/* Theme */}
@@ -193,16 +204,19 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
? "oklch(50% 0 0)"
: `oklch(55% 0.12 ${hue})`;
return (
<button
<motion.button
key={hue}
type="button"
onClick={() => setAccentColor(hue)}
className="size-7 rounded-full transition-transform hover:scale-110"
className="size-7 rounded-full"
style={{
backgroundColor: bg,
outline: settings.accentColor === hue ? "2px solid currentColor" : "none",
outlineOffset: "2px",
}}
whileHover={microInteraction.hover}
whileTap={microInteraction.tap}
transition={springs.snappy}
aria-label={label}
title={label}
/>
@@ -278,7 +292,8 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
</p>
</div>
)}
</div>
</motion.div>
</AnimatePresence>
</DialogContent>
</Dialog>
);

View File

@@ -1,3 +1,5 @@
import { motion } from "framer-motion";
import { springs, staggerContainer, fadeSlideUp } from "@/lib/motion";
import {
Dialog,
DialogContent,
@@ -42,9 +44,14 @@ export function ShortcutHelpModal({ open, onOpenChange }: ShortcutHelpModalProps
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-4">
<motion.div
className="flex flex-col gap-4"
variants={staggerContainer(0.06)}
initial="hidden"
animate="visible"
>
{SHORTCUT_GROUPS.map((group) => (
<div key={group.category}>
<motion.div key={group.category} variants={fadeSlideUp} transition={springs.bouncy}>
<h4 className="mb-2 font-mono text-xs font-semibold uppercase tracking-widest text-pylon-text-secondary">
{group.category}
</h4>
@@ -58,9 +65,9 @@ export function ShortcutHelpModal({ open, onOpenChange }: ShortcutHelpModalProps
</div>
))}
</div>
</div>
</motion.div>
))}
</div>
</motion.div>
</DialogContent>
</Dialog>
);

View File

@@ -1,4 +1,5 @@
import { AnimatePresence, motion } from "framer-motion";
import { springs } from "@/lib/motion";
import { useToastStore } from "@/stores/toast-store";
const TYPE_STYLES = {
@@ -18,8 +19,8 @@ export function ToastContainer() {
key={toast.id}
initial={{ opacity: 0, y: 20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.95 }}
transition={{ type: "spring", stiffness: 400, damping: 25 }}
exit={{ opacity: 0, y: 20, scale: 0.9 }}
transition={springs.wobbly}
className={`pointer-events-auto rounded-lg border px-4 py-2 text-sm shadow-md ${TYPE_STYLES[toast.type]}`}
>
{toast.message}