fix(ui): 모바일 깨짐 수정 — 표 카드뷰 opt-in, 대시보드 반응형, CSS 캐시버스팅

폰에서 화면이 깨지던 문제 수정.
- 전역 반응형 표 '카드뷰'가 모든 table에 적용돼, data-label 없는 목록 표
  (수집함·발굴·발행 등)가 거대한 행으로 변해 페이지가 32만px로 늘어남.
  → 카드뷰를 .rcards opt-in 으로 스코프. 그 클래스 가진 레거시 표
  (multi_channel_videos·videos)만 카드뷰, 나머지는 모바일에서 가로 스크롤.
- 대시보드: 모바일(≤680px) 미디어쿼리 추가 — 마스트헤드 세로 정렬, 긴
  브레드크럼 숨김, 떡상 리더보드 액션 버튼을 아랫줄 우측 정렬, 숫자 축소.
- base.html: CSS/JS 링크에 v= 캐시버스팅 — 배포 후 브라우저가 옛 CSS를
  캐시해 스타일이 깨지는 것 방지(CSS 변경 시 버전 올리기).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hehihoho3@gmail.com 2026-06-17 06:56:50 +09:00
parent e0d92792b1
commit 921c24f143
5 changed files with 33 additions and 17 deletions

View File

@ -302,18 +302,19 @@ body.sidebar-collapsed #mainContent { margin-left: var(--sidebar-collapsed-width
}
.mobile-menu-btn { display: none; }
/* Mobile responsive tables (card view) */
/* Mobile responsive tables (card view) OPT-IN via .rcards (tables with data-label).
기타 표는 카드뷰 미적용 카드의 overflow-x:auto 가로 스크롤(거대 방지). */
@media (max-width: 768px) {
table, thead, tbody, th, td, tr { display: block; }
thead tr { position: absolute; top: -9999px; left: -9999px; }
tr { margin-bottom: 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 1rem; }
td { border: none; position: relative; padding-left: 50% !important; padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; min-height: 2rem; display: flex; align-items: center; text-align: right; justify-content: flex-end; }
td::before { content: attr(data-label); position: absolute; left: 1rem; width: 45%; padding-right: 10px; white-space: nowrap; text-align: left; font-weight: bold; color: var(--text-muted); }
td[data-label="Video"] { padding-left: 0 !important; display: block; text-align: left; }
td[data-label="Video"]::before { display: none; }
td[data-label="Rank"] { display: inline-block; padding: 0 !important; margin-bottom: 0.5rem; width: auto; }
td[data-label="Rank"]::before { display: none; }
td[data-label="Rank"] span { font-size: 0.875rem !important; background: var(--accent-soft); padding: 2px 8px; border-radius: 4px; color: var(--text-muted); border: 1px solid var(--border); }
.rcards, .rcards thead, .rcards tbody, .rcards th, .rcards td, .rcards tr { display: block; }
.rcards thead tr { position: absolute; top: -9999px; left: -9999px; }
.rcards tr { margin-bottom: 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 1rem; }
.rcards td { border: none; position: relative; padding-left: 50% !important; padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; min-height: 2rem; display: flex; align-items: center; text-align: right; justify-content: flex-end; }
.rcards td::before { content: attr(data-label); position: absolute; left: 1rem; width: 45%; padding-right: 10px; white-space: nowrap; text-align: left; font-weight: bold; color: var(--text-muted); }
.rcards td[data-label="Video"] { padding-left: 0 !important; display: block; text-align: left; }
.rcards td[data-label="Video"]::before { display: none; }
.rcards td[data-label="Rank"] { display: inline-block; padding: 0 !important; margin-bottom: 0.5rem; width: auto; }
.rcards td[data-label="Rank"]::before { display: none; }
.rcards td[data-label="Rank"] span { font-size: 0.875rem !important; background: var(--accent-soft); padding: 2px 8px; border-radius: 4px; color: var(--text-muted); border: 1px solid var(--border); }
}
/* ===== Shared components ===== */

View File

@ -178,6 +178,21 @@
.fd-vmodal-frame iframe{ width:100%; height:100%; border:0; display:block; }
@media (max-width:1100px){ .fd-cols{ grid-template-columns:1fr; } .fd-actions{ grid-template-columns:1fr; } .fd-act{ border-right:none; border-bottom:1.5px solid var(--fd-rule-strong); } }
@media (max-width:680px){
.fd-dash{ margin:-1.25rem; }
.fd-masthead{ flex-direction:column; align-items:stretch; gap:8px; padding:16px 16px 12px; }
.fd-masthead .fd-tick{ display:none; } /* 긴 브레드크럼/라벨 숨김 */
.fd-masthead h1{ font-size:26px; }
.fd-meta{ display:flex; align-items:center; gap:12px; }
.fd-meta .fd-big{ margin:0; }
.fd-body{ padding:16px 16px 28px; }
.fd-num{ font-size:44px; }
.fd-act{ padding:16px 16px; }
/* 리더보드 행: 배율까지 한 줄, 액션 버튼은 아랫줄 우측 정렬 */
.fd-lead{ grid-template-columns:24px 46px 1fr auto; column-gap:10px; }
.fd-lead .fd-actcell{ grid-column:1 / -1; justify-content:flex-end; opacity:1; padding-top:2px; }
.fd-lead .fd-t{ white-space:normal; max-width:none; }
}
</style>
<script th:inline="javascript">

View File

@ -21,9 +21,9 @@
<!-- Editorial identity (dashboard) -->
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,500;12..96,700;12..96,800&family=Hanken+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<!-- CSS -->
<link rel="stylesheet" th:href="@{/css/variables.css}">
<link rel="stylesheet" th:href="@{/css/style.css}">
<!-- CSS (v= 캐시 버스팅 — CSS 바뀌면 숫자 올리기) -->
<link rel="stylesheet" th:href="@{/css/variables.css(v=20260617)}">
<link rel="stylesheet" th:href="@{/css/style.css(v=20260617)}">
<!-- Lucide Icons (CDN for simplicity in SSR) -->
<script src="https://unpkg.com/lucide@latest"></script>
@ -48,7 +48,7 @@
</div>
<!-- JS -->
<script th:src="@{/js/common.js}"></script>
<script th:src="@{/js/common.js(v=20260617)}"></script>
<script>
lucide.createIcons();
</script>

View File

@ -94,7 +94,7 @@
<!-- Video List Table -->
<div class="card p-0" style="overflow-x: auto;">
<table class="w-full" style="border-collapse: collapse; text-align: left;">
<table class="w-full rcards" style="border-collapse: collapse; text-align: left;">
<thead style="background: var(--surface-2); border-bottom: 1px solid var(--glass-border);">
<tr>
<th class="p-4 text-sm font-bold text-muted"><input type="checkbox" id="selectAll" onclick="toggleSelectAll(this.checked)" title="전체 선택"></th>

View File

@ -100,7 +100,7 @@
<!-- Video List Table -->
<div class="card p-0" style="overflow-x: auto;">
<table class="w-full" style="border-collapse: collapse; text-align: left;">
<table class="w-full rcards" style="border-collapse: collapse; text-align: left;">
<thead style="background: var(--surface-2); border-bottom: 1px solid var(--glass-border);">
<tr>
<th class="p-4 text-sm font-bold text-muted"><input type="checkbox" id="selectAll" onclick="toggleSelectAll(this.checked)" title="전체 선택"></th>