crabidy/cbd-web/style.css

485 lines
9.4 KiB
CSS

/* crabidy web client — pure modern CSS (architecture/web-client.md).
One accent variable (crab orange-red), light and dark themes via
color-scheme + light-dark(); the theme toggle stamps data-theme on
<html>, otherwise the OS decides. No frameworks, no external
requests — everything ships in the bundle. */
:root {
color-scheme: light dark;
/* The crab. Every accent tone derives from this one value. */
--accent: oklch(0.62 0.19 35);
--accent-strong: color-mix(in oklch, var(--accent) 85%, black);
--accent-soft: color-mix(in oklch, var(--accent) 14%, transparent);
--on-accent: oklch(0.99 0.005 60);
--bg: light-dark(oklch(0.98 0.005 60), oklch(0.17 0.01 260));
--bg-raised: light-dark(oklch(1 0 0), oklch(0.21 0.012 260));
--fg: light-dark(oklch(0.25 0.015 260), oklch(0.92 0.005 60));
--fg-dim: light-dark(oklch(0.52 0.012 260), oklch(0.68 0.008 60));
--danger: light-dark(oklch(0.54 0.2 25), oklch(0.68 0.19 25));
--border: color-mix(in oklch, var(--fg) 14%, transparent);
--shadow: 0 8px 32px light-dark(rgb(0 0 0 / 0.14), rgb(0 0 0 / 0.55));
}
:root[data-theme="light"] {
color-scheme: light;
}
:root[data-theme="dark"] {
color-scheme: dark;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font: 15px/1.45 system-ui, sans-serif;
background: var(--bg);
color: var(--fg);
overscroll-behavior: none;
}
button {
font: inherit;
color: inherit;
background: var(--accent);
color: var(--on-accent);
border: none;
border-radius: 6px;
padding: 0.3rem 0.75rem;
cursor: pointer;
&:hover {
background: var(--accent-strong);
}
&:disabled {
opacity: 0.35;
cursor: default;
}
&.ghost {
background: transparent;
color: var(--fg-dim);
padding: 0.25rem 0.5rem;
&:hover:not(:disabled) {
background: var(--accent-soft);
color: var(--fg);
}
&.active {
color: var(--accent);
background: var(--accent-soft);
}
}
&.ghost.danger {
background: transparent;
color: var(--danger);
&:hover:not(:disabled) {
background: color-mix(in oklch, var(--danger) 15%, transparent);
}
}
}
input {
font: inherit;
color: var(--fg);
background: var(--bg);
border: 1px solid var(--border);
border-radius: 6px;
padding: 0.4rem 0.6rem;
&:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
}
/* ---- frame ---------------------------------------------------------- */
.shell {
display: grid;
grid-template-rows: auto 1fr auto;
block-size: 100dvh;
}
.topbar {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.4rem 0.9rem;
border-block-end: 1px solid var(--border);
background: var(--bg-raised);
& .brand {
color: var(--accent);
font-weight: 700;
font-size: 1.05rem;
letter-spacing: 0.02em;
}
& .conn {
font-size: 0.85rem;
color: var(--fg-dim);
&.offline {
color: var(--danger);
}
}
& .topbar-actions {
margin-inline-start: auto;
display: flex;
gap: 0.25rem;
}
}
/* ---- panes ----------------------------------------------------------- */
.panes {
display: grid;
grid-template-columns: 3fr 2fr;
min-block-size: 0;
}
.pane {
display: grid;
grid-template-rows: auto 1fr auto;
min-block-size: 0;
border-inline-end: 1px solid var(--border);
/* The focused pane shows it like the TUI's highlighted border. */
box-shadow: inset 0 2px 0 transparent;
&:last-child {
border-inline-end: none;
}
&.focused {
box-shadow: inset 0 2px 0 var(--accent);
}
}
.toolbar {
display: flex;
align-items: center;
gap: 0.15rem;
padding: 0.35rem 0.6rem;
border-block-end: 1px solid var(--border);
overflow-x: auto;
& .path {
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
& .spacer {
flex: 1;
}
}
.list {
margin: 0;
padding: 0.25rem 0;
list-style: none;
overflow-y: auto;
min-block-size: 0;
& li {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.28rem 0.75rem;
cursor: pointer;
border-inline-start: 3px solid transparent;
& .title {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
& .badge {
color: var(--fg-dim);
font-size: 0.8rem;
}
&:hover {
background: var(--accent-soft);
}
&.selected {
background: var(--accent-soft);
border-inline-start-color: var(--accent);
}
&.marked .title {
color: var(--accent);
font-weight: 600;
&::before {
content: "* ";
}
}
/* Skipped tracks carry no audio (incremental captures). */
&.skipped .title {
color: var(--danger);
}
&.node .title {
color: color-mix(in oklch, var(--fg) 80%, var(--accent));
}
&.current .title {
color: var(--accent);
font-weight: 700;
}
& .row-action {
visibility: hidden;
}
&:hover .row-action,
&.selected .row-action {
visibility: visible;
}
}
}
.capture-lines {
padding: 0.2rem 0.75rem 0.4rem;
font-size: 0.85rem;
color: var(--fg-dim);
& .capture-line.error {
color: var(--danger);
}
}
/* ---- transport -------------------------------------------------------- */
.transport {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 1rem;
padding: 0.5rem 0.9rem;
border-block-start: 1px solid var(--border);
background: var(--bg-raised);
& .controls {
display: flex;
align-items: center;
gap: 0.1rem;
& .big {
font-size: 1.3rem;
color: var(--accent);
}
}
& .now-playing {
min-inline-size: 0;
& .np-title {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 600;
}
/* Frequency-spectrum bars (architecture/spectrum.md): a row of
accent-colored columns whose heights track the streamed bins. */
& .spectrum {
display: flex;
align-items: flex-end;
gap: 1px;
block-size: 1.5rem;
margin-block-end: 0.15rem;
& .bar {
flex: 1;
min-block-size: 1px;
background: var(--accent);
border-radius: 1px;
transition: height 0.08s linear;
}
}
}
& .progress {
display: flex;
align-items: center;
gap: 0.5rem;
& .time {
font-size: 0.8rem;
color: var(--fg-dim);
font-variant-numeric: tabular-nums;
}
& .gauge {
flex: 1;
block-size: 6px;
border-radius: 3px;
background: var(--accent-soft);
overflow: hidden;
& .gauge-fill {
block-size: 100%;
background: var(--accent);
border-radius: 3px;
transition: width 0.4s linear;
}
}
}
& .volume {
display: flex;
align-items: center;
gap: 0.4rem;
& input[type="range"] {
inline-size: 7rem;
accent-color: var(--accent);
padding: 0;
border: none;
background: transparent;
}
}
}
/* ---- overlays ---------------------------------------------------------- */
.overlay {
position: fixed;
inset: 0;
display: grid;
place-items: center;
background: rgb(0 0 0 / 0.4);
backdrop-filter: blur(2px);
}
.dialog {
display: grid;
gap: 0.7rem;
min-inline-size: min(26rem, 90vw);
max-block-size: 85dvh;
overflow-y: auto;
padding: 1.1rem 1.3rem;
border-radius: 10px;
background: var(--bg-raised);
box-shadow: var(--shadow);
& label {
color: var(--fg-dim);
font-size: 0.9rem;
}
& .dialog-actions {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
}
}
/* Visual (paint-select) mode badge in a pane toolbar — the web counterpart of
the TUI's "— VISUAL" pane title. */
.mode {
padding: 0.05rem 0.4rem;
border: 1px solid var(--accent);
border-radius: 0.6rem;
color: var(--accent);
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.04em;
}
.help {
min-inline-size: min(52rem, 94vw);
& h2 {
margin: 0;
color: var(--accent);
}
& .help-columns {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
gap: 0.5rem 2rem;
& h3 {
margin: 0.4rem 0 0.2rem;
font-size: 0.9rem;
color: var(--fg-dim);
text-transform: uppercase;
letter-spacing: 0.06em;
}
& table {
border-collapse: collapse;
inline-size: 100%;
& td {
padding: 0.12rem 0.4rem 0.12rem 0;
vertical-align: top;
}
& .key {
font-family: ui-monospace, monospace;
color: var(--accent);
white-space: nowrap;
}
}
}
}
.toast {
position: fixed;
inset-block-end: 4.5rem;
inset-inline-start: 50%;
translate: -50% 0;
padding: 0.5rem 1rem;
border-radius: 8px;
background: var(--danger);
color: var(--on-accent);
box-shadow: var(--shadow);
}
/* ---- phone ------------------------------------------------------------- */
@media (max-width: 700px) {
/* One pane at a time; Tab (or tapping a pane edge) switches — the
unfocused pane collapses to a slim strip acting as its tab. */
.panes {
grid-template-columns: 1fr;
grid-template-rows: 1fr auto;
}
.pane:not(.focused) {
grid-template-rows: auto;
max-block-size: 2.4rem;
overflow: hidden;
border-block-start: 1px solid var(--border);
opacity: 0.75;
}
.transport {
grid-template-columns: 1fr;
gap: 0.4rem;
& .volume {
justify-content: flex-end;
}
}
}