diff --git a/src/components/MobileSheet.css b/src/components/MobileSheet.css index 6939b58..33a60f5 100644 --- a/src/components/MobileSheet.css +++ b/src/components/MobileSheet.css @@ -108,7 +108,7 @@ flex: 1; overflow-y: auto; padding: 16px 20px; - padding-bottom: calc(16px + var(--safe-area-bottom)); + padding-bottom: calc(20px + var(--safe-area-bottom)); overscroll-behavior: contain; } diff --git a/src/components/SwipeableView.css b/src/components/SwipeableView.css index ebd7bb6..36b6275 100644 --- a/src/components/SwipeableView.css +++ b/src/components/SwipeableView.css @@ -46,6 +46,18 @@ .swipeable-view__tab.is-active { background: var(--surface-raised); color: var(--neon-cyan); + position: relative; +} + +.swipeable-view__tab.is-active::after { + content: ''; + position: absolute; + bottom: 2px; + left: 20%; + right: 20%; + height: 2px; + background: var(--neon-cyan); + border-radius: 1px; } /* Sliding track */ @@ -67,6 +79,15 @@ overflow-y: auto; } +/* Mobile touch targets */ +@media (max-width: 768px) { + .swipeable-view__tab { + min-height: 44px; + font-size: 14px; + padding: 10px 16px; + } +} + /* Reduced motion */ @media (prefers-reduced-motion: reduce) { .swipeable-view__track { diff --git a/src/pages/todo/Todo.css b/src/pages/todo/Todo.css index f8eaf75..ecc4af9 100644 --- a/src/pages/todo/Todo.css +++ b/src/pages/todo/Todo.css @@ -233,6 +233,7 @@ transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease; padding: 0; line-height: 1; + -webkit-tap-highlight-color: transparent; } .todo-card__btn:hover { @@ -288,6 +289,24 @@ opacity: 0.6; } +.todo-done-panel__clear-btn { + font-size: 11px; + padding: 4px 10px; + border-radius: 6px; + border: 1px solid rgba(239, 68, 68, 0.3); + background: rgba(239, 68, 68, 0.08); + color: #ef4444; + cursor: pointer; + font-family: inherit; + -webkit-tap-highlight-color: transparent; + transition: background 0.15s ease; + min-height: 32px; +} + +.todo-done-panel__clear-btn:active { + background: rgba(239, 68, 68, 0.2); +} + /* ── 날짜 필터 ────────────────────────────────────────────────────────── */ .todo-done-panel__filter { @@ -311,6 +330,7 @@ white-space: nowrap; font-family: inherit; line-height: 1.6; + -webkit-tap-highlight-color: transparent; } .todo-date-btn:hover { @@ -387,23 +407,166 @@ display: block; } + .todo-toolbar { + display: none; + } + .todo-col { min-height: 120px; + border: none; + background: transparent; + backdrop-filter: none; + -webkit-backdrop-filter: none; + } + + .todo-col__head { + padding: 10px 4px; + border-bottom: none; + } + + .todo-col__body { + padding: 4px 0; + } + + /* 카드 버튼 44px 터치 타겟 */ + .todo-card__btn { + width: 44px; + height: 44px; + font-size: 14px; + border-radius: 10px; + } + + .todo-card__actions { + gap: 6px; + } + + .todo-card__title { + font-size: 15px; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + + /* 날짜 필터 버튼 44px 터치 타겟 */ + .todo-date-btn { + font-size: 12px; + padding: 8px 14px; + min-height: 36px; + } + + .todo-date-input { + font-size: 13px; + padding: 8px 12px; + height: 36px; + min-height: 36px; + } + + .todo-done-panel { + border: none; + background: transparent; + backdrop-filter: none; + -webkit-backdrop-filter: none; } .todo-done-panel__head { flex-direction: column; align-items: flex-start; gap: 10px; + padding: 10px 4px; + border-bottom: none; } .todo-done-panel__filter { justify-content: flex-start; + gap: 8px; } .todo-done-panel__body { grid-template-columns: 1fr; + padding: 4px 0; } + + /* 폼 라벨 가독성 */ + .todo-form__field { + font-size: 14px; + gap: 8px; + } + + .todo-form__field input, + .todo-form__field textarea { + font-size: 16px; + padding: 12px 14px; + } + + .todo-form__actions .button { + width: 100%; + min-height: 48px; + font-size: 15px; + } + + /* 빈 상태 메시지 */ + .todo-col__empty { + font-size: 13px; + padding: 32px 16px; + } + + /* 라벨 버튼 (모바일) */ + .todo-card__btn--labeled { + width: auto; + height: 38px; + padding: 0 12px; + gap: 4px; + font-size: 12px; + } + + .todo-card__btn-label { + font-size: 11px; + font-weight: 500; + } + + .todo-card__actions { + flex-wrap: wrap; + } +} + +/* ── 확인 시트 (모바일) ─────────────────────────────────────────────── */ + +.todo-confirm-sheet { + display: flex; + flex-direction: column; + gap: 20px; + padding: 8px 0; +} + +.todo-confirm-sheet__msg { + margin: 0; + font-size: 15px; + color: var(--text); + line-height: 1.6; + text-align: center; +} + +.todo-confirm-sheet__actions { + display: flex; + gap: 10px; +} + +.todo-confirm-sheet__actions .button { + flex: 1; + min-height: 48px; + font-size: 15px; + justify-content: center; +} + +.button.danger { + background: rgba(239, 68, 68, 0.15); + border: 1px solid rgba(239, 68, 68, 0.4); + color: #ef4444; +} + +.button.danger:hover { + background: rgba(239, 68, 68, 0.25); } @media (max-width: 480px) { diff --git a/src/pages/todo/Todo.jsx b/src/pages/todo/Todo.jsx index c1d66f2..d877073 100644 --- a/src/pages/todo/Todo.jsx +++ b/src/pages/todo/Todo.jsx @@ -35,6 +35,7 @@ const Todo = () => { const [dragging, setDragging] = useState(null); const [dragOver, setDragOver] = useState(null); const [doneDate, setDoneDate] = useState(''); // '' = 전체 + const [confirmClear, setConfirmClear] = useState(false); const dragItem = useRef(null); const load = useCallback(async () => { @@ -93,6 +94,7 @@ const Todo = () => { }; const handleClear = async () => { + setConfirmClear(false); try { await clearTodos(); setTodos((prev) => prev.filter((t) => t.status !== 'done')); @@ -172,20 +174,22 @@ const Todo = () => { ))} @@ -208,7 +212,9 @@ const Todo = () => {
드래그하여 이동
++ {isMobile ? '아직 항목이 없습니다' : '드래그하여 이동'} +
)} {items.map((todo) => renderCard(todo, col.id))}{doneDate ? '해당 날짜에 완료된 항목이 없습니다' : '드래그하여 이동'}
+{doneDate ? '해당 날짜에 완료된 항목이 없습니다' : '완료된 항목이 없습니다'}
) : ( doneTodos.map((todo) => renderCard(todo, 'done')) )} @@ -369,7 +384,7 @@ const Todo = () => {- {doneDate ? '해당 날짜에 완료된 항목이 없습니다' : '드래그하여 이동'} + {doneDate ? '해당 날짜에 완료된 항목이 없습니다' : (isMobile ? '완료된 항목이 없습니다' : '드래그하여 이동')}
) : ( doneTodos.map((todo) => renderCard(todo, 'done')) @@ -388,6 +403,24 @@ const Todo = () => { {addForm} + {/* 모바일: 완료 비우기 확인 시트 */} ++ 완료된 항목 {todos.filter(t => t.status === 'done').length}건을 모두 삭제합니다. +
+