feat(space): break와 return 톤 분리
This commit is contained in:
@@ -43,6 +43,10 @@ Last Updated: 2026-03-14
|
||||
- tray 폭과 열림 높이를 키워 긴 한국어 카피가 잘리지 않게 조정
|
||||
- eyebrow / title / body의 typography hierarchy와 line-height를 재정렬
|
||||
- option row spacing, radius, chevron 위치를 보정해 급조된 버튼 묶음 느낌을 완화
|
||||
- `/space` Pause / Break / Return tone 분리 1차 구현:
|
||||
- `Return(focus)`와 `Return(break)`가 같은 tray처럼 보이지 않도록 break tray에 emerald tint release tone 도입
|
||||
- `Goal Complete`의 `잠깐 쉬기` 선택도 같은 break 계열 material로 연결
|
||||
- timer HUD는 break phase에서 더 가벼운 emerald 계열 glass로 보정해 focus/pause와 구분되게 조정
|
||||
|
||||
- Focus Entry Surface / Execution Surface 재정의:
|
||||
- `/app`을 planning home이 아니라 hero-first focus entry surface로 재구성
|
||||
|
||||
@@ -48,6 +48,10 @@ Last Updated: 2026-03-14
|
||||
- tray 폭과 max-height를 늘려 한국어 제목/설명 잘림을 줄였다.
|
||||
- title/body line-height와 spacing을 다시 잡아 임시 패널 느낌을 줄였다.
|
||||
- option row의 radius, padding, chevron 정렬을 보정해 더 차분한 recovery panel처럼 읽히게 했다.
|
||||
- `Pause / Break / Return`의 감정 톤 분리를 시작했다.
|
||||
- `Return(break)`은 focus 복귀 tray와 같은 재질을 쓰지 않고, 더 부드러운 emerald tint release tone으로 분리했다.
|
||||
- `Goal Complete`의 `잠깐 쉬기` 선택도 같은 release tone으로 연결했다.
|
||||
- timer HUD도 break phase에서는 더 가벼운 emerald 계열 material로 바뀌어 pause/focus와 다르게 읽히도록 정리 중이다.
|
||||
- `/app`을 single-goal commitment gate로 다시 줄였다.
|
||||
- 2-step ritual setup을 제거했다.
|
||||
- current session이 있으면 `Resume` UI만 보여주고, `/space`로 이어가기만 제안한다.
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
HUD_FIELD,
|
||||
HUD_OPTION_CHEVRON,
|
||||
HUD_OPTION_ROW,
|
||||
HUD_OPTION_ROW_BREAK,
|
||||
HUD_OPTION_ROW_PRIMARY,
|
||||
HUD_TEXT_LINK,
|
||||
HUD_TEXT_LINK_STRONG,
|
||||
@@ -190,17 +191,17 @@ export const GoalCompleteSheet = ({
|
||||
type="button"
|
||||
onClick={onRest}
|
||||
disabled={isSubmitting}
|
||||
className={HUD_OPTION_ROW}
|
||||
className={cn(HUD_OPTION_ROW, HUD_OPTION_ROW_BREAK)}
|
||||
>
|
||||
<div>
|
||||
<p className="text-[13px] font-medium tracking-[0.01em] text-white/78">
|
||||
{copy.space.goalComplete.restButton}
|
||||
</p>
|
||||
<p className="mt-1 text-[12px] text-white/44">
|
||||
<p className="mt-1 text-[12px] text-emerald-50/56">
|
||||
{copy.space.goalComplete.restDescription}
|
||||
</p>
|
||||
</div>
|
||||
<span aria-hidden className={HUD_OPTION_CHEVRON}>→</span>
|
||||
<span aria-hidden className={cn(HUD_OPTION_CHEVRON, 'text-emerald-100/34 group-hover:text-emerald-100/58')}>→</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -5,9 +5,15 @@ import { cn } from '@/shared/lib/cn';
|
||||
import {
|
||||
HUD_OPTION_CHEVRON,
|
||||
HUD_OPTION_ROW,
|
||||
HUD_OPTION_ROW_BREAK,
|
||||
HUD_OPTION_ROW_PRIMARY,
|
||||
HUD_RETURN_BODY,
|
||||
HUD_RETURN_TITLE,
|
||||
HUD_TRAY_HAIRLINE_BREAK,
|
||||
HUD_TRAY_HAIRLINE,
|
||||
HUD_TRAY_LAYER_BREAK,
|
||||
HUD_TRAY_LAYER,
|
||||
HUD_TRAY_SHELL_BREAK,
|
||||
HUD_TRAY_SHELL,
|
||||
} from './overlayStyles';
|
||||
|
||||
@@ -42,43 +48,46 @@ export const ReturnPrompt = ({
|
||||
)}
|
||||
aria-hidden={!open}
|
||||
>
|
||||
<section className={HUD_TRAY_SHELL}>
|
||||
<div aria-hidden className={HUD_TRAY_LAYER} />
|
||||
<div aria-hidden className={HUD_TRAY_HAIRLINE} />
|
||||
<section className={cn(isBreakReturn ? HUD_TRAY_SHELL_BREAK : HUD_TRAY_SHELL)}>
|
||||
<div aria-hidden className={isBreakReturn ? HUD_TRAY_LAYER_BREAK : HUD_TRAY_LAYER} />
|
||||
<div aria-hidden className={isBreakReturn ? HUD_TRAY_HAIRLINE_BREAK : HUD_TRAY_HAIRLINE} />
|
||||
|
||||
<div className="relative">
|
||||
<p className="text-[11px] font-medium tracking-[0.08em] text-white/42">
|
||||
<div className="relative px-6 py-5">
|
||||
<p className={cn(
|
||||
'text-[11px] font-medium tracking-[0.12em]',
|
||||
isBreakReturn ? 'text-emerald-100/54' : 'text-white/42',
|
||||
)}>
|
||||
{copy.space.focusHud.returnPromptEyebrow}
|
||||
</p>
|
||||
<h3 className="mt-1 text-[1rem] font-medium tracking-tight text-white/94">
|
||||
<h3 className={cn(HUD_RETURN_TITLE, isBreakReturn ? 'text-white/96' : undefined)}>
|
||||
{isBreakReturn
|
||||
? copy.space.focusHud.returnPromptBreakTitle
|
||||
: copy.space.focusHud.returnPromptFocusTitle}
|
||||
</h3>
|
||||
<p className="mt-1 text-[13px] text-white/58">
|
||||
<p className={cn(HUD_RETURN_BODY, isBreakReturn ? 'text-emerald-50/62' : undefined)}>
|
||||
{isBreakReturn
|
||||
? copy.space.focusHud.returnPromptBreakDescription
|
||||
: copy.space.focusHud.returnPromptFocusDescription}
|
||||
</p>
|
||||
|
||||
<div className="mt-4 space-y-2">
|
||||
<div className="mt-5 space-y-2.5 border-t pt-4 border-white/8">
|
||||
{isBreakReturn ? (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onRest}
|
||||
disabled={isBusy}
|
||||
className={cn(HUD_OPTION_ROW, HUD_OPTION_ROW_PRIMARY)}
|
||||
className={cn(HUD_OPTION_ROW, HUD_OPTION_ROW_BREAK)}
|
||||
>
|
||||
<div>
|
||||
<p className="text-[13px] font-semibold tracking-[0.01em] text-white/90">
|
||||
<div className="min-w-0">
|
||||
<p className="text-[14px] font-semibold leading-[1.35] tracking-[-0.01em] text-white/92">
|
||||
{copy.space.focusHud.returnPromptRest}
|
||||
</p>
|
||||
<p className="mt-1 text-[12px] text-white/48">
|
||||
<p className="mt-1.5 max-w-[20rem] text-[12px] leading-[1.5] text-emerald-50/56">
|
||||
{copy.space.focusHud.returnPromptRestHint}
|
||||
</p>
|
||||
</div>
|
||||
<span aria-hidden className={HUD_OPTION_CHEVRON}>→</span>
|
||||
<span aria-hidden className={cn(HUD_OPTION_CHEVRON, 'mt-1 text-emerald-100/34 group-hover:text-emerald-100/58')}>→</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -86,15 +95,15 @@ export const ReturnPrompt = ({
|
||||
disabled={isBusy}
|
||||
className={HUD_OPTION_ROW}
|
||||
>
|
||||
<div>
|
||||
<p className="text-[13px] font-medium tracking-[0.01em] text-white/78">
|
||||
<div className="min-w-0">
|
||||
<p className="text-[14px] font-medium leading-[1.35] tracking-[-0.01em] text-white/82">
|
||||
{copy.space.focusHud.returnPromptNext}
|
||||
</p>
|
||||
<p className="mt-1 text-[12px] text-white/44">
|
||||
<p className="mt-1.5 max-w-[20rem] text-[12px] leading-[1.5] text-white/46">
|
||||
{copy.space.focusHud.returnPromptNextHint}
|
||||
</p>
|
||||
</div>
|
||||
<span aria-hidden className={HUD_OPTION_CHEVRON}>→</span>
|
||||
<span aria-hidden className={cn(HUD_OPTION_CHEVRON, 'mt-1')}>→</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -102,15 +111,15 @@ export const ReturnPrompt = ({
|
||||
disabled={isBusy}
|
||||
className={HUD_OPTION_ROW}
|
||||
>
|
||||
<div>
|
||||
<p className="text-[13px] font-medium tracking-[0.01em] text-white/78">
|
||||
<div className="min-w-0">
|
||||
<p className="text-[14px] font-medium leading-[1.35] tracking-[-0.01em] text-white/82">
|
||||
{copy.space.focusHud.returnPromptRefocus}
|
||||
</p>
|
||||
<p className="mt-1 text-[12px] text-white/44">
|
||||
<p className="mt-1.5 max-w-[20rem] text-[12px] leading-[1.5] text-white/46">
|
||||
{copy.space.focusHud.returnPromptRefocusHint}
|
||||
</p>
|
||||
</div>
|
||||
<span aria-hidden className={HUD_OPTION_CHEVRON}>→</span>
|
||||
<span aria-hidden className={cn(HUD_OPTION_CHEVRON, 'mt-1')}>→</span>
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
@@ -121,15 +130,15 @@ export const ReturnPrompt = ({
|
||||
disabled={isBusy}
|
||||
className={cn(HUD_OPTION_ROW, HUD_OPTION_ROW_PRIMARY)}
|
||||
>
|
||||
<div>
|
||||
<p className="text-[13px] font-semibold tracking-[0.01em] text-white/90">
|
||||
<div className="min-w-0">
|
||||
<p className="text-[14px] font-semibold leading-[1.35] tracking-[-0.01em] text-white/92">
|
||||
{copy.space.focusHud.returnPromptContinue}
|
||||
</p>
|
||||
<p className="mt-1 text-[12px] text-white/48">
|
||||
<p className="mt-1.5 max-w-[20rem] text-[12px] leading-[1.5] text-white/48">
|
||||
{copy.space.focusHud.returnPromptContinueHint}
|
||||
</p>
|
||||
</div>
|
||||
<span aria-hidden className={HUD_OPTION_CHEVRON}>→</span>
|
||||
<span aria-hidden className={cn(HUD_OPTION_CHEVRON, 'mt-1')}>→</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -137,15 +146,15 @@ export const ReturnPrompt = ({
|
||||
disabled={isBusy}
|
||||
className={HUD_OPTION_ROW}
|
||||
>
|
||||
<div>
|
||||
<p className="text-[13px] font-medium tracking-[0.01em] text-white/78">
|
||||
<div className="min-w-0">
|
||||
<p className="text-[14px] font-medium leading-[1.35] tracking-[-0.01em] text-white/82">
|
||||
{copy.space.focusHud.returnPromptRefocus}
|
||||
</p>
|
||||
<p className="mt-1 text-[12px] text-white/44">
|
||||
<p className="mt-1.5 max-w-[20rem] text-[12px] leading-[1.5] text-white/46">
|
||||
{copy.space.focusHud.returnPromptRefocusHint}
|
||||
</p>
|
||||
</div>
|
||||
<span aria-hidden className={HUD_OPTION_CHEVRON}>→</span>
|
||||
<span aria-hidden className={cn(HUD_OPTION_CHEVRON, 'mt-1')}>→</span>
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
export const HUD_TRAY_SHELL =
|
||||
'pointer-events-auto relative mt-3 w-full overflow-hidden rounded-[22px] border border-white/10 bg-[#101318]/30 text-white shadow-[0_12px_28px_rgba(2,6,23,0.14)] backdrop-blur-[8px] backdrop-saturate-125';
|
||||
|
||||
export const HUD_TRAY_SHELL_BREAK =
|
||||
'pointer-events-auto relative mt-3 w-full overflow-hidden rounded-[22px] border border-emerald-200/14 bg-[rgba(10,20,18,0.34)] text-white shadow-[0_12px_28px_rgba(2,6,23,0.12)] backdrop-blur-[8px] backdrop-saturate-125';
|
||||
|
||||
export const HUD_TRAY_LAYER =
|
||||
'pointer-events-none absolute inset-0 rounded-[22px] bg-[linear-gradient(180deg,rgba(255,255,255,0.08)_0%,rgba(255,255,255,0.025)_24%,rgba(255,255,255,0.01)_100%)]';
|
||||
|
||||
export const HUD_TRAY_LAYER_BREAK =
|
||||
'pointer-events-none absolute inset-0 rounded-[22px] bg-[linear-gradient(180deg,rgba(110,231,183,0.10)_0%,rgba(255,255,255,0.025)_22%,rgba(255,255,255,0.01)_100%)]';
|
||||
|
||||
export const HUD_TRAY_HAIRLINE = 'pointer-events-none absolute inset-x-0 top-0 h-px bg-white/16';
|
||||
|
||||
export const HUD_TRAY_HAIRLINE_BREAK = 'pointer-events-none absolute inset-x-0 top-0 h-px bg-emerald-200/18';
|
||||
|
||||
export const HUD_FIELD =
|
||||
'h-11 w-full rounded-[18px] border border-white/10 bg-black/14 px-3.5 text-[0.98rem] tracking-tight text-white placeholder:text-white/30 focus:border-white/20 focus:bg-black/20 focus:outline-none focus:ring-2 focus:ring-white/8';
|
||||
|
||||
@@ -15,6 +23,9 @@ export const HUD_OPTION_ROW =
|
||||
export const HUD_OPTION_ROW_PRIMARY =
|
||||
'border-white/12 bg-black/14 hover:border-white/18 hover:bg-black/18';
|
||||
|
||||
export const HUD_OPTION_ROW_BREAK =
|
||||
'border-emerald-200/12 bg-[rgba(16,38,31,0.28)] hover:border-emerald-200/18 hover:bg-[rgba(16,38,31,0.38)]';
|
||||
|
||||
export const HUD_OPTION_CHEVRON =
|
||||
'mt-0.5 shrink-0 text-[13px] text-white/28 transition-colors duration-200 group-hover:text-white/52';
|
||||
|
||||
@@ -27,6 +38,12 @@ export const HUD_PAUSE_TITLE =
|
||||
export const HUD_PAUSE_BODY =
|
||||
'mt-2 max-w-[23rem] text-[13px] leading-[1.6] text-white/58 md:text-[13.5px]';
|
||||
|
||||
export const HUD_RETURN_TITLE =
|
||||
'mt-2 max-w-[24rem] text-[1.08rem] font-medium leading-[1.36] tracking-[-0.02em] text-white/95 md:text-[1.14rem]';
|
||||
|
||||
export const HUD_RETURN_BODY =
|
||||
'mt-2 max-w-[23rem] text-[13px] leading-[1.58] text-white/58';
|
||||
|
||||
export const HUD_TEXT_LINK =
|
||||
'text-[12px] font-medium tracking-[0.08em] text-white/62 underline decoration-white/16 underline-offset-4 transition-all duration-200 hover:text-white/84 hover:decoration-white/28 disabled:cursor-default disabled:text-white/26 disabled:no-underline';
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ export const SpaceTimerHudWidget = ({
|
||||
onResetClick,
|
||||
}: SpaceTimerHudWidgetProps) => {
|
||||
const { isBreatheMode, triggerRestart } = useRestart30s();
|
||||
const isBreakPhase = hasActiveSession && sessionPhase === 'break';
|
||||
const modeLabel = isBreatheMode
|
||||
? RECOVERY_30S_MODE_LABEL
|
||||
: !hasActiveSession
|
||||
@@ -63,7 +64,11 @@ export const SpaceTimerHudWidget = ({
|
||||
className={cn(
|
||||
'relative z-10 flex h-[3.5rem] items-center justify-between gap-6 overflow-hidden rounded-full px-5 transition-colors',
|
||||
isImmersionMode
|
||||
? 'border border-white/10 bg-black/20 backdrop-blur-2xl shadow-[0_8px_32px_rgba(0,0,0,0.12)]'
|
||||
? isBreakPhase
|
||||
? 'border border-emerald-200/14 bg-[rgba(10,20,18,0.26)] backdrop-blur-2xl shadow-[0_8px_32px_rgba(0,0,0,0.10)]'
|
||||
: 'border border-white/10 bg-black/20 backdrop-blur-2xl shadow-[0_8px_32px_rgba(0,0,0,0.12)]'
|
||||
: isBreakPhase
|
||||
? 'border border-emerald-200/16 bg-[rgba(10,20,18,0.32)] backdrop-blur-2xl shadow-[0_8px_32px_rgba(0,0,0,0.14)]'
|
||||
: 'border border-white/15 bg-black/30 backdrop-blur-2xl shadow-[0_8px_32px_rgba(0,0,0,0.16)]',
|
||||
)}
|
||||
>
|
||||
@@ -71,7 +76,7 @@ export const SpaceTimerHudWidget = ({
|
||||
<span
|
||||
className={cn(
|
||||
'w-14 text-right text-[10px] font-bold uppercase tracking-[0.15em] opacity-80',
|
||||
sessionPhase === 'break' ? 'text-emerald-400' : 'text-brand-primary'
|
||||
sessionPhase === 'break' ? 'text-emerald-300' : 'text-brand-primary'
|
||||
)}
|
||||
>
|
||||
{modeLabel}
|
||||
@@ -114,13 +119,21 @@ export const SpaceTimerHudWidget = ({
|
||||
className={cn(
|
||||
'inline-flex h-8 w-8 items-center justify-center rounded-full text-sm transition-all duration-150 ease-out focus-visible:outline-none focus-visible:ring-2 active:scale-95 disabled:cursor-not-allowed disabled:opacity-30',
|
||||
isImmersionMode
|
||||
? 'text-white/70 hover:bg-white/10 hover:text-white'
|
||||
? isBreakPhase
|
||||
? 'text-white/74 hover:bg-emerald-100/10 hover:text-white'
|
||||
: 'text-white/70 hover:bg-white/10 hover:text-white'
|
||||
: isBreakPhase
|
||||
? 'text-white/82 hover:bg-emerald-100/12 hover:text-white'
|
||||
: 'text-white/80 hover:bg-white/15 hover:text-white',
|
||||
isStartAction && isHighlighted
|
||||
? 'bg-white/10 text-white shadow-sm'
|
||||
? isBreakPhase
|
||||
? 'bg-emerald-100/10 text-white shadow-sm'
|
||||
: 'bg-white/10 text-white shadow-sm'
|
||||
: '',
|
||||
isPauseAction && isHighlighted
|
||||
? 'bg-white/10 text-white shadow-sm'
|
||||
? isBreakPhase
|
||||
? 'bg-emerald-100/10 text-white shadow-sm'
|
||||
: 'bg-white/10 text-white shadow-sm'
|
||||
: '',
|
||||
)}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user