From 5c17bcf250ab784f93575ddc53e70f5bbc059901 Mon Sep 17 00:00:00 2001 From: corpi Date: Wed, 4 Mar 2026 14:44:45 +0900 Subject: [PATCH] =?UTF-8?q?style(control-center):=20Quick=20Controls=20?= =?UTF-8?q?=EC=9E=AC=EC=A7=88=EA=B3=BC=20=EB=AA=A8=EC=85=98=20=EA=B7=9C?= =?UTF-8?q?=EC=B9=99=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/ControlCenterSheetWidget.tsx | 36 ++++++++++++++----- .../space-sheet-shell/ui/SpaceSideSheet.tsx | 23 +++++++----- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/widgets/control-center-sheet/ui/ControlCenterSheetWidget.tsx b/src/widgets/control-center-sheet/ui/ControlCenterSheetWidget.tsx index 8614c6d..9ca7c8e 100644 --- a/src/widgets/control-center-sheet/ui/ControlCenterSheetWidget.tsx +++ b/src/widgets/control-center-sheet/ui/ControlCenterSheetWidget.tsx @@ -10,6 +10,7 @@ import { import { getRoomCardBackgroundStyle, type RoomTheme } from '@/entities/room'; import type { SoundPreset, TimerPreset } from '@/entities/session'; import { cn } from '@/shared/lib/cn'; +import { useReducedMotion } from '@/shared/lib/useReducedMotion'; interface ControlCenterSheetWidgetProps { plan: PlanTier; @@ -87,7 +88,14 @@ export const ControlCenterSheetWidget = ({ onApplyPack, onLockedClick, }: ControlCenterSheetWidgetProps) => { + const reducedMotion = useReducedMotion(); const isPro = plan === 'pro'; + const interactiveMotionClass = reducedMotion + ? 'transition-none' + : 'transition-[transform,background-color,border-color,box-shadow,color,opacity] duration-[220ms] ease-out'; + const colorMotionClass = reducedMotion + ? 'transition-none' + : 'transition-colors duration-[220ms] ease-out'; const selectedRoom = useMemo(() => { return rooms.find((room) => room.id === selectedRoomId) ?? rooms[0]; @@ -99,9 +107,12 @@ export const ControlCenterSheetWidget = ({ return (
-
+
-
+
{rooms.slice(0, 6).map((room) => { const selected = room.id === selectedRoomId; const locked = !isPro && PRO_LOCKED_ROOM_IDS.includes(room.id); @@ -119,7 +130,9 @@ export const ControlCenterSheetWidget = ({ onSelectRoom(room.id); }} className={cn( - 'relative h-24 w-[130px] shrink-0 overflow-hidden rounded-xl border text-left transition-transform hover:-translate-y-0.5', + 'relative h-24 w-[130px] shrink-0 snap-start overflow-hidden rounded-xl border text-left', + interactiveMotionClass, + reducedMotion ? '' : 'hover:-translate-y-0.5', selected ? 'border-sky-200/44 shadow-[0_8px_16px_rgba(56,189,248,0.18)]' : 'border-white/16', )} > @@ -136,7 +149,7 @@ export const ControlCenterSheetWidget = ({
-
+
{timerPresets.slice(0, 3).map((preset) => { @@ -156,7 +169,8 @@ export const ControlCenterSheetWidget = ({ onSelectTimer(preset.label); }} className={cn( - 'relative rounded-xl border px-2 py-2 text-xs transition-colors', + 'relative rounded-xl border px-2 py-2 text-xs', + colorMotionClass, selected ? 'border-sky-200/42 bg-sky-200/16 text-white' : 'border-white/18 bg-white/[0.04] text-white/74 hover:bg-white/[0.1]', @@ -170,7 +184,7 @@ export const ControlCenterSheetWidget = ({
-
+
{soundPresets.slice(0, 6).map((preset) => { @@ -190,7 +204,8 @@ export const ControlCenterSheetWidget = ({ onSelectSound(preset.id); }} className={cn( - 'rounded-full border px-3 py-1.5 text-xs transition-colors', + 'rounded-full border px-3 py-1.5 text-xs', + colorMotionClass, selected ? 'border-sky-200/42 bg-sky-200/16 text-white' : 'border-white/18 bg-white/[0.04] text-white/74 hover:bg-white/[0.1]', @@ -204,7 +219,7 @@ export const ControlCenterSheetWidget = ({
-
+
{QUICK_PACKS.map((pack) => { @@ -222,7 +237,10 @@ export const ControlCenterSheetWidget = ({ onApplyPack(pack.id); }} - className="relative rounded-xl border border-white/16 bg-white/[0.04] px-3 py-2 text-left transition-colors hover:bg-white/[0.1]" + className={cn( + 'relative rounded-xl border border-white/16 bg-white/[0.04] px-3 py-2 text-left hover:bg-white/[0.1]', + colorMotionClass, + )} > {locked ? : null}

{pack.name}

diff --git a/src/widgets/space-sheet-shell/ui/SpaceSideSheet.tsx b/src/widgets/space-sheet-shell/ui/SpaceSideSheet.tsx index 32d4d2c..db2d229 100644 --- a/src/widgets/space-sheet-shell/ui/SpaceSideSheet.tsx +++ b/src/widgets/space-sheet-shell/ui/SpaceSideSheet.tsx @@ -2,8 +2,7 @@ import { useEffect, useRef, useState, type ReactNode } from 'react'; import { cn } from '@/shared/lib/cn'; - -const SHEET_TRANSITION_MS = 300; +import { useReducedMotion } from '@/shared/lib/useReducedMotion'; interface SpaceSideSheetProps { open: boolean; @@ -28,6 +27,8 @@ export const SpaceSideSheet = ({ dismissible = true, headerAction, }: SpaceSideSheetProps) => { + const reducedMotion = useReducedMotion(); + const transitionMs = reducedMotion ? 0 : 260; const closeTimerRef = useRef(null); const [shouldRender, setShouldRender] = useState(open); const [visible, setVisible] = useState(open); @@ -53,7 +54,7 @@ export const SpaceSideSheet = ({ closeTimerRef.current = window.setTimeout(() => { setShouldRender(false); closeTimerRef.current = null; - }, SHEET_TRANSITION_MS); + }, transitionMs); return () => { if (closeTimerRef.current) { @@ -61,7 +62,7 @@ export const SpaceSideSheet = ({ closeTimerRef.current = null; } }; - }, [open]); + }, [open, transitionMs]); useEffect(() => { if (!open) { @@ -93,7 +94,8 @@ export const SpaceSideSheet = ({ aria-label="시트 닫기" onClick={onClose} className={cn( - 'fixed inset-0 z-40 bg-slate-950/14 backdrop-blur-[1px] transition-opacity duration-300', + 'fixed inset-0 z-40 bg-slate-950/14 backdrop-blur-[1px] transition-opacity', + reducedMotion ? 'duration-0' : 'duration-[240ms]', visible ? 'opacity-100' : 'pointer-events-none opacity-0', )} /> @@ -101,7 +103,8 @@ export const SpaceSideSheet = ({
@@ -109,15 +112,17 @@ export const SpaceSideSheet = ({