전수 E2E 순회·소유자 결정 6건 구현·디자인 감사 반영
- 전수 순회: IA 전 라우트 412개 기능 인벤토리를 체크리스트 백로그로 관리 (docs/ops/e2e-full-sweep-2026-07-27.md), 신규 full-sweep 스펙 10파일 추가. RED→GREEN으로 결함 12건 수정: 관리자 무한 렌더 프리즈(TanStack autoReset 루프), 복수 코호트 저장 유실, 페르소나 보관 503(SQL 컬럼 모호성), PII 과잉 마스킹, /admin/ai watchdog 오탐 오버레이, 모바일 겹침 2건, 설정 스크롤 스파이, 온보딩 전화번호 무검증, 리뷰 조사·난도 라벨, pending 피드백 등. - 소유자 결정 구현: 설정 아바타 변경, 동의 철회·재동의 전체 흐름, 신규 학습자 기초 우선 추천, 학생 분석 테이블 가상화(@tanstack/react-virtual), 저작 모드 죽은 레일 정리, 감정 밸런스 타임라인 차트(deep turn_valence + 결정론 파생 폴백). - 디자인 감사(142차): 라이트 팔레트 AA 대비, 다크 토큰 별칭 통일, 미정의 CSS 변수 정리, 한글 keep-all 전역화, 탭 타깃 24px, LCP preconnect. - 검증: npm run e2e 병렬 432 수집 GREEN + 직렬 49/49 exit 0, 백엔드 pytest 421, gateway 29, typecheck/build/design-ssot/dead-code/중복 게이트 통과, layout-visual-gate 15/15, session-layout 8/8. 상세는 SSOT 대시보드 142~144차 노트.
This commit is contained in:
parent
0ae94499f2
commit
4511383cd9
55 changed files with 9333 additions and 573 deletions
|
|
@ -591,6 +591,55 @@
|
|||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
/* 업로드된 아바타 이미지 — 원형 프레임을 그대로 채운다(이니셜 폴백 대체). */
|
||||
.vg-set__avatar img,
|
||||
.vg-set__rail-avatar img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
/* 아바타 업로드 컨트롤 — 숨은 file input + 버튼(온보딩과 같은 계약). */
|
||||
.vg-set__avatar-actions {
|
||||
position: relative;
|
||||
margin-left: auto;
|
||||
flex: none;
|
||||
}
|
||||
.vg-set__avatar-input {
|
||||
position: absolute;
|
||||
inline-size: 1px;
|
||||
block-size: 1px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ── 동의 섹션 — 철회 확인·재동의 안내 블록 ── */
|
||||
.vg-set__consent-block {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--sp-3);
|
||||
padding: var(--sp-4);
|
||||
margin-top: var(--sp-3);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius);
|
||||
background: var(--bg-surface-2);
|
||||
font-size: var(--fs-sm);
|
||||
color: var(--text-body);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.vg-set__consent-block > span {
|
||||
min-width: 0;
|
||||
flex: 1 1 240px;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.vg-set__consent-actions {
|
||||
display: flex;
|
||||
gap: var(--sp-2);
|
||||
flex: none;
|
||||
}
|
||||
|
||||
/* ── 모델/엔진 메타 한 줄(읽기전용 상태 표기) ── */
|
||||
.vg-set__meta {
|
||||
|
|
@ -1032,9 +1081,48 @@
|
|||
.vg-set__range-control {
|
||||
min-width: 0;
|
||||
}
|
||||
/* WCAG 2.2 SC 2.5.8(타깃 최소 24px): 브라우저 기본 range 는 높이 16px 라 미달이다.
|
||||
thumb 을 24px 로 잡되 투명 테두리 + background-clip:content-box 로 실제로 칠하는
|
||||
원은 16px 로 남긴다. 시각 크기는 그대로, 포인터 적중 영역만 24px 로 넓어진다. */
|
||||
.vg-set__range-control input[type="range"] {
|
||||
accent-color: var(--accent);
|
||||
display: block;
|
||||
height: 24px;
|
||||
background: transparent;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.vg-set__range-control input[type="range"]::-webkit-slider-runnable-track {
|
||||
height: 4px;
|
||||
border-radius: 999px;
|
||||
background: var(--border-strong);
|
||||
}
|
||||
.vg-set__range-control input[type="range"]::-moz-range-track {
|
||||
height: 4px;
|
||||
border-radius: 999px;
|
||||
background: var(--border-strong);
|
||||
}
|
||||
.vg-set__range-control input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
box-sizing: border-box;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-top: -10px;
|
||||
border: 4px solid transparent;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
background-clip: content-box;
|
||||
}
|
||||
.vg-set__range-control input[type="range"]::-moz-range-thumb {
|
||||
box-sizing: border-box;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 4px solid transparent;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
background-clip: content-box;
|
||||
}
|
||||
.vg-set__meta {
|
||||
margin-top: var(--sp-2);
|
||||
|
|
@ -1281,15 +1369,18 @@ body[data-page="settings"] #set-support {
|
|||
body[data-page="settings"] #set-engine {
|
||||
order: 3;
|
||||
}
|
||||
body[data-page="settings"] #set-appearance {
|
||||
body[data-page="settings"] #set-consent {
|
||||
order: 4;
|
||||
}
|
||||
body[data-page="settings"] #set-voice {
|
||||
body[data-page="settings"] #set-appearance {
|
||||
order: 5;
|
||||
}
|
||||
body[data-page="settings"] #set-notify {
|
||||
body[data-page="settings"] #set-voice {
|
||||
order: 6;
|
||||
}
|
||||
body[data-page="settings"] #set-notify {
|
||||
order: 7;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__group {
|
||||
padding: 18px;
|
||||
border-radius: var(--radius);
|
||||
|
|
@ -1404,48 +1495,92 @@ body[data-page="settings"] .vg-set__pill {
|
|||
background: var(--glass-surface-inset);
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
body[data-page="settings"] .vg-set {
|
||||
grid-template-columns: 86px minmax(0, 1fr);
|
||||
gap: 18px;
|
||||
max-width: none;
|
||||
/* 721~1080px 구간을 상단 가로 탭으로 접는다.
|
||||
이 폭에서 좌측 레일 배치가 두 갈래로 깨져 서브네비 라벨 7개가 모두 폭 0 이 됐다.
|
||||
(1) 981~1080px: 공용 `@media (max-width:1080px) .vg-set__rail{display:grid;
|
||||
grid-template-columns:minmax(220px,280px) minmax(0,1fr)}` 가 이 페이지 레일까지
|
||||
덮어썼다. 레일 트랙은 244px 뿐이라 실측상 `220px 0px` 로 풀려 nav 폭 0,
|
||||
nav-item 폭 28px 로 찌그러졌다(981·1023~1025·1079~1081 재현).
|
||||
(2) 721~980px: 이전 980 미디어쿼리가 레일을 86px 아이콘 전용으로 만들며
|
||||
`.vg-set__nav-item span{display:none}` 으로 라벨을 통째로 숨겼다.
|
||||
두 경우 모두 라벨이 사라지므로, 이 프로젝트가 선호하는 가로 탭 전환으로 통일한다.
|
||||
244px+28px+820px = 1092px 를 요구하는 2열 배치는 1081px 이상에서만 쓴다. */
|
||||
@media (max-width: 1080px) {
|
||||
body[data-page="settings"] .vg-set__callout {
|
||||
margin: var(--sp-4) 24px 0;
|
||||
}
|
||||
body[data-page="settings"] .vg-set {
|
||||
display: block;
|
||||
min-height: 100dvh;
|
||||
max-width: none;
|
||||
padding: 0 24px 28px;
|
||||
}
|
||||
/* 공용 1080 미디어쿼리의 2열 grid 를 무효화해 레일 전체를 가로 탭 바로 되돌린다.
|
||||
이 구간에서는 아래 폼 카드와 같은 폭·라운드로 맞춰 탭이 카드 밖으로 삐져나오지 않게 한다. */
|
||||
body[data-page="settings"] .vg-set__rail {
|
||||
padding: 18px 10px;
|
||||
display: block;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 20;
|
||||
min-height: auto;
|
||||
margin: 0 0 14px;
|
||||
padding: 14px 16px 10px;
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
body[data-page="settings"] .vg-set__rail-title {
|
||||
justify-content: center;
|
||||
padding: 0 0 16px;
|
||||
gap: 10px;
|
||||
padding: 0 0 12px;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__rail-title h1 {
|
||||
font-size: 19px;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__rail-title p {
|
||||
display: none;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__rail-mark {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__rail-title div,
|
||||
body[data-page="settings"] .vg-set__nav-item span,
|
||||
body[data-page="settings"] .vg-set__rail-status {
|
||||
display: none;
|
||||
}
|
||||
/* 탭이 많아 폭이 모자라면 가로 스크롤로 흘린다(라벨은 계속 노출). */
|
||||
body[data-page="settings"] .vg-set__nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
gap: 6px;
|
||||
overflow-x: auto;
|
||||
padding: 0 0 2px;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__nav-item {
|
||||
justify-content: center;
|
||||
min-height: 48px;
|
||||
padding: 0;
|
||||
flex: 0 0 auto;
|
||||
min-height: 38px;
|
||||
gap: 7px;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__nav-item span {
|
||||
display: inline;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__forms {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ≤720px 는 위 1080 가로 탭 배치를 그대로 이어받고 여백·타이포만 더 좁힌다.
|
||||
display/position/overflow 같은 배치 선언은 1080 블록에 한 번만 두고 여기선 중복 제거. */
|
||||
@media (max-width: 720px) {
|
||||
body[data-page="settings"] .vg-set__callout {
|
||||
margin: 12px 12px 0;
|
||||
}
|
||||
body[data-page="settings"] .vg-set {
|
||||
display: block;
|
||||
min-height: 100dvh;
|
||||
padding: 0 16px 28px;
|
||||
}
|
||||
/* ≤720px 만 full-bleed 상단 바(모바일 헤더 패턴)로 되돌린다. */
|
||||
body[data-page="settings"] .vg-set__rail {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 20;
|
||||
min-height: auto;
|
||||
margin: 0 -16px 12px;
|
||||
padding: 13px 16px 10px;
|
||||
border-radius: 0;
|
||||
|
|
@ -1456,44 +1591,27 @@ body[data-page="settings"] .vg-set__pill {
|
|||
gap: 8px;
|
||||
padding: 0 0 10px;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__rail-title div {
|
||||
display: block;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__rail-title h1 {
|
||||
font-size: 16px;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__rail-title p {
|
||||
display: none;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__rail-mark {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 2px;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__nav-item {
|
||||
flex: 0 0 auto;
|
||||
min-height: 34px;
|
||||
gap: 5px;
|
||||
padding: 0 7px;
|
||||
font-size: 11px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__nav-item svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__nav-item span {
|
||||
display: inline;
|
||||
}
|
||||
body[data-page="settings"] .vg-set__forms {
|
||||
gap: 10px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue