Spaces:
Running
Running
Download frontend/src/styles/chess/tablebase.css from chesstb/explorer: direct link, hf CLI and curl.
- Browser
- Download file 18.5 kB
-
https://huggingface.co/spaces/chesstb/explorer/resolve/main/frontend/src/styles/chess/tablebase.css
- Command line
-
hf download hf://spaces/chesstb/explorer/frontend/src/styles/chess/tablebase.css
-
curl -L -o tablebase.css https://huggingface.co/spaces/chesstb/explorer/resolve/main/frontend/src/styles/chess/tablebase.css
18.5 kB
| /* ── Results panel ──────────────────────────────────────────────────── */ | |
| .results-panel { container: results / inline-size; } | |
| .results-panel.is-pending .moves-view { pointer-events: none; } | |
| .results-panel.is-loading .moves-view { opacity: .55; pointer-events: none; } | |
| /* The auto-play "stop" control must stay clickable even while a probe is in | |
| flight - otherwise there's only a brief window between probe cycles where | |
| auto-play can actually be turned off. */ | |
| .results-panel.is-loading .btn--col-autoplay { pointer-events: auto; opacity: 1; } | |
| /* Shown only in the compact table view (see the `results` container query below). */ | |
| .metric-tabs { display: none; } | |
| /* ── SSE progress bar ───────────────────────────────────────────────── */ | |
| /* Inline next to the panel title, to its right, instead of a full-width | |
| bar under the panel header - keeps the header height stable and puts | |
| progress right next to the label it's updating towards. */ | |
| .probe-status { align-items: center; gap: 0.625rem; min-width: 0; } | |
| .probe-progress { | |
| width: 10.5rem; | |
| height: 0.25rem; | |
| border-radius: 0.125rem; | |
| background: var(--border); | |
| flex-shrink: 0; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .probe-progress__fill { | |
| position: absolute; | |
| left: 0; top: 0; bottom: 0; | |
| background: linear-gradient(90deg, var(--progress-from), var(--progress-to)); | |
| transition: width 0.3s ease; | |
| width: 0%; | |
| } | |
| .probe-progress.is-indeterminate .probe-progress__fill { | |
| width: 35%; | |
| animation: probe-pending 1.4s ease-in-out infinite; | |
| } | |
| @keyframes probe-pending { | |
| from { transform: translateX(-100%); } | |
| to { transform: translateX(290%); } | |
| } | |
| @media (prefers-reduced-motion: reduce) { | |
| .probe-progress.is-indeterminate .probe-progress__fill { animation: none; left: 32.5%; } | |
| } | |
| .probe-progress-label { | |
| font-size: var(--text-xs); | |
| color: var(--text-muted); | |
| font-family: var(--font-mono); | |
| white-space: nowrap; | |
| /* Optical alignment with the adjacent header text. */ | |
| position: relative; | |
| top: -0.5px; | |
| } | |
| .probe-progress-label:empty { | |
| display: none; | |
| } | |
| @media (max-width: 767px) { | |
| .results-panel .panel-header { flex-wrap: wrap; } | |
| .results-panel .panel-header__start { display: contents; } | |
| .probe-status { order: 3; flex: 0 0 100%; } | |
| .probe-status .probe-progress { flex: 1; width: auto; } | |
| } | |
| /* ── Moves view ─────────────────────────────────────────────────────── */ | |
| /* The header table is fixed above .moves-body-scroll, the only vertically | |
| scrolling element of the results panel, so the scrollbar starts at the | |
| first moves row. Both tables share the column widths below. No scrollbar | |
| gutter is reserved: while the body overflows, its table is widened by the | |
| scrollbar's measured width (see .has-vscroll below and syncMovesGutter() | |
| in engine/scroll.js), which keeps its columns at the header's pixel | |
| widths. */ | |
| .moves-view { | |
| flex: 1; | |
| min-height: 17.5rem; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .moves-head { flex: 0 0 auto; overflow: hidden; } | |
| .moves-body-scroll { | |
| flex: 1; | |
| min-height: 0; | |
| display: flex; | |
| flex-direction: column; | |
| overflow-x: hidden; | |
| overflow-y: auto; | |
| } | |
| /* The extra width lies beneath the scrollbar, outside the visible content box. */ | |
| .moves-body-scroll.has-vscroll .moves-table--body { | |
| width: calc(100% + var(--moves-vscroll-width, 0px)); | |
| } | |
| /* ── Moves table ────────────────────────────────────────────────────── */ | |
| .moves-table { | |
| width: 100%; | |
| flex: 0 0 auto; | |
| border-collapse: collapse; | |
| /* Fixed column widths (see the col rules below) keep column boundaries, | |
| and the auto-play buttons in the header, stable as score text length | |
| changes. */ | |
| table-layout: fixed; | |
| } | |
| .moves-table thead th { | |
| height: var(--thead-row-h); | |
| background: var(--surface-2); | |
| } | |
| #root-row td { height: var(--thead-root-h); } | |
| /* Keep move notation readable; score columns share the remaining width. */ | |
| .moves-table col.col-rank { width: 2rem; } | |
| .moves-table col.col-move { width: 4.25rem; } | |
| .moves-table col.col-score { width: auto; } | |
| .moves-table col.group-dtc.col-score { width: 9rem; } | |
| /* ── Group header row (DTZ / DTC / DTM / DTM50 labels) - centred ────── */ | |
| .moves-table thead tr.group-row th { | |
| font-size: var(--text-xs); | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| letter-spacing: 0.08em; | |
| color: var(--text-dim); | |
| text-align: center; /* centre all group labels */ | |
| padding: 0.375rem var(--space-2) var(--space-1); | |
| white-space: nowrap; | |
| } | |
| /* Flex row inside the th (not on the th itself - display:flex on a table | |
| cell suppresses its table-cell box, which breaks the whole table layout | |
| and stacks the columns). Inline-flex so it still respects text-align. */ | |
| .group-row-inner { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.3125rem; | |
| } | |
| /* Left edge on group boundaries - :not(.col-score) so it draws once per | |
| metric, at its Move cell (or at the group header's own colspan=2 cell), | |
| not again at the Score cell beside it. */ | |
| .moves-table th.group-dtz:not(.col-score), | |
| .moves-table th.group-dtc:not(.col-score), | |
| .moves-table th.group-dtm:not(.col-score), | |
| .moves-table th.group-dtm50:not(.col-score) { box-shadow: inset 1px 0 0 var(--border-2); } | |
| /* ── Column header row (Move / Score labels) ────────────────────────── */ | |
| .moves-table thead tr.col-row th { | |
| font-size: var(--text-xs); | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| letter-spacing: 0.08em; | |
| color: var(--text-muted); | |
| text-align: left; | |
| padding: 0.1875rem var(--space-2) 0.4375rem; | |
| box-shadow: inset 0 -1px 0 var(--border-2); | |
| white-space: nowrap; | |
| } | |
| .moves-table thead tr.col-row th.group-dtz:not(.col-score), | |
| .moves-table thead tr.col-row th.group-dtc:not(.col-score), | |
| .moves-table thead tr.col-row th.group-dtm:not(.col-score), | |
| .moves-table thead tr.col-row th.group-dtm50:not(.col-score) { | |
| box-shadow: inset 1px 0 0 var(--border-2), inset 0 -1px 0 var(--border-2); | |
| } | |
| /* Override group-boundary padding for col-row: add left spacing after | |
| border - :not(.col-score) for the same reason as the border rules | |
| above; Score's own right-aligned padding (below) is unaffected either | |
| way. */ | |
| .moves-table thead tr.col-row th.group-dtz:not(.col-score) { padding-left: var(--space-2); } | |
| .moves-table thead tr.col-row th.group-dtc:not(.col-score) { padding-left: var(--space-2); } | |
| .moves-table thead tr.col-row th.group-dtm:not(.col-score) { padding-left: var(--space-2); } | |
| .moves-table thead tr.col-row th.group-dtm50:not(.col-score){ padding-left: var(--space-2); } | |
| /* Move header: tight padding and tracking so "MOVE" fits the narrow move column. */ | |
| .moves-table thead tr.col-row th.col-move { padding-right: var(--space-1); letter-spacing: 0.03em; } | |
| .moves-table thead tr.col-row th.col-rank { padding-left: 0; padding-right: 0; text-align: center; text-overflow: clip; letter-spacing: 0; } | |
| /* Score column right-aligned */ | |
| .moves-table thead tr.col-row th.col-score { text-align: right; padding-right: 0.875rem; } | |
| /* ── Auto-play buttons - sit in the gap between Move and Score within | |
| the existing column-header row, instead of a dedicated row ─────────── */ | |
| .btn--col-autoplay { | |
| position: relative; | |
| height: 1rem; | |
| min-width: 1.25rem; | |
| padding: 0 0.3125rem; | |
| border-radius: 0.1875rem; | |
| line-height: 1; | |
| justify-content: center; | |
| color: var(--text-dim); | |
| } | |
| /* Invisible hit-area expansion - the visible button stays small enough to | |
| sit inline with the group label, but the clickable/tappable region | |
| clears a 24px minimum in both dimensions (WCAG 2.5.8 Target Size). */ | |
| .btn--col-autoplay::after { | |
| content: ''; | |
| position: absolute; | |
| inset: -0.25rem; | |
| } | |
| /* Two icons are always present; CSS toggles which is visible via the | |
| existing .is-playing class. Both are hand-built SVG rather than text | |
| glyphs, since a Unicode symbol ('▶'/'⏹') at 0.5625rem is at the mercy of the | |
| browser/OS's emoji font and renders inconsistently and fuzzily at that | |
| size. Both use fill: currentColor, so they automatically pick up the | |
| amber .is-playing colour from the .btn.is-playing rule below with no | |
| extra colour rule needed here. */ | |
| .autoplay-icon { | |
| width: 0.5625rem; | |
| height: 0.5625rem; | |
| fill: currentColor; | |
| } | |
| .autoplay-icon--play { display: block; } | |
| .autoplay-icon--stop { display: none; } | |
| .btn--col-autoplay.is-playing .autoplay-icon--play { display: none; } | |
| .btn--col-autoplay.is-playing .autoplay-icon--stop { display: block; } | |
| /* ── Table body cells ───────────────────────────────────────────────── */ | |
| .moves-table td { | |
| padding: 0.375rem var(--space-2); | |
| font-family: var(--font-mono); | |
| font-size: var(--text-xs); | |
| border-bottom: 1px solid var(--border); | |
| white-space: nowrap; | |
| } | |
| .moves-table tbody tr:hover { background: var(--surface-2); } | |
| .moves-table td.col-rank { | |
| color: var(--text-muted); | |
| font-size: var(--text-xs); | |
| text-align: center; | |
| padding-left: 0.125rem; | |
| padding-right: 0.125rem; | |
| } | |
| .moves-table td.col-move { | |
| color: var(--blue); | |
| font-weight: 500; | |
| padding-right: var(--space-1); | |
| cursor: pointer; | |
| } | |
| /* Group boundary edges on td, drawn the same way as the header cells. */ | |
| .moves-table td.group-dtz:not(.col-score) { box-shadow: inset 1px 0 0 var(--border-2); padding-left: var(--space-2); } | |
| .moves-table td.group-dtc:not(.col-score) { box-shadow: inset 1px 0 0 var(--border-2); padding-left: var(--space-2); } | |
| .moves-table td.group-dtm:not(.col-score) { box-shadow: inset 1px 0 0 var(--border-2); padding-left: var(--space-2); } | |
| .moves-table td.group-dtm50:not(.col-score){ box-shadow: inset 1px 0 0 var(--border-2); padding-left: var(--space-2); } | |
| .moves-table td.col-score { | |
| text-align: right; | |
| font-weight: 500; | |
| color: var(--text-muted); | |
| padding-left: var(--space-1); | |
| padding-right: 0.875rem; | |
| } | |
| /* Outcome colour mapping via data attribute */ | |
| .moves-table td.col-score[data-outcome] { color: var(--text-muted); } | |
| .moves-table td.col-score[data-outcome="win"] { color: var(--win); } | |
| .moves-table td.col-score[data-outcome="cursed_win"] { color: var(--amber); } | |
| .moves-table td.col-score[data-outcome="blessed_loss"] { color: var(--amber); } | |
| .moves-table td.col-score[data-outcome="loss"] { color: var(--loss); } | |
| .moves-table td.col-score[data-outcome="draw"] { color: var(--draw); } | |
| .moves-table td.col-score.is-mate { font-weight: 700; } | |
| .moves-table td.is-empty-cell { color: var(--text-muted); text-align: center; opacity: .35; } | |
| /* ── Root row ───────────────────────────────────────────────────────── */ | |
| /* Lives in <thead> (see ResultsPanel.jsx), pinned below the group and | |
| column rows. The label reuses | |
| .col-move for identical column alignment/font as a real move - only | |
| the pointer cursor is dropped, since it isn't a clickable move. Gets | |
| its own --surface-3 background plus a thin --gold-dim accent bar on | |
| the leading edge, so it reads as a distinct, pinned reference row | |
| rather than blending into the header above or the ranked rows below. | |
| .is-hidden (display:none) is applied by ResultsPanel.jsx when the | |
| "Show Root Row" setting is off. */ | |
| .moves-table td.col-move.is-root { | |
| cursor: default; | |
| color: var(--gold); | |
| font-weight: 600; | |
| } | |
| #root-row td { | |
| background: var(--surface-3); | |
| border: 0; | |
| box-shadow: inset 0 -1px 0 var(--border-2); | |
| } | |
| #root-row td.group-dtz:not(.col-score), | |
| #root-row td.group-dtc:not(.col-score), | |
| #root-row td.group-dtm:not(.col-score), | |
| #root-row td.group-dtm50:not(.col-score) { | |
| box-shadow: inset 1px 0 0 var(--border-2), inset 0 -1px 0 var(--border-2); | |
| } | |
| #root-row td:first-child { | |
| box-shadow: inset 3px 0 0 var(--gold-dim), inset 0 -1px 0 var(--border-2); | |
| } | |
| #root-row.is-hidden { display: none; } | |
| /* Pair hover - highlight both move + score cells together */ | |
| .moves-table td.is-pair-hover { background: var(--surface-3); } | |
| .moves-table td.is-pair-hover.col-move { color: var(--text); } | |
| .moves-table td.col-move[data-san], | |
| .moves-table td.col-score[data-san] { | |
| cursor: pointer; | |
| transition: color var(--tr), background var(--tr); | |
| } | |
| .moves-table td.col-move[data-san]:active { color: var(--gold); background: var(--surface-3); } | |
| .moves-table td.col-score[data-san]:active { background: var(--surface-3); } | |
| /* ── Info dot / warning dot ──────────────────────────────────────────── | |
| .info-dot sits after the "Draw" score text for a move whose draw is | |
| caused by insufficient material - the reason is a hover tooltip | |
| (native title attribute) instead of being spelled out inline, so the | |
| score column never has to fit "Draw by Insufficient Material". With | |
| touch or pen input, a tap on a dot shows the same text as a notice. | |
| Where a coarse pointer is available, each dot carries a transparent | |
| --touch-target wide box spanning the full height of its score cell, | |
| centered on the icon and clipped to the cell. | |
| .warning-dot sits in the same spot for a move flagged cursed win or | |
| blessed loss. Both share identical layout, icon, and colours (see | |
| DotIcon in ResultsPanel.jsx, which renders both) - only the hover | |
| title tells the two apart, so their rules are declared together | |
| below. */ | |
| .info-dot, .warning-dot { | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 0.8125rem; height: 0.8125rem; | |
| margin-left: var(--space-1); | |
| cursor: help; | |
| vertical-align: middle; | |
| flex-shrink: 0; | |
| } | |
| @media (any-pointer: coarse) { | |
| .moves-table td.col-score { position: relative; } | |
| .info-dot::after, .warning-dot::after { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| bottom: 0; | |
| width: var(--touch-target); | |
| } | |
| } | |
| .info-dot__icon, .warning-dot__icon { | |
| width: 100%; | |
| height: 100%; | |
| display: block; | |
| overflow: visible; | |
| } | |
| .info-dot__bg, .warning-dot__bg { fill: var(--text-dim); } | |
| .info-dot__mark, .warning-dot__mark { fill: var(--surface); } | |
| /* ── Empty state ────────────────────────────────────────────────────── */ | |
| .moves-empty-state { | |
| display: none; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| gap: var(--space-3); | |
| padding: 3rem var(--space-5); | |
| color: var(--text-muted); | |
| text-align: center; | |
| font-size: var(--text-xs); | |
| flex: 1; | |
| } | |
| .moves-empty-state__icon { font-size: 1.75rem; opacity: .18; } | |
| /* ── Loading shimmer ────────────────────────────────────────────────── */ | |
| @keyframes shimmer { | |
| 0%, 100% { opacity: 0.3; } | |
| 50% { opacity: 0.65; } | |
| } | |
| .is-loading .moves-table td.col-score, | |
| .is-loading .moves-table td.col-move { animation: shimmer 1.1s ease-in-out infinite; } | |
| /* ── Compact view ─────────────────────────────────────────────────────── | |
| Below 49rem the nine-column table is replaced by a three-column one | |
| so distances, units and annotations fit without wrapping. At 49rem, | |
| the 2rem rank + four 4.25rem moves + 9rem DTC score leave 7rem for | |
| each other score, enough for "Not Available" or five-digit plies | |
| with a warning dot and cell padding. | |
| (rank, move, score) for the metric chosen in .metric-tabs. */ | |
| @container results (width < 49rem) { | |
| .metric-tabs { | |
| display: grid; | |
| grid-template-columns: repeat(4, minmax(0, 1fr)); | |
| min-height: 2rem; | |
| } | |
| .metric-tab { | |
| min-width: 0; | |
| padding: 0 var(--space-1); | |
| border: 0 solid var(--border); | |
| border-bottom-width: 1px; | |
| border-radius: 0; | |
| background: var(--surface-2); | |
| color: var(--text-dim); | |
| font: 600 var(--text-xs) var(--font-ui); | |
| letter-spacing: 0.03em; | |
| } | |
| .metric-tab + .metric-tab { border-left: 1px solid var(--border); } | |
| .metric-tab.is-active { | |
| color: var(--gold); | |
| background: var(--gold-glow); | |
| box-shadow: inset 0 0 0 1px var(--gold-dim); | |
| } | |
| .metric-tab:focus-visible { outline-offset: -2px; } | |
| .moves-table[data-active-metric] .group-dtz, | |
| .moves-table[data-active-metric] .group-dtc, | |
| .moves-table[data-active-metric] .group-dtm, | |
| .moves-table[data-active-metric] .group-dtm50 { display: none; } | |
| .moves-table[data-active-metric="dtz"] .group-dtz, | |
| .moves-table[data-active-metric="dtc"] .group-dtc, | |
| .moves-table[data-active-metric="dtm"] .group-dtm, | |
| .moves-table[data-active-metric="dtm50"] .group-dtm50 { display: table-cell; } | |
| .moves-table[data-active-metric="dtz"] th.group-dtz:not(.col-score), | |
| .moves-table[data-active-metric="dtz"] td.group-dtz:not(.col-score) { padding-left: 0.625rem; } | |
| .moves-table[data-active-metric] col.group-dtz, | |
| .moves-table[data-active-metric] col.group-dtc, | |
| .moves-table[data-active-metric] col.group-dtm, | |
| .moves-table[data-active-metric] col.group-dtm50 { display: none; } | |
| .moves-table[data-active-metric] col.col-rank { width: 14%; } | |
| .moves-table[data-active-metric="dtz"] col.group-dtz, | |
| .moves-table[data-active-metric="dtc"] col.group-dtc, | |
| .moves-table[data-active-metric="dtm"] col.group-dtm, | |
| .moves-table[data-active-metric="dtm50"] col.group-dtm50 { display: table-column; } | |
| .moves-table[data-active-metric] col.col-move { width: 26%; } | |
| .moves-table[data-active-metric] col.col-score { width: 60%; } | |
| } | |