diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-17 14:32:37 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-17 14:32:37 +0800 |
| commit | 5c0ba37eda80d39e6ceca59bb1d5f4942f858995 (patch) | |
| tree | 948723f9cedf7ccb0707fa6ee516bd30fe20fd10 /frontend/src/app.css | |
| download | strategy-lab-5c0ba37eda80d39e6ceca59bb1d5f4942f858995.tar.gz | |
chore: establish Strategy Lab source baseline (development, not release)
Diffstat (limited to 'frontend/src/app.css')
| -rw-r--r-- | frontend/src/app.css | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/frontend/src/app.css b/frontend/src/app.css new file mode 100644 index 0000000..c9ed4be --- /dev/null +++ b/frontend/src/app.css @@ -0,0 +1,249 @@ +:root { + --bg: #f7fafb; + --surface: #ffffff; + --surface-2: #f1f5f6; + --border: #e2e8ea; + --border-strong: #cbd5d8; + --text: #1e2a30; + --text-2: #4b5a62; + --text-3: #7b8a92; + --teal: #0d9488; + --teal-strong: #0f766e; + --teal-soft: #e6f4f2; + --ink: #12312d; + --danger: #b91c1c; + --danger-soft: #fdf2f2; + --warn: #92400e; + --warn-soft: #fff9ec; + --ok: #15803d; + --ok-soft: #ecfdf3; + --radius: 10px; + --shadow: 0 1px 2px rgba(16, 42, 49, 0.05), 0 2px 8px rgba(16, 42, 49, 0.05); + --mono: ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, 'Noto Sans Mono CJK SC', monospace; + --font: -apple-system, 'SF Pro Text', BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, + 'Noto Sans CJK SC', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif; +} + +* { box-sizing: border-box; } + +html, body { + margin: 0; + padding: 0; + background: var(--bg); + color: var(--text); + font-family: var(--font); + font-size: 14.5px; + line-height: 1.55; + -webkit-font-smoothing: antialiased; +} + +h1, h2, h3, h4 { font-weight: 600; margin: 0; } +h1 { font-size: 1.35rem; } +h2 { font-size: 1.1rem; } +h3 { font-size: 1rem; } +a { color: var(--teal-strong); text-decoration: none; } +a:hover { text-decoration: underline; } +p { margin: 0.4rem 0; } + +button, input, select, textarea { font: inherit; color: inherit; } + +.btn { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.45rem 0.95rem; + border-radius: var(--radius); + border: 1px solid var(--border-strong); + background: var(--surface); + color: var(--text); + cursor: pointer; + font-size: 0.92rem; + transition: background 0.12s, border-color 0.12s, box-shadow 0.12s; + white-space: nowrap; +} +.btn:hover:not(:disabled) { background: var(--surface-2); } +.btn:disabled { opacity: 0.5; cursor: not-allowed; } +.btn:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, a:focus-visible { + outline: 2px solid var(--teal); + outline-offset: 1px; +} +.btn.primary { + background: var(--teal-strong); + border-color: var(--teal-strong); + color: #fff; +} +.btn.primary:hover:not(:disabled) { background: #115e59; } +.btn.danger { color: var(--danger); border-color: #f2c7c7; } +.btn.danger:hover:not(:disabled) { background: var(--danger-soft); } +.btn.ghost { border-color: transparent; background: transparent; } +.btn.ghost:hover:not(:disabled) { background: var(--surface-2); } +.btn.small { padding: 0.25rem 0.6rem; font-size: 0.83rem; } +.btn[aria-pressed='true'] { background: var(--teal-soft); border-color: var(--teal); color: var(--ink); } + +label { display: block; font-size: 0.84rem; color: var(--text-2); font-weight: 500; margin-bottom: 0.25rem; } +input, select, textarea.plain { + width: 100%; + padding: 0.45rem 0.65rem; + border: 1px solid var(--border-strong); + border-radius: 8px; + background: var(--surface); + font-size: 0.92rem; +} +input[type='checkbox'], input[type='radio'] { width: auto; } + +.card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 1rem 1.15rem; + box-shadow: var(--shadow); +} +.card + .card { margin-top: 0.9rem; } + +.hint { color: var(--text-3); font-size: 0.83rem; } + +table.data { + width: 100%; + border-collapse: collapse; + font-size: 0.88rem; +} +table.data th { + text-align: left; + font-weight: 600; + color: var(--text-2); + border-bottom: 1px solid var(--border-strong); + padding: 0.45rem 0.6rem; + white-space: nowrap; +} +table.data td { + border-bottom: 1px solid var(--border); + padding: 0.42rem 0.6rem; + vertical-align: top; +} +table.data tr:hover td { background: var(--surface-2); } +.num { font-family: var(--mono); font-variant-numeric: tabular-nums; } + +.badge { + display: inline-block; + padding: 0.1rem 0.5rem; + border-radius: 999px; + font-size: 0.76rem; + font-weight: 600; + border: 1px solid transparent; +} +.badge.teal { background: var(--teal-soft); color: var(--teal-strong); } +.badge.grey { background: var(--surface-2); color: var(--text-2); border-color: var(--border); } +.badge.warn { background: var(--warn-soft); color: var(--warn); border-color: #f0ddb4; } +.badge.danger { background: var(--danger-soft); color: var(--danger); border-color: #f2c7c7; } +.badge.ok { background: var(--ok-soft); color: var(--ok); border-color: #c3ecd0; } + +.banner { + border-radius: var(--radius); + padding: 0.65rem 0.9rem; + font-size: 0.9rem; + border: 1px solid; + display: flex; + gap: 0.6rem; + align-items: flex-start; +} +.banner.error { background: var(--danger-soft); border-color: #f2c7c7; color: var(--danger); } +.banner.warn { background: var(--warn-soft); border-color: #f0ddb4; color: var(--warn); } +.banner.info { background: var(--teal-soft); border-color: #c8eae6; color: var(--teal-strong); } + +.stack { display: flex; flex-direction: column; gap: 0.75rem; } +.row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } +.spread { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; flex-wrap: wrap; } +.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; } +.grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.75rem; } + +.tabs { + display: flex; + gap: 0.25rem; + border-bottom: 1px solid var(--border); +} +.tabs button { + padding: 0.55rem 1rem; + border: none; + background: none; + color: var(--text-2); + font-size: 0.95rem; + cursor: pointer; + border-bottom: 2px solid transparent; + border-radius: 6px 6px 0 0; +} +.tabs button:hover { background: var(--surface-2); } +.tabs button[aria-selected='true'] { + color: var(--teal-strong); + font-weight: 600; + border-bottom-color: var(--teal); +} + +/* metric tiles shared by the results card and other metric rows; without + these the six result metrics stacked vertically with large blank space */ +.metric-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 0.7rem; +} +.metric { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 0.6rem 0.85rem; + min-width: 0; +} +.mlabel { color: var(--text-3); font-size: 0.8rem; } +.mvalue { + font-size: 1.15rem; + font-weight: 700; + color: var(--text); + font-variant-numeric: tabular-nums; + overflow-wrap: anywhere; +} +.cols-2 { display: grid; grid-template-columns: 1.4fr 1fr; gap: 1rem; align-items: start; } +.table-wrap { overflow-x: auto; min-width: 0; } + +@media (max-width: 900px) { + .cols-2 { grid-template-columns: minmax(0, 1fr); } + .metric-grid { grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); } +} + +code, pre { + font-family: var(--mono); + font-size: 0.85rem; +} +pre { margin: 0; white-space: pre-wrap; word-break: break-all; } + +.sr-only { + position: absolute; + width: 1px; height: 1px; + padding: 0; margin: -1px; + overflow: hidden; clip: rect(0 0 0 0); + border: 0; +} + +/* ---- narrow-viewport wrapping (no global content hiding) ---- + Real provider error strings contain long unbreakable URLs; .spread/.stack + children would otherwise propagate min-content outwards and inflate the + document width on mobile. `overflow-wrap` keeps every word reachable while + wrapping; nothing is hidden — long tables get a local scroll container. */ +.card, .stack, .row, .spread, .banner { min-width: 0; } +.banner { overflow-wrap: anywhere; } +.banner .err-list { overflow-wrap: anywhere; } +.card table.data { min-width: 0; } + +@media (max-width: 900px) { + /* collapse multi-column forms: the dataset wizard grid3 + label min-widths + measured 523px min-content at 375px viewport (mobile-overflow-probe) */ + .grid2, .grid3 { grid-template-columns: minmax(0, 1fr); } + /* wrap table cells instead of nowrap inflating min-content; a truly wide + tabular block still gets its own local scrollbar (.scrollx), content + remains fully accessible without a document-level horizontal overflow */ + table.data th, table.data td { + white-space: normal; + overflow-wrap: anywhere; + } + .btn { white-space: normal; } +} + + |
