feat(review): 요약 패널 그래프 단독화 + 카드 6축 미터바를 제목행으로 이동

- 요약(상단) 패널: 육각 그래프에 이미 축명/점수 라벨이 표시되므로 옆의
  범례(미터바)를 제거하고 그래프만 중앙 배치 (buildRadarLegend 호출 삭제)
- 리뷰 카드: 컴팩트 육각 아이콘 + sr-only 범례를 걷어내고, 닉네임 옆
  제목행에 6축 미니 세로 미터바(game-reviews__title-meter)를 노출 —
  4개 배치 샘플(Artifact)을 사용자가 검토 후 C안(타이틀 내부 통합형) 선택
- buildRadarLegend/.game-reviews__axis-legend*/.game-reviews__card-radar
  등 완전히 죽은 코드 제거

검증: node --check (스크립트 블록, JSP EL 치환) 통과, docker compose app
재기동 후 /game/3 실제 렌더 확인(요약 그래프 중앙 단독, 카드 6곳 모두
제목행 미터바 노출), 콘솔 에러 없음. 자동화 테스트 대상 아님(순수 뷰/JS,
서버 로직·매퍼 변경 없음).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011h6etRXJLx1xdfxmVcHjBg
This commit is contained in:
이정수 2026-07-01 11:59:24 +09:00
parent f086b12142
commit 0c8da405e7
1 changed files with 47 additions and 99 deletions

View File

