fix(ui): eliminate table/grid overflow breaking the frame at every width

Two root causes of tables and panels spilling past the window frame:

1. Global 'table { min-width: 850px }' forced EVERY table (source/score/modal/
   backtest) to 850px regardless of container -> now min-width:0 so tables
   shrink to 100%; only .factor-table (stock board, inside .table-wrap)
   keeps its 850px and scrolls. Small tables get table-layout:fixed +
   overflow-wrap on mobile so long content wraps instead of pushing out.

2. Grid/flex tracks lacked minmax(0,1fr) / min-width:0, so wide grid items
   (KPI numbers, theme cards) pushed the whole row past the viewport at
   768-1040px (doc overflow 208px). Fixed: all grid-template-columns use
   minmax(0,1fr); .content is now flex:1 1 auto + min-width:0; KPI/theme/bt
   cards get min-width:0 + overflow-wrap.

Verified programmatically in-browser: document overflow = 0 at 320/360/500/
600/720/760/768/820/900/1040/1200/1440px; score-table + source-table over=0
at all widths (incl inside the per-source modal). Board scrolls in .table-wrap
as designed. Backend suite 369 green (CSS-only change).
This commit is contained in:
Kunthawat Greethong
2026-08-30 09:10:21 +07:00
parent de5dd29217
commit 053b94b45f
5 changed files with 39 additions and 21 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -5,8 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#0b1018" />
<title>SET50 Signal Lab</title>
<script type="module" crossorigin src="/assets/index-DDvZwbpd.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CUx7tUuk.css">
<script type="module" crossorigin src="/assets/index-ePg0o_Uc.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BiR2h3Pv.css">
</head>
<body>
<div id="app"></div>

View File

