feat: FE 테마 리디자인 (Minimalist+ × Indigo × Pretendard)#702
Conversation
* Minimalist+ × Indigo × Pretendard 방향성 * 디자인 토큰 + 컴포넌트별 사양 + 8 phase 구현 계획 * .superpowers/ (brainstorm 임시 mockup 디렉토리) gitignore Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* index.html에 Pretendard 1.3.9 CDN <link> 추가 * :root에 색/타이포/스페이싱/라디우스/섀도우 토큰 정의 * base body 스타일 + :focus-visible + .visually-hidden 유틸리티 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* useMemo로 todos 분류, "N개 진행 중 · M개 완료" 텍스트 생성 * 카운트 0이면 해당 부분 생략, 전체 0이면 status 자체 비표시 * .app-header / .app-title / .app-status 스타일 추가 * App 통합 테스트에 카운트 검증 시나리오 추가 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…de 추가 * vite.config.ts test.exclude에 'e2e/**' 추가 * 기존엔 e2e/todo.spec.ts가 vitest에서 시도되어 file fail 발생 * npm test 실행 시 unit 테스트만 깨끗이 통과 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* dismissedError 로컬 state, useEffect([error])로 새 에러 발생 시 reset * aria-label="에러 닫기" 버튼, 빨강 톤 배너 스타일 * App 테스트에 dismiss 시나리오 추가 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* todo-form 카드 컨테이너, todo-form__input/priority/due/submit 클래스 * focus 시 accent ring, submit 버튼 hover/disabled 스타일 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* hidden radio + label 패턴으로 시각은 segmented, 마크업은 라디오 (a11y 유지) * counts prop으로 전체/미완료/완료 N개 inline 표시 (count는 aria-hidden) * 정렬 셀렉트 + 방향 토글 그룹을 우측으로 정렬 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 카드 컨테이너 + 항목 구분선 * todo-item--completed 시 line-through + 회색 * priority 색 뱃지 (high=빨강 / medium=인디고 / low=그레이) * 마감일을 제목 아래 작은 글씨 subtext로 분리, 행 hover 효과 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* hidden radio 패턴이라 getByRole('radio').click()이 actionable check 실패
* .filter-bar__segment label 요소를 직접 클릭하도록 변경
* '완료'와 '미완료' 구분 위해 ^anchor 정규식 사용
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 핵심 토큰 표 + 컴포넌트 BEM 클래스 목록 * 다크모드는 미적용 상태 명시 (향후 작업) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Claude finished @kenshin579's task in 2m 22s —— View job PR #702 코드 리뷰작업 목록
전반적으로 잘 구조화된 디자인 토큰 시스템이며, BEM 네이밍, 접근성 속성, 테스트 커버리지 모두 양호합니다. 발견한 이슈들을 심각도 순으로 정리합니다. 🔴 버그: FilterBar 카운트 배지가 필터 상태에 따라 잘못 표시됨파일: const counts = useMemo(() => {
const active = todos.filter((t) => !t.completed).length
const completed = todos.length - active
return { all: todos.length, active, completed }
}, [todos])
수정 방향: 🟡 접근성: 인라인 편집 span에 키보드 접근 불가파일: <span className="todo-item__title" onClick={() => setEditing(true)}>클릭으로만 편집 모드가 트리거되며 <span
className="todo-item__title"
role="button"
tabIndex={0}
onClick={() => setEditing(true)}
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') setEditing(true) }}
>🟡 보안: CDN 폰트 SRI(Subresource Integrity) 누락파일: <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css">SRI <link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css"
integrity="sha384-<HASH>"
crossorigin="anonymous">🟡 CSS: 변수 정의 후 미사용 (
|
| 위치 | 현재 | 변수 대안 |
|---|---|---|
.error-banner (119줄) |
padding: 10px 14px |
--space-* 조합 |
.error-banner__close (137줄) |
font-size: 16px |
--text-base |
.filter-bar__segment-label (221줄) |
border-radius: 6px |
--radius-md (8px) or --radius-sm |
.filter-bar__count (236줄) |
font-size: 11px, margin-left: 4px |
--text-xs, --space-1 |
.todo-item__due (339줄) |
font-size: 11px, margin-top: 2px |
--text-xs |
.todo-item__delete (358줄) |
font-size: 16px |
🟢 CSS: outline: none과 전역 :focus-visible 패턴 충돌
파일: frontend/src/index.css, 161줄
.todo-form__input,
.todo-form__priority,
.todo-form__due {
...
outline: none; /* ← 전역 :focus-visible 패턴을 무시 */
}전역 :focus-visible 규칙(81–85줄)에서 outline을 사용하지만, form 입력들은 이를 outline: none으로 재정의하고 box-shadow로 대체합니다. 동작은 올바르지만, 포커스 표시 방식이 두 가지로 나뉩니다. 일관성을 위해 전역 :focus-visible을 input, select, button에만 적용하고, form 전용 ring을 하나로 통일하는 것을 고려해 보세요.
🟢 성능: Pretendard 정적 폰트 전체 로드
파일: frontend/index.html, 7줄
pretendard.min.css는 모든 웨이트의 정적 폰트를 로드합니다 (~2MB+). 실제 사용되는 웨이트(400, 500, 700)만 필요하다면 variable font 버전(pretendard-variable.min.css)을 사용하면 초기 로드가 줄어듭니다. 튜토리얼 프로젝트이므로 크리티컬하지는 않습니다.
✅ 잘된 점
- 디자인 토큰 구조: 35개 CSS 변수를 의미 카테고리(surface/text/accent/priority/error)로 체계적으로 정리
- BEM 네이밍: 컴포넌트 전반에 일관되게 적용
- 접근성:
role="alert",aria-label,aria-hidden,visually-hidden,role="toolbar"등 적절히 사용 - 에러 배너:
useEffect로 새 에러 시 자동 reset — 깔끔한 패턴 - vite.config.ts: vitest가 e2e 파일을 picking up하던 버그 수정 (
test.exclude) — 좋은 버그픽스 - 테스트:
App.test.tsx4개 시나리오, e2e 9개 시나리오 모두 의미있는 커버리지 - 숨겨진 radio + label 패턴: segmented control 구현이 접근성과 스타일링 모두 고려됨
- YAGNI 원칙: 다크모드, 반응형, 애니메이션 등 의도적으로 제외 — 올바른 scope 관리
핵심 수정 권장: FilterBar 카운트 배지 버그(필터 적용 시 incorrect count)는 UX에 직접 영향을 미치므로 merge 전에 수정하는 것을 권장합니다.
Summary
ai/superpowers/todo/frontend/의 시각 테마를 리디자인합니다. 11줄짜리 minimal CSS를 디자인 토큰 기반 시스템으로 확장하고, 헤더 상태 카운트 / segmented control 필터 / dismissible 에러 배너 등 폴리시를 추가합니다.주요 변경
:rootCSS 변수 35개 정의 (index.css) — 색/타이포/스페이싱/라디우스/섀도우의도적 제외 (YAGNI)
color-scheme: light만 선언)부수 변경
vite.config.ts에test.exclude: ['node_modules', 'dist', 'e2e/**']추가 — vitest가 playwright e2e 파일을 picking up 하던 기존 버그 수정getByRole('radio').click()→.filter-bar__segment label.click()으로 변경 (hidden radio 패턴 호환)테스트 결과
make test-be✓ (백엔드 회귀, 변경 없음)make test-fe✓ 14개 (기존 12 + 신규 2: 헤더 카운트 / 에러 dismiss)make test-e2e✓ 9개 시나리오 (셀렉터 조정 후)문서
ai/superpowers/todo/docs/superpowers/specs/2026-04-30-fe-theme-redesign-design.mdai/superpowers/todo/docs/superpowers/plans/2026-04-30-fe-theme-redesign-plan.mdTest plan
make test-fe— 14/14 PASSmake test-e2e— 9/9 PASSmake test-be— 백엔드 회귀 OK🤖 Generated with Claude Code