Skip to content

feat: FE 테마 리디자인 (Minimalist+ × Indigo × Pretendard)#702

Merged
kenshin579 merged 11 commits intomasterfrom
feat/fe-theme
Apr 30, 2026
Merged

feat: FE 테마 리디자인 (Minimalist+ × Indigo × Pretendard)#702
kenshin579 merged 11 commits intomasterfrom
feat/fe-theme

Conversation

@kenshin579
Copy link
Copy Markdown
Owner

Summary

ai/superpowers/todo/frontend/의 시각 테마를 리디자인합니다. 11줄짜리 minimal CSS를 디자인 토큰 기반 시스템으로 확장하고, 헤더 상태 카운트 / segmented control 필터 / dismissible 에러 배너 등 폴리시를 추가합니다.

주요 변경

  • 디자인 토큰: :root CSS 변수 35개 정의 (index.css) — 색/타이포/스페이싱/라디우스/섀도우
  • 폰트: Pretendard 1.3.9 CDN 도입 (한국어 가독성 ↑)
  • 헤더: "N개 진행 중 · M개 완료" 상태 카운트
  • TodoForm: 흰색 카드 + inline 4-요소 (input/priority/due/추가)
  • FilterBar: segmented control (hidden radio + label) + 카운트 뱃지(aria-hidden) + 정렬 셀렉트/방향 토글
  • TodoList/Item: 카드 컨테이너, 구분선, priority 색 뱃지(high=빨강 / medium=인디고 / low=그레이), 완료 항목 line-through, 마감일 작은 글씨 subtext, 행 hover
  • 에러 배너: 빨강 톤 dismissible 박스, 새 에러 발생 시 자동 reset
  • 빈 상태: dashed border 박스

의도적 제외 (YAGNI)

  • 다크모드 (별도 작업, color-scheme: light만 선언)
  • 외부 컴포넌트 라이브러리, CSS Modules
  • 애니메이션 (CSS hover transition 0.15s 외)
  • 반응형 모바일 레이아웃

부수 변경

  • vite.config.tstest.exclude: ['node_modules', 'dist', 'e2e/**'] 추가 — vitest가 playwright e2e 파일을 picking up 하던 기존 버그 수정
  • e2e spec의 radio 클릭을 getByRole('radio').click().filter-bar__segment label.click()으로 변경 (hidden radio 패턴 호환)

테스트 결과

  • make test-be ✓ (백엔드 회귀, 변경 없음)
  • make test-fe ✓ 14개 (기존 12 + 신규 2: 헤더 카운트 / 에러 dismiss)
  • make test-e2e ✓ 9개 시나리오 (셀렉터 조정 후)
  • 시각 검증 ✓ MCP playwright로 헤더 카운트, 필터 segmented, priority 색, 완료 line-through, 마감일 subtext 모두 확인

문서

  • 설계: ai/superpowers/todo/docs/superpowers/specs/2026-04-30-fe-theme-redesign-design.md
  • 구현 plan: ai/superpowers/todo/docs/superpowers/plans/2026-04-30-fe-theme-redesign-plan.md
  • README에 "테마" 섹션 추가 (디자인 토큰 표 + BEM 클래스 + 다크모드 미적용 명시)

Test plan

  • make test-fe — 14/14 PASS
  • make test-e2e — 9/9 PASS
  • make test-be — 백엔드 회귀 OK
  • 브라우저로 http://localhost:5173 시각 검증 (Pretendard, 카드, segmented, priority 색, 완료 line-through, 에러 배너 dismiss)

🤖 Generated with Claude Code

kenshin579 and others added 11 commits April 30, 2026 22:49
* 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>
@kenshin579 kenshin579 self-assigned this Apr 30, 2026
@claude
Copy link
Copy Markdown

claude Bot commented Apr 30, 2026

Claude finished @kenshin579's task in 2m 22s —— View job


PR #702 코드 리뷰

작업 목록

  • 컨텍스트 수집 및 분석
  • 변경된 파일 읽기 (index.css, App.tsx, components, tests)
  • 코드 품질, 버그, 보안, 성능 검토
  • 리뷰 피드백 작성

전반적으로 잘 구조화된 디자인 토큰 시스템이며, BEM 네이밍, 접근성 속성, 테스트 커버리지 모두 양호합니다. 발견한 이슈들을 심각도 순으로 정리합니다.


🔴 버그: FilterBar 카운트 배지가 필터 상태에 따라 잘못 표시됨

파일: frontend/src/App.tsx, 14–18줄

const counts = useMemo(() => {
  const active = todos.filter((t) => !t.completed).length
  const completed = todos.length - active
  return { all: todos.length, active, completed }
}, [todos])

useTodos(query)는 현재 status 필터가 적용된 목록을 반환합니다. 따라서 "미완료" 필터가 활성화된 상태에서는 todos에 미완료 항목만 들어오기 때문에:

  • counts.completed가 항상 0이 됨
  • FilterBar의 "완료(0)" 뱃지가 실제 수를 반영하지 못함
  • 헤더 statusText도 전체가 아닌 필터된 부분집합 기준으로 표시됨

수정 방향: useTodos에서 전체 목록(status 필터 없이)을 별도로 가져오거나, 전체 카운트용 훅/상태를 분리해야 합니다.

Fix this →


🟡 접근성: 인라인 편집 span에 키보드 접근 불가

파일: frontend/src/components/TodoItem.tsx, 60줄

<span className="todo-item__title" onClick={() => setEditing(true)}>

클릭으로만 편집 모드가 트리거되며 tabIndexonKeyDown 핸들러가 없습니다. 키보드 사용자는 편집을 시작할 수 없습니다.

<span
  className="todo-item__title"
  role="button"
  tabIndex={0}
  onClick={() => setEditing(true)}
  onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') setEditing(true) }}
>

Fix this →


🟡 보안: CDN 폰트 SRI(Subresource Integrity) 누락

파일: frontend/index.html, 7줄

<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css">

SRI integrity 속성이 없어 CDN 공급망 공격 시 악성 CSS 삽입 가능성이 있습니다. @v1.3.9 태그 핀을 사용하고 있어 위험이 낮지만, 명시적 해시를 추가하는 것이 권장됩니다.

<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: 변수 정의 후 미사용 (--shadow-md)

파일: frontend/src/index.css, 59줄

--shadow-md: 0 1px 3px rgba(0,0,0,0.04);

--shadow-md:root에 정의되어 있으나 파일 어디에서도 사용되지 않습니다. 나중에 사용할 예정이라면 괜찮지만, 지금 불필요하면 제거하는 것이 좋습니다.


🟢 CSS: 하드코딩된 픽셀 값 — CSS 변수 불일치

파일: frontend/src/index.css

여러 곳에서 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-visibleinput, 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.tsx 4개 시나리오, e2e 9개 시나리오 모두 의미있는 커버리지
  • 숨겨진 radio + label 패턴: segmented control 구현이 접근성과 스타일링 모두 고려됨
  • YAGNI 원칙: 다크모드, 반응형, 애니메이션 등 의도적으로 제외 — 올바른 scope 관리

핵심 수정 권장: FilterBar 카운트 배지 버그(필터 적용 시 incorrect count)는 UX에 직접 영향을 미치므로 merge 전에 수정하는 것을 권장합니다.

@kenshin579 kenshin579 merged commit b0fd5bc into master Apr 30, 2026
2 checks passed
@kenshin579 kenshin579 deleted the feat/fe-theme branch April 30, 2026 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant