diff --git a/MAIN_PLAN.md b/MAIN_PLAN.md index 45a74a2..4a80f26 100644 --- a/MAIN_PLAN.md +++ b/MAIN_PLAN.md @@ -199,6 +199,16 @@ - 오류 원인/디버깅 - 없음(스냅샷은 정규식 매칭으로 안정화) +### 스타일 토큰 회귀 강화(링/비활성) (2025-11-17) +- TDD + - `lib/wysiwyg/export.style.tokens.focusVisible.test.ts`: `--ring`, `--ring-offset` 토큰 존재 및 `:focus-visible`에서 outline/offset 참조 검증 + - `lib/wysiwyg/export.style.tokens.disabled.test.ts`: `--disabled-bg`, `--disabled-fg` 토큰 존재 및 `:disabled`/`[readonly]`에서 참조 검증 +- 구현: `lib/wysiwyg/export.ts` + - :root/테마 토큰에 `--ring`, `--ring-offset`, `--disabled-bg`, `--disabled-fg` 추가 + - 규칙 추가: `:focus-visible{outline:2px solid var(--ring);outline-offset:var(--ring-offset)}` + - 규칙 추가: `input,select,textarea:disabled{background:var(--disabled-bg);color:var(--disabled-fg)}` / `[readonly]{color:var(--disabled-fg)}` +- 결과: 전체 테스트 그린(166 파일 / 291 테스트) + ### Guides/그룹 액션 완성(드래그/리사이즈/회전 + 스냅) (2025-11-17) - TDD - `components/wysiwyg/WysiwygBuilder.guides.acceptance.test.tsx`: Guides 토글 ON/OFF 표시 확인 diff --git a/lib/wysiwyg/export.style.tokens.disabled.test.ts b/lib/wysiwyg/export.style.tokens.disabled.test.ts new file mode 100644 index 0000000..dc88c46 --- /dev/null +++ b/lib/wysiwyg/export.style.tokens.disabled.test.ts @@ -0,0 +1,23 @@ +import { describe, it, expect } from 'vitest' +import { exportFrame } from '@/lib/wysiwyg/export' +import type { Frame } from '@/lib/wysiwyg/schema' + +function baseFrame(): Frame { + return { + id: 'f', width: 320, height: 200, background: '#111111', + layers: [{ id: 'l1', name: 'Base', visible: true, locked: false, objects: [] }], + } +} + +describe('wysiwyg export - disabled/read-only control tokens', () => { + it('defines disabled tokens and uses them for :disabled and [readonly]', () => { + const out = exportFrame(baseFrame()) + // tokens + expect(out.css).toMatch(/:root\s*{[^}]*--disabled-bg:/) + expect(out.css).toMatch(/:root\s*{[^}]*--disabled-fg:/) + // usage + expect(out.css).toMatch(/input:disabled,select:disabled,textarea:disabled\s*{[^}]*background:var\(--disabled-bg\)/) + expect(out.css).toMatch(/input:disabled,select:disabled,textarea:disabled\s*{[^}]*color:var\(--disabled-fg\)/) + expect(out.css).toMatch(/input\[readonly\],select\[readonly\],textarea\[readonly\]\s*{[^}]*color:var\(--disabled-fg\)/) + }) +}) diff --git a/lib/wysiwyg/export.style.tokens.focusVisible.test.ts b/lib/wysiwyg/export.style.tokens.focusVisible.test.ts new file mode 100644 index 0000000..427e7ec --- /dev/null +++ b/lib/wysiwyg/export.style.tokens.focusVisible.test.ts @@ -0,0 +1,22 @@ +import { describe, it, expect } from 'vitest' +import { exportFrame } from '@/lib/wysiwyg/export' +import type { Frame } from '@/lib/wysiwyg/schema' + +function baseFrame(): Frame { + return { + id: 'f', width: 320, height: 200, background: '#111111', + layers: [{ id: 'l1', name: 'Base', visible: true, locked: false, objects: [] }], + } +} + +describe('wysiwyg export - focus-visible ring tokens', () => { + it('defines ring tokens and uses them in :focus-visible styles', () => { + const out = exportFrame(baseFrame()) + // tokens + expect(out.css).toMatch(/:root\s*{[^}]*--ring:/) + expect(out.css).toMatch(/:root\s*{[^}]*--ring-offset:/) + // usage (focus-visible across common focusables and controls) + expect(out.css).toMatch(/:focus-visible\s*{[^}]*outline:2px solid var\(--ring\)/) + expect(out.css).toMatch(/:focus-visible\s*{[^}]*outline-offset:var\(--ring-offset\)/) + }) +}) diff --git a/lib/wysiwyg/export.ts b/lib/wysiwyg/export.ts index 86ec61f..302fd62 100644 --- a/lib/wysiwyg/export.ts +++ b/lib/wysiwyg/export.ts @@ -120,15 +120,18 @@ export function exportFrame(frame: Frame): Exported { } const inner = `