diff --git a/docs/CODE-BOUNDARIES.md b/docs/CODE-BOUNDARIES.md index 8ca26d9f2..a23ba3e0f 100644 --- a/docs/CODE-BOUNDARIES.md +++ b/docs/CODE-BOUNDARIES.md @@ -749,6 +749,14 @@ teardown. CCTV effective-pose conversion and tuner/device operations remain feature responsibilities. A Vue replacement can reuse this contract and CSS; it replaces DOM construction, rather than adding a second state owner. +The shared stylesheet owns field surfaces and disabled opacity; feature and theme +palettes use `--gev-field-background`, `--gev-field-border-color`, +`--gev-field-color` and `--gev-field-disabled-opacity`. Radio retains its own +compact density and arrow space. CCTV owns calibration editor width in +`cctv.css`; the shared inline density does not impose a feature-specific width. +Cyber supplies a shared-field palette explicitly and keeps its legacy native +field rules separate, so Radio's palette remains an intentional exception. + ### Read-only status badges `src/ui/statusBadge.js` constructs/updates ordinary badges without listeners, diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 7f0dd1476..922c285b6 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -1233,6 +1233,17 @@ Feature owners retain bounds, steps, formatted values, input/change timing and listener cleanup. The radio tuning dial, imagery comparison divider and event comparison/progression controls keep their specialized presentation and behavior. +## Shared control CSS ownership + +Shared styles own ordinary control surfaces and interaction states; feature CSS +owns surrounding layout and context-specific sizing. CCTV's calibration editor +width lives in `ui/styles/cctv.css`, while the shared inline field variant owns +only density and spinner treatment. Radio supplies its field palette and disabled +opacity through the `--gev-field-*` variables, retaining compact typography and +arrow space locally. Cyber supplies shared-field palette variables explicitly; +its legacy native-field palette rules exclude `.gev-field`. These ownership +boundaries preserve the current Tactical/Cyber appearance and native behavior. + ## Ordinary status badges CCTV source/frame labels, calibration provenance and Weather coverage labels use diff --git a/src/ui/styles/cctv.css b/src/ui/styles/cctv.css index 12c369a9e..1e8dced21 100644 --- a/src/ui/styles/cctv.css +++ b/src/ui/styles/cctv.css @@ -109,6 +109,10 @@ html:not([lang='en']) .cctv-controls .scene-btn { gap: 5px; } +.cctv-cal-input { + width: 58px; +} + .cctv-cal-value { font-family: var(--font-mono); font-size: 9px; diff --git a/src/ui/styles/cyber.css b/src/ui/styles/cyber.css index cad6e69af..e25bbae2d 100644 --- a/src/ui/styles/cyber.css +++ b/src/ui/styles/cyber.css @@ -544,12 +544,21 @@ text-shadow: none; } -:root[data-ui-theme='cyber'] :where(.pp-select, select, input, textarea) { +:root[data-ui-theme='cyber'] + :where(.pp-select, select, input, textarea):where(:not(.gev-field)) { border-color: rgba(206, 222, 226, 0.24); background-color: rgba(26, 35, 39, 0.96); color: var(--text-primary); } +/* Shared fields consume theme palettes through their own surface rule. + Feature palettes (Radio) retain precedence without restyling native fields. */ +:root[data-ui-theme='cyber'] .gev-field { + --gev-field-border-color: rgba(206, 222, 226, 0.24); + --gev-field-background: rgba(26, 35, 39, 0.96); + --gev-field-color: var(--text-primary); +} + :root[data-ui-theme='cyber'] :where( .panel-title, diff --git a/src/ui/styles/form-field.css b/src/ui/styles/form-field.css index e0eec99b1..1d7f2b34f 100644 --- a/src/ui/styles/form-field.css +++ b/src/ui/styles/form-field.css @@ -1,11 +1,11 @@ -/* Native selects and numeric inputs. Features own options, constraints and - commit timing; density variants do not introduce custom keyboard behavior. */ +/* Native selects and numeric inputs. This file owns the field surface/states; + features and themes supply palette variables and retain their own layout. */ .gev-field { min-width: 0; - background: rgba(255, 255, 255, 0.06); - border: 1px solid rgba(255, 255, 255, 0.14); + background: var(--gev-field-background, rgba(255, 255, 255, 0.06)); + border: 1px solid var(--gev-field-border-color, rgba(255, 255, 255, 0.14)); border-radius: 7px; - color: var(--text-primary); + color: var(--gev-field-color, var(--text-primary)); padding: 6px 8px; font-family: var(--font-sans); font-size: 12px; @@ -18,7 +18,7 @@ } .gev-field:disabled { - opacity: 0.45; + opacity: var(--gev-field-disabled-opacity, 0.45); } .gev-field--compact { @@ -30,15 +30,14 @@ padding: 2px 6px; } -/* Effective-pose editors must fit inside CCTV value chips. Retain their - existing spinner-free surface; calibration conversion stays with the owner. */ +/* Compact inline editors share density and hide native number spinners. + Their containing feature sets width to fit the surrounding readout. */ .gev-field--inline { - width: 58px; font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.7px; - background: rgba(0, 212, 255, 0.1); - border-color: rgba(0, 212, 255, 0.5); + --gev-field-background: rgba(0, 212, 255, 0.1); + --gev-field-border-color: rgba(0, 212, 255, 0.5); border-radius: 4px; padding: 1px 3px; -moz-appearance: textfield; diff --git a/src/ui/styles/radio.css b/src/ui/styles/radio.css index 568a60a9e..483321992 100644 --- a/src/ui/styles/radio.css +++ b/src/ui/styles/radio.css @@ -49,13 +49,15 @@ min-height: 36px; } #radio-filter { - min-width: 0; + /* Radio owns its compact density and arrow space; shared fields paint the + palette and disabled state, including this intentional theme exception. */ padding: 7px 24px 7px 8px; - border: 1px solid rgba(65, 220, 231, 0.28); border-radius: 5px; - color: #c5faff; - background: rgba(5, 24, 31, 0.88); font: 9px/1.2 var(--font-mono); + --gev-field-border-color: rgba(65, 220, 231, 0.28); + --gev-field-color: #c5faff; + --gev-field-background: rgba(5, 24, 31, 0.88); + --gev-field-disabled-opacity: 0.42; } .radio-station-card { display: grid; @@ -273,11 +275,13 @@ html:not([lang='en']) .radio-transport button { color: #fff; outline: none; } -.radio-transport button:disabled, -#radio-filter:disabled { +.radio-transport button:disabled { opacity: 0.42; cursor: not-allowed; } +#radio-filter:disabled { + cursor: not-allowed; +} #radio-play-btn.active { color: #fff; border-color: rgba(101, 247, 255, 0.8);