@@ -59,7 +59,7 @@ button { cursor: pointer; }
.mode-detail, .version-line { margin-top: 3px; color: var(--faint); font: 10px 'DM Mono', monospace; }
.version-line { padding: 14px 3px 0; }
.content { width: min(100%, 1440px); margin: 0 auto; padding: 42px clamp(22px, 4vw, 64px) 70px; }
.content { flex: 1 1 auto; min-width: 0; width: min(100%, 1440px); margin: 0 auto; padding: 42px clamp(22px, 4vw, 64px) 70px; }
.topbar { display: flex; justify-content: space-between; gap: 24px; align-items: flex-start; padding-bottom: 35px; }
.eyebrow, .section-kicker { color: var(--mint); font: 500 10px 'DM Mono', monospace; letter-spacing: .14em; text-transform: uppercase; }
h1, h2, p { margin: 0; }
@@ -72,19 +72,19 @@ h2 { margin-top: 7px; font-size: 17px; letter-spacing: -.025em; }
.freshness-pill.pill-fixture .freshness-dot { background: var(--amber); box-shadow: 0 0 12px var(--amber); }
.as-of { margin-top: 10px; }
.kpi-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 12px; }
.kpi-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin-bottom: 12px; }
.kpi-card, .panel, .state-card { border: 1px solid var(--line); background: linear-gradient(145deg, rgba(21,31,45,.86), rgba(14,21,31,.94)); box-shadow: 0 18px 42px rgba(0,0,0,.12); }
.kpi-card { min-height: 132px; padding: 19px 19px 16px; border-radius: 10px; }
.kpi-card { min-height: 132px; min-width: 0; padding: 19px 19px 16px; border-radius: 10px; }
.accent-card { border-color: rgba(82,214,189,.3); background: linear-gradient(145deg, rgba(25,56,59,.8), rgba(14,31,38,.92)); }
.kpi-label { color: var(--muted); font: 10px 'DM Mono', monospace; text-transform: uppercase; letter-spacing: .08em; }
.kpi-value { margin-top: 12px; color: var(--text); font-size: 29px; font-weight: 700; letter-spacing: -.055em; }
.kpi-label { color: var(--muted); font: 10px 'DM Mono', monospace; text-transform: uppercase; letter-spacing: .08em; overflow-wrap: anywhere; }
.kpi-value { margin-top: 12px; color: var(--text); font-size: 29px; font-weight: 700; letter-spacing: -.055em; overflow-wrap: anywhere; }
.kpi-unit { margin-left: 4px; color: var(--mint); font: 14px 'DM Mono', monospace; }
.quality-value { font-size: 22px; text-transform: capitalize; }
.kpi-foot { margin-top: 10px; color: var(--faint); font: 10px 'DM Mono', monospace; }
.long-count, .positive-text { color: var(--mint); }
.short-count, .negative-text { color: var(--red); }
.hero-grid, .bottom-grid { display: grid; grid-template-columns: 1.45fr 1fr; gap: 12px; margin-bottom: 12px; }
.hero-grid, .bottom-grid { display: grid; grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); gap: 12px; margin-bottom: 12px; }
.panel { border-radius: 10px; padding: 22px; }
.panel-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 20px; }
.confidence-tag, .status-tag { padding: 6px 8px; color: var(--mint); border: 1px solid rgba(82,214,189,.22); border-radius: 6px; background: var(--mint-soft); font: 10px 'DM Mono', monospace; white-space: nowrap; }
@@ -113,7 +113,19 @@ h2 { margin-top: 7px; font-size: 17px; letter-spacing: -.025em; }
.strategy-meta { color: var(--faint); font: 10px 'DM Mono', monospace; }
.strategy-meta span { color: var(--line-bright); padding: 0 5px; }
.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; min-width: 850px; }
/* Tables shrink to their container by default (min-width 0) so nothing overflows
the card/window. Only the wide stock board (inside .table-wrap) forces 850px
and scrolls horizontally. Small tables (source/score) fit 100% width. */
table { width: 100%; border-collapse: collapse; min-width: 0; }
.factor-table { min-width: 850px; }
/* Small multi-column tables (source/score) must never push past their parent.
min-width:0 on table lets them shrink; overflow-x:auto on the containers is a
scroll safety net so long content scrolls instead of breaking the frame. */
.modal-card { overflow-x: auto; }
.lineage-panel, .sim-panel { min-width: 0; overflow-x: auto; }
.source-table, .score-table td { overflow-wrap: anywhere; }
th { padding: 12px 16px; color: var(--faint); border-bottom: 1px solid var(--line); font: 10px 'DM Mono', monospace; font-weight: 400; text-align: left; text-transform: uppercase; letter-spacing: .06em; }
td { padding: 14px 16px; border-bottom: 1px solid rgba(38,51,68,.72); color: var(--muted); font-size: 11px; vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
@@ -136,8 +148,8 @@ tbody tr:hover { background: rgba(255,255,255,.025); }
.theme-panel, .lineage-panel, .health-panel, .sim-panel, .backtest-panel,
.research-panel { padding: 0; overflow: hidden; }
.theme-panel { margin-top: 18px; }
.theme-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.theme-card { border: 1px solid var(--line-bright); border-radius: 10px; padding: 14px; background: var(--card); }
.theme-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.theme-card { border: 1px solid var(--line-bright); border-radius: 10px; padding: 14px; background: var(--card); min-width: 0; }
.theme-card-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.theme-chip { background: var(--accent-soft, rgba(63,161,255,0.12)); color: var(--accent); font: 9px 'DM Mono', monospace; padding: 2px 6px; border-radius: 4px; text-transform: uppercase; }
.theme-label-en { font: 600 12px/1.4 var(--font); color: var(--foreground); }
@@ -192,7 +204,7 @@ tbody tr:hover { background: rgba(255,255,255,.025); }
.contrib-calc em { font-style: normal; color: var(--accent); }
.contrib-calc strong { color: var(--mint); font-family: 'DM Mono', monospace; }
.contrib-calc { color: var(--text-2, #9aa); font-size: 12px; }
.fund-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; font-size: 12px; }
.fund-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; font-size: 12px; }
.fund-grid span { background: rgba(255,255,255,.03); border-radius: 6px; padding: 6px 8px; }
.fund-grid strong { color: var(--text); font-family: 'DM Mono', monospace; }
.score-table { width: 100%; border-collapse: collapse; font-size: 13px; }
@@ -214,8 +226,8 @@ tbody tr:hover { background: rgba(255,255,255,.025); }
.backtest-controls input, .backtest-controls select { background: #0a0e14; border: 1px solid var(--line-bright); color: var(--text); border-radius: 6px; padding: 7px 9px; font-size: 12px; }
.primary-btn { background: var(--mint); color: #062a1f; border: none; border-radius: 7px; padding: 9px 16px; font-weight: 700; cursor: pointer; font-size: 12px; }
.primary-btn:disabled { opacity: .5; cursor: not-allowed; }
.bt-kpi-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin: 12px 0; }
.bt-kpi { background: rgba(255,255,255,.03); border: 1px solid var(--line-bright); border-radius: 8px; padding: 12px; }
.bt-kpi-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin: 12px 0; }
.bt-kpi { background: rgba(255,255,255,.03); border: 1px solid var(--line-bright); border-radius: 8px; padding: 12px; min-width: 0; }
.bt-kpi span { display: block; font-size: 11px; color: var(--faint); margin-bottom: 6px; }
.bt-kpi strong { font-size: 15px; font-family: 'DM Mono', monospace; }
.bt-meta { margin: 4px 0 10px; font-size: 11px; }
@@ -270,7 +282,7 @@ th.sortable:hover { color: var(--text); }
th.sortable.active { color: var(--mint); }
.research-panel { margin-bottom: 12px; }
.research-grid { display: grid; grid-template-columns: 1fr 1.4fr; gap: 24px; align-items: center; margin-top: 22px; }
.research-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: 24px; align-items: center; margin-top: 22px; }
.research-status { display: inline-block; padding: 7px 9px; border-radius: 5px; font: 11px 'DM Mono', monospace; text-transform: uppercase; }
.status-ready { color: var(--mint); background: var(--mint-soft); }
.status-blocked { color: var(--amber); background: var(--amber-soft); }
@@ -280,12 +292,12 @@ th.sortable.active { color: var(--mint); }
.gate-list { display: grid; gap: 0; border-top: 1px solid var(--line); }
.gate-row { display: flex; justify-content: space-between; gap: 15px; padding: 12px 0; border-bottom: 1px solid var(--line); color: var(--muted); font-size: 11px; }
.gate-row strong { color: var(--text); font: 10px 'DM Mono', monospace; text-align: right; }
.research-results { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-top: 18px; }
.research-results { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin-top: 18px; }
.research-result-row { display: grid; gap: 8px; padding: 12px; border: 1px solid var(--line); border-radius: 6px; color: var(--faint); font: 9px 'DM Mono', monospace; }
.research-result-row strong { font-size: 14px; }
.empty-research { margin-top: 18px; padding: 14px; border: 1px dashed var(--line-bright); border-radius: 6px; color: var(--faint); font: 10px 'DM Mono', monospace; }
.bottom-grid { grid-template-columns: 1fr 1fr; }
.bottom-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.thesis-panel { background: linear-gradient(145deg, rgba(36,48,64,.9), rgba(15,23,34,.95)); }
.thesis-panel p, .ledger-panel p { margin-top: 14px; color: var(--muted); font-size: 12px; line-height: 1.8; }
.thesis-rule { display: flex; align-items: center; gap: 9px; margin-top: 24px; color: var(--mint); font: 10px 'DM Mono', monospace; }
@@ -311,7 +323,7 @@ input:focus { border-color: var(--mint); box-shadow: 0 0 0 3px rgba(82,214,189,.
@media (max-width: 1040px) {
.sidebar { width: 208px; flex-basis: 208px; }
.kpi-grid { grid-template-columns: repeat(2, 1fr); }
.kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 760px) {
@@ -332,12 +344,18 @@ input:focus { border-color: var(--mint); box-shadow: 0 0 0 3px rgba(82,214,189,.
header (#1a7f) would sit inset 18px further than the body. */
.panel:not(.theme-panel):not(.stock-panel):not(.lineage-panel):not(.health-panel):not(.sim-panel):not(.backtest-panel):not(.research-panel):not(.signal-panel) { padding: 18px; }
.signal-header { padding: 18px; }
/* Force small multi-column tables (stock factor board is the exception — it
intentionally keeps its 850px min-width inside .table-wrap) to fit their
narrow container on mobile: fixed layout shrinks columns and wraps content
instead of pushing the table past the frame. */
.source-table, .score-table { table-layout: fixed; }
.source-table th, .source-table td, .score-table th, .score-table td { overflow-wrap: anywhere; word-break: break-word; }
.research-grid { grid-template-columns: 1fr; gap: 18px; }
.research-results { grid-template-columns: repeat(2, 1fr); }
.research-results { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 500px) {
.kpi-grid { grid-template-columns: 1fr 1fr; gap: 7px; }
.kpi-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 7px; }
.kpi-card { min-height: 112px; padding: 14px; }
.kpi-value { font-size: 22px; }
.kpi-foot { font-size: 9px; }