fix(frontend): restore Ref document and control contracts

Ref's `.bg0/.bg1/.bg2` set only a background image and `html, body` centre the
texture with vertical tiling, so the extra background colours and the default
tiling are removed. Two ranking views declared unscoped `body` rules that leaked
a 500px min-width and a 21px line-height onto every screen; each page now owns
its own contract. Controls that Ref renders with the Bootstrap/Lumen family get
the shared modifier so the bottom edge moves on hover and while pressed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-04 07:22:15 +00:00
co-authored by Claude Opus 5
parent f0fa55245b
commit 80fb1d118b
16 changed files with 174 additions and 63 deletions
+23 -4
View File
@@ -14,19 +14,36 @@ html {
color-scheme: dark; color-scheme: dark;
} }
/*
* Ref's `html, body` contract: the page texture is centred and only tiles
* vertically, so a wide screen keeps one column of the texture.
*/
body { body {
min-width: 320px; min-width: 320px;
margin: 0; margin: 0;
background: #000; background: #000;
background-position: center;
background-repeat: repeat-y;
color: #fff; color: #fff;
font-family: var(--sammo-font-sans); font-family: var(--sammo-font-sans);
font-size: 14px; font-size: 14px;
line-height: 1.3; line-height: 1.3;
} }
/*
* Ref's in-game document never narrows below its 500px mobile column, so the
* app root keeps that floor. Previously this came from an unscoped `body` rule
* inside two ranking views, which also leaked their line-height everywhere.
*/
#app {
min-width: 500px;
}
body:has(.battle-page), body:has(.battle-page),
body:has(.chief-page), body:has(.chief-page),
body:has(.global-page) { body:has(.global-page),
body:has(.main-page),
body:has(.yearbook-page) {
line-height: 1.5; line-height: 1.5;
} }
@@ -37,17 +54,19 @@ textarea {
font: inherit; font: inherit;
} }
/*
* Ref's `.bg0/.bg1/.bg2` set a background image and nothing else, so the
* element stays transparent where the texture does not cover it. Adding a
* background colour here paints over whatever the parent shows through.
*/
.legacy-bg0 { .legacy-bg0 {
background-color: #302016;
background-image: var(--sammo-texture-walnut); background-image: var(--sammo-texture-walnut);
} }
.legacy-bg1 { .legacy-bg1 {
background-color: #14241b;
background-image: var(--sammo-texture-green); background-image: var(--sammo-texture-green);
} }
.legacy-bg2 { .legacy-bg2 {
background-color: #172a52;
background-image: var(--sammo-texture-blue); background-image: var(--sammo-texture-blue);
} }
@@ -48,6 +48,26 @@
cursor: pointer; cursor: pointer;
} }
/*
* Ref renders the dashboard reload control with the Lumen navigation family:
* the bottom edge shortens on hover and again while pressed.
*/
.game-shell__action--navigation {
border-color: var(--sammo-button-navigation-border);
border-width: 0 1px 4px;
background: var(--sammo-button-navigation-bg);
}
.game-shell__action--navigation:not(:disabled):hover {
margin-top: 1px;
border-bottom-width: 3px;
}
.game-shell__action--navigation:not(:disabled):active {
margin-top: 2px;
border-bottom-width: 2px;
}
.game-feedback--error { .game-feedback--error {
color: var(--sammo-color-error); color: var(--sammo-color-error);
font-size: 0.85rem; font-size: 0.85rem;
@@ -1317,10 +1317,9 @@ const shouldShowUI = computed(() => !loading.value && !!options.value);
.battle-simulator :deep(.panel-card) { .battle-simulator :deep(.panel-card) {
border-color: rgba(0, 0, 0, 0.18); border-color: rgba(0, 0, 0, 0.18);
border-radius: 5px; border-radius: 5.25px;
background: #303030; background: #303030;
background-image: none; background-image: none;
overflow: hidden;
} }
.battle-simulator :deep(.panel-header) { .battle-simulator :deep(.panel-header) {
@@ -1373,15 +1372,24 @@ button {
color: inherit; color: inherit;
} }
/*
* Ref uses the Bootstrap/Lumen family here: no top border, 1px sides and a 4px
* bottom that shortens on hover and active while the control moves down.
*/
.primary, .primary,
.ghost { .ghost {
border: 1px solid transparent; --simulator-button-border: #d04436;
border-radius: 5px;
border-color: var(--simulator-button-border);
border-style: solid;
border-width: 0 1px 4px;
border-radius: 5.25px;
padding: 5.25px 10.5px; padding: 5.25px 10.5px;
color: #fff; color: #fff;
font-size: 14px; font-size: 14px;
font-weight: 700; font-weight: 700;
line-height: 21px; line-height: 21px;
vertical-align: middle;
cursor: pointer; cursor: pointer;
transition: transition:
color 0.15s ease-in-out, color 0.15s ease-in-out,
@@ -1394,6 +1402,18 @@ button {
background: #e74c3c; background: #e74c3c;
} }
.primary:not(:disabled):hover,
.ghost:not(:disabled):hover {
margin-top: 1px;
border-bottom-width: 3px;
}
.primary:not(:disabled):active,
.ghost:not(:disabled):active {
margin-top: 2px;
border-bottom-width: 2px;
}
.primary:active { .primary:active {
background: #b93d30; background: #b93d30;
} }
@@ -1404,14 +1424,20 @@ button {
} }
.ghost { .ghost {
--simulator-button-border: #2f89c5;
background: #3498db; background: #3498db;
} }
.load-action { .load-action {
--simulator-button-border: #26527d;
background: #2c5d8f; background: #2c5d8f;
} }
.add-action { .add-action {
--simulator-button-border: #0da271;
background: #10b981; background: #10b981;
} }
@@ -1472,7 +1498,12 @@ button:disabled {
box-shadow 0.15s ease-in-out; box-shadow 0.15s ease-in-out;
} }
.unit-after input[type='number'] {
text-align: right;
}
.repeat-field select { .repeat-field select {
border-radius: 0 5.25px 5.25px 0;
padding-right: 31.5px; padding-right: 31.5px;
background-image: url('data:image/svg+xml,%3csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 16 16%27%3e%3cpath fill=%27none%27 stroke=%27%23303030%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27 stroke-width=%272%27 d=%27m2 5 6 6 6-6%27/%3e%3c/svg%3e'); background-image: url('data:image/svg+xml,%3csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 16 16%27%3e%3cpath fill=%27none%27 stroke=%27%23303030%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27 stroke-width=%272%27 d=%27m2 5 6 6 6-6%27/%3e%3c/svg%3e');
background-repeat: no-repeat; background-repeat: no-repeat;
@@ -1485,6 +1516,7 @@ button:disabled {
.field input:focus, .field input:focus,
.field select:focus { .field select:focus {
border-color: #9badbf; border-color: #9badbf;
box-shadow: 0 0 0 3.5px rgba(55, 90, 127, 0.25);
outline: 0; outline: 0;
} }
@@ -1557,19 +1589,32 @@ button:disabled {
.summary-table { .summary-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
vertical-align: top;
} }
.summary-table tbody,
.summary-table tr {
vertical-align: top;
}
/*
* Ref renders this as a Bootstrap table: 7px padding on every side, a #444
* bottom rule and the transparent accent inset shadow the framework always
* emits.
*/
.summary-table th, .summary-table th,
.summary-table td { .summary-table td {
padding: 7px 8px; padding: 7px;
border-bottom: 1px solid rgba(201, 164, 90, 0.2); border-bottom: 1px solid #444;
box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0);
text-align: left; text-align: left;
vertical-align: top;
} }
.summary-table th { .summary-table th {
width: 18ch; width: 180px;
color: #fff; color: #fff;
font-weight: 600; font-weight: 700;
} }
.battle-summary-card :deep(.panel-body) { .battle-summary-card :deep(.panel-body) {
@@ -165,7 +165,7 @@ watch(viewMode, () => {
</template> </template>
<style scoped> <style scoped>
:global(body) { :global(body:has(.legacy-ranking-page)) {
min-width: 500px; min-width: 500px;
overflow-x: hidden; overflow-x: hidden;
} }
@@ -348,7 +348,7 @@ watch(viewMode, () => {
} }
@media (min-width: 1000px) { @media (min-width: 1000px) {
:global(body) { :global(body:has(.legacy-ranking-page)) {
min-width: 1000px; min-width: 1000px;
} }
+1 -1
View File
@@ -158,7 +158,7 @@ onMounted(() => {
</div> </div>
<div class="article-submit-row"> <div class="article-submit-row">
<div></div> <div></div>
<button id="submitArticle" class="legacy-button" type="button" @click="submitArticle">등록</button> <button id="submitArticle" class="legacy-button legacy-button--secondary" type="button" @click="submitArticle">등록</button>
</div> </div>
</section> </section>
@@ -271,7 +271,7 @@ onBeforeUnmount(() => {
<tbody> <tbody>
<tr> <tr>
<td> <td>
<br /><button class="legacy-button" type="button" @click="router.push('/')"> <br /><button class="legacy-button legacy-button--primary" type="button" @click="router.push('/')">
돌아가기 돌아가기
</button> </button>
</td> </td>
@@ -622,7 +622,7 @@ onBeforeUnmount(() => {
<tbody> <tbody>
<tr> <tr>
<td> <td>
<button class="legacy-button" type="button" @click="router.push('/')">돌아가기</button <button class="legacy-button legacy-button--primary" type="button" @click="router.push('/')">돌아가기</button
><br /><br /> ><br /><br />
삼국지 모의전투 PHP HiDCHe - unknown / KOEI의 이미지를 사용, 응용하였습니다 / 제작 : 삼국지 모의전투 PHP HiDCHe - unknown / KOEI의 이미지를 사용, 응용하였습니다 / 제작 :
HideD(hided62@gmail.com) / HideD(hided62@gmail.com) /
@@ -298,8 +298,9 @@ onMounted(loadDetail);
</template> </template>
<style scoped> <style scoped>
:global(body) { :global(body:has(.dynasty-page)) {
min-width: 1016px; /* Ref renders this page as a 1000px column, mobile included. */
min-width: 1000px;
} }
.dynasty-page { .dynasty-page {
@@ -153,26 +153,30 @@ onMounted(loadDynasty);
</template> </template>
<style scoped> <style scoped>
:global(body) { :global(body:has(.dynasty-page)) {
min-width: 1016px; /* Ref renders this page as a 1000px column, mobile included. */
min-width: 1000px;
} }
.dynasty-page { .dynasty-page {
width: 1000px; width: 1000px;
margin: 8px auto 0; margin: 8px auto 0;
color: #fff; color: #fff;
font-family: 'Times New Roman', serif;
font-size: 16px;
line-height: normal;
} }
/* Ref `table.tb_layout` + `.tb_layout td`: collapsed 1px gray cell borders. */
.legacy-table { .legacy-table {
width: 1000px; width: 1000px;
border-spacing: 2px; background-color: transparent;
border-collapse: collapse;
font-size: 14px;
word-break: break-all;
} }
.legacy-table td { .legacy-table td {
padding: 1px; border: 1px solid gray;
padding: 0;
word-break: break-all;
} }
.spaced-table { .spaced-table {
@@ -181,7 +181,7 @@ onMounted(loadOptions);
</template> </template>
<style scoped> <style scoped>
:global(body) { :global(body:has(.legacy-hall-page)) {
min-width: 500px; min-width: 500px;
overflow-x: hidden; overflow-x: hidden;
} }
@@ -346,7 +346,7 @@ onMounted(loadOptions);
} }
@media (min-width: 1000px) { @media (min-width: 1000px) {
:global(body) { :global(body:has(.legacy-hall-page)) {
min-width: 1000px; min-width: 1000px;
} }
+1 -1
View File
@@ -151,7 +151,7 @@ watch(
> >
실시간 동기화: {{ realtimeLabel }} 실시간 동기화: {{ realtimeLabel }}
</button> </button>
<button class="game-shell__action" type="button" @click="loadMainData"> </button> <button class="game-shell__action game-shell__action--navigation" type="button" @click="loadMainData"> </button>
<button class="game-shell__action" type="button" @click="moveLobby">로비로</button> <button class="game-shell__action" type="button" @click="moveLobby">로비로</button>
</div> </div>
</header> </header>
@@ -338,7 +338,7 @@ onMounted(() => {
<main id="nation-betting-container" class="nation-betting-page legacy-bg0"> <main id="nation-betting-container" class="nation-betting-page legacy-bg0">
<header class="legacy-top-bar"> <header class="legacy-top-bar">
<RouterLink v-slot="{ navigate }" custom to="/"> <RouterLink v-slot="{ navigate }" custom to="/">
<button class="legacy-nav-button" type="button" @click="navigate">돌아가기</button> <button class="legacy-button legacy-button--navigation" type="button" @click="navigate">돌아가기</button>
</RouterLink> </RouterLink>
<div></div> <div></div>
<h1>국가 베팅장</h1> <h1>국가 베팅장</h1>
@@ -440,7 +440,7 @@ onMounted(() => {
</section> </section>
<footer class="betting-footer"> <footer class="betting-footer">
<RouterLink class="legacy-nav-button" to="/">돌아가기</RouterLink> <RouterLink class="legacy-button legacy-button--navigation" to="/">돌아가기</RouterLink>
</footer> </footer>
</main> </main>
</template> </template>
@@ -78,7 +78,7 @@ onMounted(load);
<strong>세력 장수</strong> <strong>세력 장수</strong>
<span class="right-actions"> <span class="right-actions">
<span class="dropdown"> <span class="dropdown">
<button class="top-button mode-button" @click="viewMenuOpen = !viewMenuOpen">보기 모드</button> <button class="top-button mode-button" :aria-expanded="viewMenuOpen" @click="viewMenuOpen = !viewMenuOpen">보기 모드</button>
<span v-if="viewMenuOpen" class="dropdown-menu"> <span v-if="viewMenuOpen" class="dropdown-menu">
<button <button
@click=" @click="
@@ -258,6 +258,7 @@ onMounted(load);
.right-actions { .right-actions {
right: 0; right: 0;
} }
/* Ref Lumen primary: the bottom edge carries the pressed-state movement. */
.top-button { .top-button {
display: inline-flex; display: inline-flex;
height: 32px; height: 32px;
@@ -282,7 +283,16 @@ onMounted(load);
} }
.mode-button { .mode-button {
background: #375a7f; background: #375a7f;
border-bottom: 0 solid #325172;
} }
/* Ref Lumen primary: a 3px bottom edge appears on hover and stays while open. */
.mode-button:hover,
.mode-button[aria-expanded='true'],
.mode-button:active {
border-bottom-width: 3px;
}
.mode-button, .mode-button,
.columns-button { .columns-button {
width: 90px; width: 90px;
@@ -196,12 +196,16 @@ th,
text-align: center; text-align: center;
font-weight: 400; font-weight: 400;
} }
/* Ref Lumen primary: no top border, 1px sides, 4px bottom that shortens. */
.close-button { .close-button {
height: 35.5px; height: 35.5px;
padding: 5.25px 10.5px; padding: 5.25px 10.5px;
border: 1px solid transparent; border-color: #325172;
border-radius: 3.5px; border-style: solid;
border-width: 0 1px 4px;
border-radius: 5.25px;
background: rgb(55, 90, 127); background: rgb(55, 90, 127);
vertical-align: middle;
color: #fff; color: #fff;
font-weight: 700; font-weight: 700;
line-height: 21px; line-height: 21px;
@@ -212,6 +216,16 @@ th,
border-color 0.15s, border-color 0.15s,
box-shadow 0.15s; box-shadow 0.15s;
} }
.close-button:not(:disabled):hover {
margin-top: 1px;
border-bottom-width: 3px;
}
.close-button:not(:disabled):active {
margin-top: 2px;
border-bottom-width: 2px;
}
input[type='submit'] { input[type='submit'] {
cursor: pointer; cursor: pointer;
padding: 1px 6px; padding: 1px 6px;
@@ -558,9 +558,13 @@ const dropPriority = (event: DragEvent, section: PrioritySectionKey, bucket: Pri
</template> </template>
<style scoped> <style scoped>
:global(html), /*
:global(body), * The document contract belongs to this page only. An unscoped html/body rule
:global(#app) { * here leaked a 21px line-height and a 500px min-width onto every other screen.
*/
:global(html:has(#npc-policy-page)),
:global(body:has(#npc-policy-page)),
:global(#app:has(#npc-policy-page)) {
min-width: 500px; min-width: 500px;
margin: 0; margin: 0;
background: #000; background: #000;
@@ -569,11 +573,6 @@ const dropPriority = (event: DragEvent, section: PrioritySectionKey, bucket: Pri
line-height: 21px; line-height: 21px;
} }
:global(body:has(#npc-policy-page)) {
min-width: 500px;
line-height: 21px;
}
.npc-page { .npc-page {
min-height: 100vh; min-height: 100vh;
padding-bottom: 32px; padding-bottom: 32px;
+16 -17
View File
@@ -91,7 +91,7 @@ onMounted(() => {
<table class="legacy-table chart-table legacy-bg0"> <table class="legacy-table chart-table legacy-bg0">
<thead> <thead>
<tr> <tr>
<th colspan="4" class="legacy-bg2 chart-title"> </th> <td colspan="4" class="legacy-bg2 chart-title"> </td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -131,7 +131,7 @@ onMounted(() => {
<table class="legacy-table chart-table legacy-bg0"> <table class="legacy-table chart-table legacy-bg0">
<thead> <thead>
<tr> <tr>
<th colspan="4" class="legacy-bg2 chart-title"> </th> <td colspan="4" class="legacy-bg2 chart-title"> </td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -173,7 +173,7 @@ onMounted(() => {
<table v-if="data" class="legacy-table suspect-table legacy-bg0"> <table v-if="data" class="legacy-table suspect-table legacy-bg0">
<thead> <thead>
<tr> <tr>
<th colspan="3" class="legacy-bg2 chart-title"> (순간과도갱신)</th> <td colspan="3" class="legacy-bg2 chart-title"> (순간과도갱신)</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -234,27 +234,26 @@ onMounted(() => {
font-size: 14px; font-size: 14px;
} }
/*
* Ref's `.tb_layout td` sets no text alignment; the legacy markup centres
* individual cells with `align=center` instead.
*/
.legacy-table td, .legacy-table td,
.legacy-table th { .legacy-table th {
border: 1px solid gray; border: 1px solid gray;
padding: 0; padding: 0;
text-align: center;
font-weight: 400; font-weight: 400;
} }
.legacy-bg0 { .chart-title,
background-color: #302016; .period,
background-image: var(--sammo-texture-walnut); .time,
} .separator,
.spacer,
.legacy-bg1 { .record,
background-color: #423226; .suspect-table td,
background-image: var(--sammo-texture-blue); .suspect-table th {
} text-align: center;
.legacy-bg2 {
background-color: #14241b;
background-image: var(--sammo-texture-green);
} }
.title-table, .title-table,
+4 -4
View File
@@ -129,7 +129,7 @@ onMounted(async () => {
<strong> </strong> <strong> </strong>
<button class="legacy-button close-button" type="button" @click="closePage"> 닫기</button> <button class="legacy-button close-button" type="button" @click="closePage"> 닫기</button>
<span class="settings-menu"> <span class="settings-menu">
<button class="legacy-button" type="button" @click="settingsOpen = !settingsOpen"> 설정</button> <button class="legacy-button legacy-button--navigation" type="button" @click="settingsOpen = !settingsOpen"> 설정</button>
<button v-if="settingsOpen" class="settings-item" type="button" @click="toggleRankingPosition"> <button v-if="settingsOpen" class="settings-item" type="button" @click="toggleRankingPosition">
국가 순서 위치 변경(모바일 전용) 국가 순서 위치 변경(모바일 전용)
</button> </button>
@@ -139,7 +139,7 @@ onMounted(async () => {
<section class="year-selector"> <section class="year-selector">
<span>연월 선택:</span> <span>연월 선택:</span>
<button <button
class="legacy-button" class="legacy-button legacy-button--secondary"
type="button" type="button"
:disabled="selectedYearMonth === range?.firstYearMonth" :disabled="selectedYearMonth === range?.firstYearMonth"
@click="moveMonth(-1)" @click="moveMonth(-1)"
@@ -152,7 +152,7 @@ onMounted(async () => {
</option> </option>
</select> </select>
<button <button
class="legacy-button" class="legacy-button legacy-button--secondary"
type="button" type="button"
:disabled="selectedYearMonth === range?.currentYearMonth" :disabled="selectedYearMonth === range?.currentYearMonth"
@click="moveMonth(1)" @click="moveMonth(1)"
@@ -222,7 +222,7 @@ onMounted(async () => {
</template> </template>
<style scoped> <style scoped>
:global(body) { :global(body:has(.yearbook-page)) {
min-width: 500px; min-width: 500px;
} }