- Settings tab: the Installer view (app/static/settings.js), drawn from
app/uilayout.py through the new app/uipage.py: search, six collapsible
groups with "n to fix / n to check / n changed" badges and an "applies
after a restart" chip, radio cards, segmented choices, sliders with units
(the peak limit in kW), entity pickers with search and the live reading,
inverter cards, solar rows, locked measured values behind "Change", and
the add-on's refusals, warnings and info beside the field they name.
- Save path: the page posts its model to the existing config/validate and
config/save; uimodel turns it into options before the shipped validator,
so nothing it refuses is saved. No route added. A value equal to the
stored one is written as stored, so a save that changed nothing writes an
identical blob. Save bar, then the restart dialog that names 0 W.
- The region choice shows its preset year; Wallonia and Brussels are listed
as having no preset yet (TARIFF-22). "Classic settings" keeps /config.
- P1 test findings: (a) "Going to 0 W" until the last accepted power request
is 0 W; (b) the trade blocks and the price line name the feed's own points
("Lowest published price"), the hour bars say "hour average"; (c) the entity
list's WARNING gives a reason on a timeout.
- test_settings.py; test_home, test_haentities, test_uilayout extended.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LyR88rXETvwhTgx6yHE39p
219 lines
17 KiB
CSS
219 lines
17 KiB
CSS
/* UX-13 page styles. Colours follow the approved mockup (UX-13-mockup.html,
|
|
version 2) and prefers-color-scheme. No web fonts, no external files. */
|
|
:root{
|
|
--bg:#f3f4f6;--card:#ffffff;--ink:#1b1d21;--muted:#5b626c;--faint:#8a919b;--line:#e2e5e9;
|
|
--accent:#1c64d9;--accent-soft:#e6efff;--on-accent:#ffffff;
|
|
--ok:#17773a;--ok-bg:#e6f4eb;--warn:#955300;--warn-bg:#fcefd9;--bad:#c02626;--bad-bg:#fce8e8;--info:#5b626c;--info-bg:#eceef1;
|
|
--solar:#c98a00;--house:#6c47c2;--grid:#56657a;--batt:#17773a;
|
|
--cheap:#8fcca4;--dear:#f0ad72;--mid:#cfd5dc;--track:#dde1e6;--shadow:0 1px 2px rgba(0,0,0,.05);
|
|
}
|
|
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){
|
|
--bg:#111317;--card:#1a1d23;--ink:#e7e9ec;--muted:#a2a9b3;--faint:#7c848f;--line:#2b3038;
|
|
--accent:#6ea2ff;--accent-soft:#1d2a44;--on-accent:#0b1220;
|
|
--ok:#7ccf98;--ok-bg:#15291d;--warn:#f3c16b;--warn-bg:#2e2412;--bad:#f28b82;--bad-bg:#3a1a19;--info:#a2a9b3;--info-bg:#22262d;
|
|
--solar:#f0c04a;--house:#b39cf0;--grid:#9fb0c6;--batt:#7ccf98;
|
|
--cheap:#3f7d56;--dear:#9a5a26;--mid:#3a414b;--track:#343a44;--shadow:none;
|
|
}}
|
|
:root[data-theme="dark"]{
|
|
--bg:#111317;--card:#1a1d23;--ink:#e7e9ec;--muted:#a2a9b3;--faint:#7c848f;--line:#2b3038;
|
|
--accent:#6ea2ff;--accent-soft:#1d2a44;--on-accent:#0b1220;
|
|
--ok:#7ccf98;--ok-bg:#15291d;--warn:#f3c16b;--warn-bg:#2e2412;--bad:#f28b82;--bad-bg:#3a1a19;--info:#a2a9b3;--info-bg:#22262d;
|
|
--solar:#f0c04a;--house:#b39cf0;--grid:#9fb0c6;--batt:#7ccf98;
|
|
--cheap:#3f7d56;--dear:#9a5a26;--mid:#3a414b;--track:#343a44;--shadow:none;
|
|
}
|
|
*{box-sizing:border-box}
|
|
[hidden]{display:none!important}
|
|
html{-webkit-text-size-adjust:100%}
|
|
body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.5 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;overflow-x:hidden}
|
|
button,input,select{font:inherit;color:inherit}
|
|
a{color:var(--accent)}
|
|
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
|
|
.top{display:flex;align-items:center;gap:12px;padding:12px 16px 8px;max-width:820px;margin:0 auto}
|
|
.logo{width:36px;height:36px;border-radius:10px;background:var(--accent-soft);display:grid;place-items:center;color:var(--accent);flex:none}
|
|
.app{font-weight:700;font-size:17px;line-height:1.2}
|
|
.pill{display:flex;flex-wrap:wrap;align-items:center;gap:0 6px;font-size:13px;color:var(--muted)}
|
|
.dot{width:8px;height:8px;border-radius:50%;background:var(--ok);flex:none}
|
|
.dot.warn{background:var(--warn)}.dot.bad{background:var(--bad)}
|
|
.tabs{display:flex;gap:4px;max-width:820px;margin:0 auto;padding:0 16px;border-bottom:1px solid var(--line)}
|
|
.tab{border:0;background:none;padding:10px 12px;border-bottom:2px solid transparent;color:var(--muted);cursor:pointer;display:flex;align-items:center;gap:6px;min-height:44px;position:relative}
|
|
.tab[aria-selected="true"]{color:var(--accent);border-bottom-color:var(--accent);font-weight:600}
|
|
.tab svg{width:20px;height:20px}
|
|
.badge{font-size:11px;font-weight:700;background:var(--warn);color:var(--card);border-radius:9px;padding:0 6px;line-height:18px}
|
|
.badge.bad{background:var(--bad)}
|
|
@media (max-width:640px){
|
|
.tabs{position:fixed;bottom:0;left:0;right:0;z-index:20;background:var(--card);border-top:1px solid var(--line);border-bottom:0;padding:0 4px env(safe-area-inset-bottom);justify-content:space-around;gap:0}
|
|
.tab{flex-direction:column;gap:0;font-size:11px;padding:6px 4px;border-bottom:0;border-top:2px solid transparent;flex:1;min-width:0}
|
|
.tab[aria-selected="true"]{border-top-color:var(--accent)}
|
|
.tab .badge{position:absolute;top:2px;left:calc(50% + 6px)}
|
|
.wrap{padding-bottom:96px!important}
|
|
}
|
|
.wrap{max-width:820px;margin:0 auto;padding:16px 16px 40px}
|
|
h2{font-size:15px;margin:0 0 10px;font-weight:650}
|
|
.card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px;margin-bottom:12px;box-shadow:var(--shadow)}
|
|
.muted{color:var(--muted)} .small{font-size:13px} .tiny{font-size:12px}
|
|
.row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
|
.spread{justify-content:space-between}
|
|
.grow{flex:1 1 auto;min-width:0}
|
|
.banner{border-radius:12px;padding:12px 14px;margin-bottom:12px;display:flex;gap:10px;align-items:flex-start;font-weight:600}
|
|
.banner .ic{flex:none;margin-top:2px}
|
|
.b-ok{background:var(--ok-bg);color:var(--ok)} .b-warn{background:var(--warn-bg);color:var(--warn)} .b-bad{background:var(--bad-bg);color:var(--bad)}
|
|
.banner small{display:block;font-weight:400;color:var(--ink);margin-top:2px}
|
|
.tiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}
|
|
@media (max-width:640px){.tiles{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
|
.tile{border:1px solid var(--line);border-radius:10px;padding:10px;min-width:0}
|
|
.tile .lab{font-size:13px;color:var(--muted);display:flex;align-items:center;gap:6px}
|
|
.tile .val{font-size:22px;font-weight:700;font-variant-numeric:tabular-nums;line-height:1.25;margin-top:2px}
|
|
.tile .val small{font-size:13px;font-weight:500;color:var(--muted)}
|
|
.tile .sub{font-size:12px;color:var(--muted)}
|
|
.sw-solar{color:var(--solar)} .sw-house{color:var(--house)} .sw-grid{color:var(--grid)} .sw-batt{color:var(--batt)}
|
|
.brow{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px 10px;padding:10px 0;border-top:1px solid var(--line)}
|
|
.brow:first-child{border-top:0}
|
|
.lvl{height:10px;background:var(--track);border-radius:6px;overflow:hidden;grid-column:1/-1;margin:4px 0 2px}
|
|
.lvl>i{display:block;height:100%;background:var(--batt);border-radius:6px}
|
|
.lvl.none>i{background:var(--faint)}
|
|
/* the plan chart */
|
|
.chart{position:relative;margin:18px 0 4px}
|
|
.bars{display:flex;align-items:flex-end;gap:2px;height:72px}
|
|
.bars i{flex:1;border-radius:3px 3px 0 0;background:var(--mid);min-width:0}
|
|
.bars i.cheap{background:var(--cheap)}
|
|
/* not by colour alone: expensive hours are striped, estimates are faded */
|
|
.bars i.dear{background:repeating-linear-gradient(135deg,var(--dear) 0 4px,color-mix(in srgb,var(--dear) 60%,var(--card)) 4px 7px)}
|
|
.bars i.est{opacity:.55}
|
|
.bars i.none{background:none;border-bottom:2px dotted var(--faint);height:2px!important}
|
|
.lane{position:relative;height:18px;margin-top:4px;background:var(--bg);border-radius:4px}
|
|
.lane b{position:absolute;top:2px;bottom:2px;border-radius:3px;min-width:3px}
|
|
.lane .house{background:repeating-linear-gradient(90deg,var(--mid) 0 6px,transparent 6px 9px);top:6px;bottom:6px}
|
|
.lane .grid{background:var(--accent)}
|
|
.lane .give{background:repeating-linear-gradient(135deg,var(--house) 0 4px,color-mix(in srgb,var(--house) 55%,var(--card)) 4px 7px)}
|
|
.lane .cycle{background:repeating-linear-gradient(90deg,var(--warn) 0 3px,var(--warn-bg) 3px 6px)}
|
|
.lane .peak{background:var(--bad)}
|
|
.lanelab{font-size:12px;color:var(--muted);margin-top:6px}
|
|
.nowline{position:absolute;top:-4px;bottom:0;width:2px;background:var(--ink);opacity:.7;pointer-events:none}
|
|
.nowline span{position:absolute;top:-15px;left:-10px;font-size:10px}
|
|
.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--faint);margin-top:4px}
|
|
.legend{display:flex;flex-wrap:wrap;gap:4px 12px;font-size:12px;color:var(--muted);margin-top:8px}
|
|
.legend span{display:inline-flex;align-items:center;gap:5px}
|
|
.legend i{width:14px;height:10px;border-radius:2px;display:inline-block}
|
|
.legend i.house{background:repeating-linear-gradient(90deg,var(--mid) 0 4px,transparent 4px 6px)}
|
|
ul.plain{list-style:none;margin:8px 0 0;padding:0}
|
|
ul.plain li{padding:6px 0;border-top:1px solid var(--line)}
|
|
ul.plain li:first-child{border-top:0}
|
|
.meter{height:12px;background:var(--track);border-radius:6px;overflow:hidden;margin:8px 0 4px}
|
|
.meter i{display:block;height:100%;background:var(--accent)}
|
|
.meter i.warn{background:var(--warn)} .meter i.bad{background:var(--bad)}
|
|
.alert{display:flex;gap:10px;padding:10px 0;border-top:1px solid var(--line)}
|
|
.alert:first-child{border-top:0}
|
|
.sev{flex:none;width:24px;height:24px;border-radius:50%;display:grid;place-items:center;font-size:13px;font-weight:800}
|
|
.sev.warning{background:var(--warn-bg);color:var(--warn)} .sev.alarm{background:var(--bad-bg);color:var(--bad)} .sev.info{background:var(--info-bg);color:var(--info)} .sev.ok{background:var(--ok-bg);color:var(--ok)}
|
|
.alert .t{font-weight:600}
|
|
.btn{border:1px solid var(--line);background:var(--card);padding:9px 14px;border-radius:10px;cursor:pointer;min-height:44px}
|
|
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent);font-weight:600}
|
|
.btn.danger{color:var(--bad);border-color:var(--bad)}
|
|
.btn:disabled{opacity:.5;cursor:not-allowed}
|
|
a.btn{display:inline-flex;align-items:center;text-decoration:none;color:var(--ink)}
|
|
details.more{margin-top:12px;border-top:1px solid var(--line)}
|
|
details.more>summary{cursor:pointer;padding:10px 0;color:var(--accent);font-size:14px;min-height:44px;display:flex;align-items:center}
|
|
table.rows{width:100%;border-collapse:collapse;font-size:13px}
|
|
table.rows td{padding:6px 0;border-bottom:1px solid var(--line);vertical-align:top}
|
|
table.rows td:last-child{text-align:right;font-variant-numeric:tabular-nums;font-weight:600;padding-left:12px;overflow-wrap:anywhere}
|
|
table.rows tr:last-child td{border-bottom:0}
|
|
ul.checks{margin:6px 0 0;padding-left:0;list-style:none;font-size:13px}
|
|
ul.checks li{margin:3px 0}
|
|
.c-ok{color:var(--ok)} .c-warn{color:var(--warn)} .c-bad{color:var(--bad)}
|
|
.kv{display:grid;grid-template-columns:auto minmax(0,1fr);gap:4px 12px;font-size:14px;margin-top:10px}
|
|
.kv dt{color:var(--muted)} .kv dd{margin:0}
|
|
#toast{position:fixed;left:50%;transform:translateX(-50%);bottom:24px;z-index:30;background:var(--ink);color:var(--bg);padding:10px 14px;border-radius:10px;font-size:14px;max-width:calc(100% - 32px);opacity:0;pointer-events:none;transition:opacity .2s}
|
|
#toast.show{opacity:1}
|
|
@media (max-width:640px){#toast{bottom:84px}}
|
|
dialog{border:1px solid var(--line);border-radius:14px;background:var(--card);color:var(--ink);max-width:min(420px,calc(100% - 32px));padding:18px}
|
|
dialog::backdrop{background:rgba(0,0,0,.45)}
|
|
@media (prefers-reduced-motion:reduce){*{transition:none!important}}
|
|
.tip{position:fixed;z-index:40;background:var(--ink);color:var(--bg);font-size:12px;line-height:1.35;padding:6px 8px;border-radius:8px;pointer-events:none;max-width:240px;opacity:0;transition:opacity .1s;box-shadow:0 2px 8px rgba(0,0,0,.25);left:0;top:0}
|
|
.tip.show{opacity:1}
|
|
.tip b{display:block;font-weight:700}
|
|
.tip span{display:block}
|
|
.chart [data-tip]{cursor:default}
|
|
.chart [data-tip]:hover,.chart [data-tip]:focus,.chart .hot{outline:2px solid var(--ink);outline-offset:-1px}
|
|
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
|
/* UX-13 P2: Settings, the Installer view (mockup version 2) */
|
|
.btn.link{border:0;background:none;color:var(--accent);padding:6px 0;min-height:0}
|
|
.search{width:100%;border:1px solid var(--line);background:var(--card);border-radius:10px;padding:10px 12px;min-height:44px;margin-bottom:10px}
|
|
.key{font:11px/1.3 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--faint);overflow-wrap:anywhere}
|
|
.chip{display:inline-flex;align-items:center;gap:4px;font-size:12px;border-radius:999px;padding:1px 8px;background:var(--info-bg);color:var(--muted);white-space:nowrap}
|
|
.chip.now{background:var(--ok-bg);color:var(--ok)} .chip.rs{background:var(--warn-bg);color:var(--warn)}
|
|
.chip.bad{background:var(--bad-bg);color:var(--bad)} .chip.changed{background:var(--accent-soft);color:var(--accent)}
|
|
details.grp{background:var(--card);border:1px solid var(--line);border-radius:12px;margin-bottom:10px;box-shadow:var(--shadow)}
|
|
details.grp>summary{list-style:none;cursor:pointer;padding:14px;display:flex;gap:10px;align-items:center;min-height:56px;flex-wrap:wrap}
|
|
details.grp>summary::-webkit-details-marker{display:none}
|
|
details.grp>summary .grow{flex:1 1 200px}
|
|
details.grp>summary .gdesc{display:block}
|
|
details.grp>summary .chev{transition:transform .15s;flex:none;font-size:22px;line-height:1;width:18px;text-align:center;color:var(--muted)}
|
|
details.grp[open]>summary .chev{transform:rotate(90deg)}
|
|
.gchips,.gbadge{display:inline-flex;flex-wrap:wrap;gap:4px}
|
|
details.grp .body{padding:0 14px 8px}
|
|
details.sub{border-top:1px solid var(--line);margin-top:4px}
|
|
details.sub>summary{cursor:pointer;padding:10px 0;color:var(--accent);font-size:14px;min-height:44px;display:flex;align-items:center}
|
|
.subhead{font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--faint);margin:14px 0 0;font-weight:700}
|
|
.f{padding:12px 0;border-top:1px solid var(--line);min-width:0}
|
|
.part>.f:first-child,.subhead+.f{border-top:0}
|
|
.f .lbl{font-weight:600;font-size:14px}
|
|
.f .help{font-size:13px;color:var(--muted);margin-top:4px}
|
|
.f .ctl{margin-top:8px;min-width:0}
|
|
.f.inline{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:4px 12px;align-items:start}
|
|
.f.inline .ctl{margin-top:0;grid-row:1;grid-column:2}
|
|
.f.inline .help,.f.inline .msgs{grid-column:1/-1}
|
|
.f.changed>.head .lbl::after{content:" •";color:var(--accent)}
|
|
.msg{font-size:13px;margin-top:6px;display:flex;gap:6px;align-items:flex-start;border-radius:8px;padding:6px 8px;overflow-wrap:anywhere}
|
|
.msg .mi{font-weight:800;flex:none}
|
|
.msg.bad{background:var(--bad-bg);color:var(--bad)} .msg.warn{background:var(--warn-bg);color:var(--warn)} .msg.info{background:var(--info-bg);color:var(--info)}
|
|
.f.has-bad .ctl select,.f.has-bad .ctl input,.f.has-bad .ent-btn{border-color:var(--bad)}
|
|
.switch{position:relative;width:52px;height:44px;border:0;background:none;cursor:pointer;flex:none;padding:0}
|
|
.switch::before{content:"";position:absolute;left:2px;top:8px;width:48px;height:28px;border-radius:14px;background:var(--track);transition:background .15s}
|
|
.switch::after{content:"";position:absolute;top:11px;left:5px;width:22px;height:22px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.3);transition:left .15s}
|
|
.switch[aria-checked="true"]::before{background:var(--accent)}
|
|
.switch[aria-checked="true"]::after{left:25px}
|
|
.swrap{display:inline-flex;align-items:center;gap:8px;min-height:44px}
|
|
.swtext{font-size:13px;color:var(--muted)}
|
|
select.sel,input.txt,input.num{border:1px solid var(--line);background:var(--card);border-radius:10px;padding:9px 10px;min-height:44px;max-width:100%}
|
|
select.sel{width:100%}
|
|
.row>select.sel{width:auto;flex:1 1 200px}
|
|
input.num{width:110px;text-align:right;font-variant-numeric:tabular-nums}
|
|
input.txt{width:100%}
|
|
input:disabled{opacity:.6}
|
|
.seg{display:inline-flex;border:1px solid var(--line);border-radius:10px;overflow:hidden;max-width:100%;flex-wrap:wrap}
|
|
.seg button{border:0;background:var(--card);padding:8px 12px;min-height:44px;cursor:pointer;border-left:1px solid var(--line)}
|
|
.seg button:first-child{border-left:0}
|
|
.seg button[aria-checked="true"]{background:var(--accent-soft);color:var(--accent);font-weight:600}
|
|
.choice{display:flex;gap:10px;align-items:flex-start;border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin-top:8px;cursor:pointer;width:100%;text-align:left;background:var(--card);min-height:44px}
|
|
.choice[aria-checked="true"]{border-color:var(--accent);background:var(--accent-soft)}
|
|
.choice .rad{flex:none;width:18px;height:18px;border-radius:50%;border:2px solid var(--faint);margin-top:2px}
|
|
.choice[aria-checked="true"] .rad{border:5px solid var(--accent)}
|
|
.slider{display:flex;align-items:center;gap:10px;width:100%}
|
|
.slider input[type=range]{flex:1;min-width:0;accent-color:var(--accent);height:28px}
|
|
.unit{color:var(--muted);font-size:13px;white-space:nowrap}
|
|
.ent-btn{width:100%;text-align:left;border:1px solid var(--line);background:var(--card);border-radius:10px;padding:8px 10px;min-height:44px;cursor:pointer;display:flex;gap:8px;align-items:center}
|
|
.ent-btn .grow{min-width:0}
|
|
.ent-btn .grow>div{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
.ent-pop{border:1px solid var(--line);border-radius:10px;margin-top:6px;background:var(--card);overflow:hidden}
|
|
.ent-pop input[type=search]{border:0;border-bottom:1px solid var(--line);width:100%;padding:10px;background:var(--card);min-height:44px}
|
|
.ent-note{padding:6px 10px}
|
|
.ent-list{max-height:240px;overflow:auto}
|
|
.ent-opt{display:flex;gap:8px;align-items:center;width:100%;border:0;border-bottom:1px solid var(--line);background:none;text-align:left;padding:8px 10px;cursor:pointer;min-height:44px}
|
|
.ent-opt:hover,.ent-opt:focus{background:var(--accent-soft)}
|
|
.ent-opt .grow{min-width:0}
|
|
.ent-opt .grow>div{overflow-wrap:anywhere}
|
|
.ent-opt .v{font-variant-numeric:tabular-nums;font-size:13px;color:var(--muted);white-space:nowrap}
|
|
.ent-all{display:flex;gap:6px;padding:6px 10px;align-items:center;min-height:44px}
|
|
.elist .erow{padding:6px 0;border-bottom:1px dashed var(--line)}
|
|
.elist>.btn{margin-top:8px}
|
|
.inv{border:1px solid var(--line);border-radius:10px;padding:0 12px;margin:10px 0}
|
|
.inv>.hd{display:flex;align-items:center;gap:8px;padding:10px 0;border-bottom:1px solid var(--line);font-weight:600;flex-wrap:wrap}
|
|
.inv>.f:nth-child(2){border-top:0}
|
|
.invs>.btn{margin:4px 0 8px}
|
|
#savebar{position:fixed;left:0;right:0;bottom:0;z-index:15}
|
|
#savebar .in{max-width:820px;margin:0 auto 10px;padding:10px 12px;background:var(--ink);color:var(--bg);border-radius:12px;display:flex;flex-wrap:wrap;gap:8px;align-items:center;box-shadow:0 4px 16px rgba(0,0,0,.25);width:calc(100% - 32px)}
|
|
#savebar .btn{min-height:44px;padding:6px 12px;background:transparent;color:var(--bg);border-color:rgba(127,127,127,.6)}
|
|
#savebar .btn.primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
|
|
@media (max-width:640px){#savebar{bottom:62px}}
|
|
#s-set{padding-bottom:80px}
|