diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 043e929..129f643 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -105,6 +105,40 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba .kpi div{background:#0f151d;border:1px solid var(--line);border-radius:12px;padding:10px} .kpi .num{font-size:20px;font-weight:800;margin-top:4px} @media(max-width:760px){.span4,.span6{grid-column:span 12}.big{font-size:36px}.wrap{padding:12px 12px 82px}.kpi{grid-template-columns:1fr}} + +/* UI polish v2 */ +body{background:radial-gradient(circle at top,#111927 0,#050a0d 48%,#030608 100%)} +.wrap{max-width:1120px} +.card{background:linear-gradient(180deg,#121a25,#0d141d);border-color:#26364b;box-shadow:0 12px 32px rgba(0,0,0,.22)} +.card:hover{border-color:#36506d} +.configGroupHeader{border-bottom:1px solid var(--line);padding-bottom:12px;margin-bottom:16px} +.configGroupTitle,.label{font-size:20px;font-weight:950;color:#f4f8ff;letter-spacing:-.02em} +.configGroupHelp,.sub{font-size:14px;color:#aebbd0;line-height:1.45;max-width:760px} +.row{padding:10px 0;border-bottom:1px solid rgba(255,255,255,.06)} +.row:last-child{border-bottom:0} +input,select,textarea{min-height:42px;margin:3px 0 8px;background:#08111a;border-color:#26384d;color:#f6f8fc;font-size:15px} +input:focus,select:focus,textarea:focus{outline:2px solid #315f9f;border-color:#5d8fd2} +button{min-height:42px;padding:11px 14px;border-radius:12px;font-weight:900;letter-spacing:.01em;border:1px solid #31445d;background:#1d324f;color:#edf5ff} +button:hover{filter:brightness(1.08)} +button:active{transform:translateY(1px)} +button.on,button.save,button[onclick^="save"]{background:linear-gradient(180deg,#1f6b38,#134922);border-color:#2b8a4b;color:#ecfff2} +button.off,button.danger,button[onclick^="reset"],button[onclick^="factory"],button[onclick^="setBmsEnabled(false)"]{background:linear-gradient(180deg,#743039,#4d1d25);border-color:#9b4050;color:#fff1f3} +button.secondary,button[onclick^="toggle"],button[onclick^="connect"],button[onclick^="scan"]{background:linear-gradient(180deg,#28466d,#1b304e);border-color:#3f6598;color:#edf5ff} +.inlineActions{gap:10px} +.inlineActions button{width:100%} +.subtabs{background:rgba(8,13,20,.72);border:1px solid rgba(255,255,255,.06);border-radius:18px;padding:8px;margin-bottom:14px} +.subtabbtn{min-height:38px;padding:9px 14px} +.subtabbtn.active{background:linear-gradient(180deg,#233b5f,#172942);border-color:#4c78b3;color:#fff} +.configWarning{background:#21170c;border-color:#665026;color:#ffe7ad} +.inputHelp{color:#aebbd0;margin-bottom:8px} +.list{gap:8px} +.pill.good{background:#082416;color:#67f0a0;border-color:#1d7747} +.pill.bad{background:#321018;color:#ff9baa;border-color:#8f3446} +@media(min-width:900px){ + #config-wifi .card.span12{padding:22px} + #config-wifi .list{gap:10px} +} + @@ -247,10 +281,10 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba
Password must be 8-63 characters. The current password is never displayed.
- - + +
- +
Saving AP settings restarts the access point. Your phone may disconnect and need to reconnect to the new SSID/password. Dashboard auto-migration is planned but not implemented yet.
@@ -272,8 +306,8 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba - - + + @@ -289,7 +323,7 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba
- + @@ -304,7 +338,7 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba
- +
@@ -319,7 +353,7 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba
- +
@@ -339,7 +373,7 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba
- +
@@ -361,7 +395,7 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba
- +
diff --git a/tests/test_http_api_contract.py b/tests/test_http_api_contract.py index 14c5b01..67aeb2d 100644 --- a/tests/test_http_api_contract.py +++ b/tests/test_http_api_contract.py @@ -472,7 +472,7 @@ def test_embedded_webui_preserves_config_input_while_polling(): assert 'document.addEventListener("input"' in source assert 'setConfigValue("apSsidInput"' in source assert "deviceNameInput" in source - assert 'setConfigValue("bmsNameInput"' in source + assert "bmsNameInput" in source assert 'setConfigValue("tempEnabledCount"' in source @@ -486,3 +486,17 @@ def test_ap_config_can_be_reset_to_defaults(): assert 'api("/config/ap/reset")' in source assert "Reset AP to Default" in source assert "regenerated password" in source + + +def test_embedded_webui_has_consistent_button_intent_styles(): + source = firmware_source() + + assert "/* UI polish v2 */" in source + assert "button.save" in source + assert "button.danger" in source + assert "button.secondary" in source + assert "configGroupTitle" in source + assert "linear-gradient" in source + assert 'class="save" onclick="saveApConfig()"' in source + assert 'class="danger" onclick="resetApConfig()"' in source + assert 'class="save" onclick="saveWifi()"' in source