@ -998,6 +998,7 @@
margin: 0 0 1.25rem; margin: 0 0 1.25rem;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center;
gap: 1.25rem; gap: 1.25rem;
padding: 1rem 1.25rem; padding: 1rem 1.25rem;
background: background:
@ -1081,75 +1082,38 @@
font-size: 0.75rem; font-size: 0.75rem;
color: var(--text-muted); color: var(--text-muted);
} }
.game-reviews__axis-legend { .game-reviews__title-meter {
margin: 0; display: inline-flex;
flex: 1; align-items: flex-end;
min-width: 11.5rem; gap: 0.15rem;
display: grid; height: 0.95rem;
gap: 0.4rem 0;
font-size: 0.75rem;
color: var(--text-muted);
} }
.game-reviews__axis-legend-row { .game-reviews__title-meter .stick {
display: grid;
grid-template-columns: 3.4rem 1fr 1.15rem;
align-items: center;
gap: 0.55rem;
}
.game-reviews__axis-legend dt {
font-weight: 600;
color: var(--text);
}
.game-reviews__axis-legend dd {
margin: 0;
font-variant-numeric: tabular-nums;
font-weight: 700;
text-align: right;
color: var(--accent);
}
.game-reviews__axis-meter {
position: relative; position: relative;
height: 0.4rem; width: 0.2rem;
border-radius: 999px; height: 100%;
border-radius: 2px;
background: var(--border); background: var(--border);
overflow: hidden; overflow: hidden;
} }
.game-reviews__axis-meter::before { .game-reviews__title-meter .stick span {
content: '';
position: absolute; position: absolute;
inset: 0; bottom: 0;
z-index: 2; left: 0;
pointer-events: none; right: 0;
background-image: repeating-linear-gradient(90deg, border-radius: 2px;
transparent 0, transparent calc(20% - 1px), background: linear-gradient(180deg, #f0c978, var(--accent));
rgba(255, 255, 255, 0.45) calc(20% - 1px), rgba(255, 255, 255, 0.45) 20%); animation: game-title-meter-grow 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
mix-blend-mode: overlay; transform-origin: bottom;
} }
.game-reviews__axis-meter span { @keyframes game-title-meter-grow {
position: relative; from { transform: scaleY(0); }
z-index: 1; to { transform: scaleY(1); }
display: block;
height: 100%;
border-radius: 999px;
background: linear-gradient(90deg, #f0c978, var(--accent));
animation: game-meter-grow 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
transform-origin: left;
}
@keyframes game-meter-grow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
} }
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.game-reviews__radar .game-axis-poly, .game-reviews__radar .game-axis-poly,
.game-reviews__radar .game-axis-dot, .game-reviews__radar .game-axis-dot,
.game-reviews__axis-meter span { animation: none; } .game-reviews__title-meter .stick span { animation: none; }
}
.game-reviews__card-radar {
margin-top: 0.6rem;
display: inline-flex;
}
.game-reviews__card-radar svg {
display: block;
} }
/* ── sort 토글 / 더보기 ──────────────────────────────── */ /* ── sort 토글 / 더보기 ──────────────────────────────── */
@ -1756,28 +1720,29 @@
return { svg: svg, nums: nums, hasData: hasData }; return { svg: svg, nums: nums, hasData: hasData };
} }
// 시각 보조 <dl> (축명/미터바/점수) — 폴리곤만으로 종료 금지(§6.6) // 리뷰 카드 제목행 삽입용 6축 미니 미터바 — axes 없으면 null(§6.6: 그래프는 요약에만, 카드는 이 미터바로 대체)
function buildRadarLegend(nums, hasData) { function buildTitleMeter(axes) {
var dl = document.createElement('dl'); var nums = AXIS_KEYS.map(function (k) {
dl.className = 'game-reviews__axis-legend'; var v = axes ? Number(axes[k]) : 0;
AXIS_KEYS.forEach(function (k, i) { return (isFinite(v) && v > 0) ? v : 0;
var row = document.createElement('div');
row.className = 'game-reviews__axis-legend-row';
var dt = document.createElement('dt');
dt.textContent = AXIS_LABELS_KO[k];
var meter = document.createElement('div');
meter.className = 'game-reviews__axis-meter';
var bar = document.createElement('span');
bar.style.width = (hasData ? (nums[i] / 5 * 100) : 0) + '%';
meter.appendChild(bar);
var dd = document.createElement('dd');
dd.textContent = hasData ? String(nums[i]) : '';
row.appendChild(dt);
row.appendChild(meter);
row.appendChild(dd);
dl.appendChild(row);
}); });
return dl; var hasData = nums.some(function (v) { return v > 0; });
if (!hasData) return null;
var wrap = document.createElement('div');
wrap.className = 'game-reviews__title-meter';
wrap.setAttribute('role', 'img');
wrap.setAttribute('aria-label', '6축 평가: ' + AXIS_KEYS.map(function (k, i) {
return AXIS_LABELS_KO[k] + ' ' + nums[i];
}).join(', '));
nums.forEach(function (v) {
var stick = document.createElement('span');
stick.className = 'stick';
var fill = document.createElement('span');
fill.style.height = (v / 5 * 100) + '%';
stick.appendChild(fill);
wrap.appendChild(stick);
});
return wrap;
} }
// ===== 별점 radiogroup 완전 패턴 (C6) ===== // ===== 별점 radiogroup 완전 패턴 (C6) =====
@ -2234,6 +2199,9 @@
mb.textContent = '내 리뷰'; mb.textContent = '내 리뷰';
head.appendChild(mb); head.appendChild(mb);
} }
// 6축 미니 미터바 — 제목행 노출(§6.6: 요약 그래프와 별개로 카드 단위 시각화)
var titleMeter = buildTitleMeter(r.axes);
if (titleMeter) head.appendChild(titleMeter);
var t = document.createElement('time'); var t = document.createElement('time');
t.className = 'game-reviews__time'; t.className = 'game-reviews__time';
t.dateTime = (r.updatedAt || r.createdAt) || ''; t.dateTime = (r.updatedAt || r.createdAt) || '';
@ -2255,25 +2223,6 @@
li.appendChild(p); li.appendChild(p);
} }
// 카드 컴팩트 육각형 (6축). axes null/누락 시 "데이터 없음"
var cardScores = AXIS_KEYS.map(function (k) { return r.axes ? Number(r.axes[k]) || 0 : 0; });
var cardRadar = buildHexRadar(cardScores, 60, 60, 44);
var radarWrap = document.createElement('div');
radarWrap.className = 'game-reviews__radar game-reviews__card-radar';
if (cardRadar.hasData) {
radarWrap.appendChild(cardRadar.svg);
// §6.6: 폴리곤만으로 종료 금지 — 시각/스크린리더 보조 표 동반(시각적으로 숨김)
var legend = buildRadarLegend(cardRadar.nums, true);
legend.className += ' sr-only';
radarWrap.appendChild(legend);
} else {
var none = document.createElement('span');
none.className = 'game-reviews__radar-empty';
none.textContent = '6축 평가 데이터 없음';
radarWrap.appendChild(none);
}
li.appendChild(radarWrap);
if (canActOn(r.userId)) { if (canActOn(r.userId)) {
var foot = document.createElement('div'); var foot = document.createElement('div');
foot.className = 'game-reviews__footer'; foot.className = 'game-reviews__footer';
@ -2322,7 +2271,6 @@
rSummaryPanel.hidden = false; rSummaryPanel.hidden = false;
if (radar.hasData) { if (radar.hasData) {
rSummaryRadar.appendChild(radar.svg); rSummaryRadar.appendChild(radar.svg);
rSummaryRadar.appendChild(buildRadarLegend(radar.nums, true));
} else { } else {
var none = document.createElement('span'); var none = document.createElement('span');
none.className = 'game-reviews__radar-empty'; none.className = 'game-reviews__radar-empty';