Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

web: improve style for mobile view #393

Merged
merged 11 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions resources/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Resources

Any files used along with application source code, but not to be mixed with C/C++ code.
This can include CSS, JS, favicon, etc.
233 changes: 233 additions & 0 deletions resources/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/*
escaped by https://www.cescaper.com/
source: https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css
*/

/*
* Usage:
* Compact / minify this code with any tool
* Copy one-line string to src/WebCfgServerConstants.h as stylecss
* TODO: automate this process upon building :)
*/

:root {
--nc-font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
--nc-font-mono: Consolas, monaco, 'Ubuntu Mono', 'Liberation Mono', 'Courier New', Courier, monospace;
--nc-tx-1: #000000;
--nc-tx-2: #1A1A1A;
--nc-bg-1: #FFFFFF;
--nc-bg-2: #F6F8FA;
--nc-bg-3: #E5E7EB;
--nc-lk-1: #0070F3;
--nc-lk-2: #0366D6;
--nc-lk-tx: #FFFFFF;
--nc-ac-1: #79FFE1;
--nc-ac-tx: #0C4047
}

@media (prefers-color-scheme:dark) {
:root {
--nc-tx-1: #ffffff;
--nc-tx-2: #eeeeee;
--nc-bg-1: #000000;
--nc-bg-2: #111111;
--nc-bg-3: #222222;
--nc-lk-1: #3291FF;
--nc-lk-2: #0070F3;
--nc-lk-tx: #FFFFFF;
--nc-ac-1: #7928CA;
--nc-ac-tx: #FFFFFF
}
}

* {
margin: 0;
padding: 0
}

img,
input,
option,
p,
table,
textarea,
ul {
margin-bottom: 1rem
}

button,
html,
input,
select {
font-family: var(--nc-font-sans)
}

body {
margin: 0 auto;
max-width: 750px;
padding: 2rem;
border-radius: 6px;
overflow-x: hidden;
word-break: normal;
overflow-wrap: anywhere;
background: var(--nc-bg-1);
color: var(--nc-tx-2);
font-size: 1.03rem;
line-height: 1.5
}

::selection {
background: var(--nc-ac-1);
color: var(--nc-ac-tx)
}

h1, h2, h3, h4, h5, h6 {
line-height: 1;
color: var(--nc-tx-1);
padding-top: .875rem
}

h1, h2, h3 {
color: var(--nc-tx-1);
padding-bottom: 2px;
margin-bottom: 8px;
border-bottom: 1px solid var(--nc-bg-2)
}

h4, h5, h6 {
margin-bottom: .3rem
}

h1 { font-size: 2.25rem }
h2 { font-size: 1.85rem }
h3 { font-size: 1.55rem }
h4 { font-size: 1.25rem }
h5 { font-size: 1rem }
h6 { font-size: .875rem }
a { color: var(--nc-lk-1) }
a:hover { color: var(--nc-lk-2) }
abbr { cursor: help }
abbr:hover { cursor: help }

a button,
button,
input[type=button],
input[type=reset],
input[type=submit] {
font-size: 1rem;
display: inline-block;
padding: 6px 12px;
text-align: center;
text-decoration: none;
white-space: nowrap;
background: var(--nc-lk-1);
color: var(--nc-lk-tx);
border: 0;
border-radius: 4px;
box-sizing: border-box;
cursor: pointer;
color: var(--nc-lk-tx)
}

a button[disabled],
button[disabled],
input[type=button][disabled],
input[type=reset][disabled],
input[type=submit][disabled] {
cursor: default;
opacity: .5;
cursor: not-allowed
}

.button:focus,
.button:hover,
button:focus,
button:hover,
input[type=button]:focus,
input[type=button]:hover,
input[type=reset]:focus,
input[type=reset]:hover,
input[type=submit]:focus,
input[type=submit]:hover {
background: var(--nc-lk-2)
}

table {
border-collapse: collapse;
width: 100%
}

td, th {
border: 1px solid var(--nc-bg-3);
text-align: left;
padding: .5rem
}

th { background: var(--nc-bg-2) }
tr:nth-child(even) { background: var(--nc-bg-2) }

textarea { max-width: 100% }

input, select, textarea {
padding: 6px 12px;
margin-bottom: .5rem;
background: var(--nc-bg-2);
color: var(--nc-tx-2);
border: 1px solid var(--nc-bg-3);
border-radius: 4px;
box-shadow: none;
box-sizing: border-box
}

img { max-width: 100% }

td>input {
margin-top: 0px;
margin-bottom: 0px
}

td>textarea {
margin-top: 0px;
margin-bottom: 0px
}

td>select {
margin-top: 0px;
margin-bottom: 0px
}

#tblnav td, th {
border: 0;
border-bottom: 1px solid;
}

.tdbtn {
text-align: center;
vertical-align: middle;
}

.warning {
color: #f00;
}

@media only screen and (max-width: 600px) {
.adapt td { display: block; }

.adapt input[type=text],
.adapt input[type=password],
.adapt input[type=submit],
.adapt textarea,
.adapt select { width: 100%; }

.adapt td:has(input[type=checkbox]) {
text-align: center;
}

.adapt input[type=checkbox] {
width: 1.5em;
height: 1.5em;
}

.adapt table td:first-child { border-bottom: 0; }
.adapt table td:last-child { border-top: 0; }
}
35 changes: 14 additions & 21 deletions src/WebCfgServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1174,7 +1174,7 @@ void WebCfgServer::buildCredHtml(String &response)
{
buildHtmlHeader(response);

response.concat("<form method=\"post\" action=\"savecfg\">");
response.concat("<form class=\"adapt\" method=\"post\" action=\"savecfg\">");
response.concat("<h3>Credentials</h3>");
response.concat("<table>");
printInputField(response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, false, true);
Expand All @@ -1186,7 +1186,7 @@ void WebCfgServer::buildCredHtml(String &response)

if(_nuki != nullptr)
{
response.concat("<br><br><form method=\"post\" action=\"savecfg\">");
response.concat("<br><br><form class=\"adapt\" method=\"post\" action=\"savecfg\">");
response.concat("<h3>Nuki Lock PIN</h3>");
response.concat("<table>");
printInputField(response, "NUKIPIN", "PIN Code (# to clear)", "*", 20, true);
Expand All @@ -1197,7 +1197,7 @@ void WebCfgServer::buildCredHtml(String &response)

if(_nukiOpener != nullptr)
{
response.concat("<br><br><form method=\"post\" action=\"savecfg\">");
response.concat("<br><br><form class=\"adapt\" method=\"post\" action=\"savecfg\">");
response.concat("<h3>Nuki Opener PIN</h3>");
response.concat("<table>");
printInputField(response, "NUKIOPPIN", "PIN Code (# to clear)", "*", 20, true);
Expand All @@ -1210,7 +1210,7 @@ void WebCfgServer::buildCredHtml(String &response)
if(_nuki != nullptr)
{
response.concat("<br><br><h3>Unpair Nuki Lock</h3>");
response.concat("<form method=\"post\" action=\"/unpairlock\">");
response.concat("<form class=\"adapt\" method=\"post\" action=\"/unpairlock\">");
response.concat("<table>");
String message = "Type ";
message.concat(_confirmCode);
Expand All @@ -1223,7 +1223,7 @@ void WebCfgServer::buildCredHtml(String &response)
if(_nukiOpener != nullptr)
{
response.concat("<br><br><h3>Unpair Nuki Opener</h3>");
response.concat("<form method=\"post\" action=\"/unpairopener\">");
response.concat("<form class=\"adapt\" method=\"post\" action=\"/unpairopener\">");
response.concat("<table>");
String message = "Type ";
message.concat(_confirmCode);
Expand All @@ -1234,8 +1234,8 @@ void WebCfgServer::buildCredHtml(String &response)
}

response.concat("<br><br><h3>Factory reset Nuki Hub</h3>");
response.concat("<h4 style=\"color: #ff0000\">This will reset all settings to default and unpair Nuki Lock and/or Opener. Optionally will also reset WiFi settings and reopen WiFi manager portal.</h4>");
response.concat("<form method=\"post\" action=\"/factoryreset\">");
response.concat("<h4 class=\"warning\">This will reset all settings to default and unpair Nuki Lock and/or Opener. Optionally will also reset WiFi settings and reopen WiFi manager portal.</h4>");
response.concat("<form class=\"adapt\" method=\"post\" action=\"/factoryreset\">");
response.concat("<table>");
String message = "Type ";
message.concat(_confirmCode);
Expand Down Expand Up @@ -1301,7 +1301,7 @@ void WebCfgServer::buildOtaCompletedHtml(String &response)
void WebCfgServer::buildMqttConfigHtml(String &response)
{
buildHtmlHeader(response);
response.concat("<form method=\"post\" action=\"savecfg\">");
response.concat("<form class=\"adapt\" method=\"post\" action=\"savecfg\">");
response.concat("<h3>Basic MQTT and Network Configuration</h3>");
response.concat("<table>");
printInputField(response, "HOSTNAME", "Host name", _preferences->getString(preference_hostname).c_str(), 100);
Expand Down Expand Up @@ -1348,9 +1348,9 @@ void WebCfgServer::buildMqttConfigHtml(String &response)
void WebCfgServer::buildAdvancedConfigHtml(String &response)
{
buildHtmlHeader(response);
response.concat("<form method=\"post\" action=\"savecfg\">");
response.concat("<form class=\"adapt\" method=\"post\" action=\"savecfg\">");
response.concat("<h3>Advanced Configuration</h3>");
response.concat("<h4 style=\"color: #ff0000\">Warning: Changing these settings can lead to bootloops that might require you to erase the ESP32 and reflash nukihub using USB/serial</h4>");
response.concat("<h4 class=\"warning\">Warning: Changing these settings can lead to bootloops that might require you to erase the ESP32 and reflash nukihub using USB/serial</h4>");
response.concat("<table>");
response.concat("<tr><td>Current bootloop prevention state</td><td>");
response.concat(_preferences->getBool(preference_enable_bootloop_reset, false) ? "Enabled" : "Disabled");
Expand Down Expand Up @@ -1466,7 +1466,7 @@ void WebCfgServer::buildAccLvlHtml(String &response)
if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad()))
{
printCheckBox(response, "KPPUB", "Publish keypad entries information", _preferences->getBool(preference_keypad_info_enabled), "");
printCheckBox(response, "KPCODE", "Also publish keypad codes (<span style=\"color: #ff0000\">Disadvised for security reasons</span>)", _preferences->getBool(preference_keypad_publish_code, false), "");
printCheckBox(response, "KPCODE", "Also publish keypad codes (<span class=\"warning\">Disadvised for security reasons</span>)", _preferences->getBool(preference_keypad_publish_code, false), "");
printCheckBox(response, "KPENA", "Add, modify and delete keypad codes", _preferences->getBool(preference_keypad_control_enabled), "");
}
printCheckBox(response, "TCPUB", "Publish time control entries information", _preferences->getBool(preference_timecontrol_info_enabled), "");
Expand Down Expand Up @@ -1623,7 +1623,7 @@ void WebCfgServer::buildNukiConfigHtml(String &response)
{
buildHtmlHeader(response);

response.concat("<form method=\"post\" action=\"savecfg\">");
response.concat("<form class=\"adapt\" method=\"post\" action=\"savecfg\">");
response.concat("<h3>Basic Nuki Configuration</h3>");
response.concat("<table>");
printCheckBox(response, "LOCKENA", "Nuki Smartlock enabled", _preferences->getBool(preference_lock_enabled), "");
Expand Down Expand Up @@ -1688,17 +1688,10 @@ void WebCfgServer::buildGpioConfigHtml(String &response)
void WebCfgServer::buildConfirmHtml(String &response, const String &message, uint32_t redirectDelay)
{
String delay(redirectDelay);
String header = "<meta http-equiv=\"Refresh\" content=\"" + delay + "; url=/\" />";

response.concat("<html>\n");
response.concat("<head>\n");
response.concat("<title>Nuki Hub</title>\n");
response.concat("<meta http-equiv=\"Refresh\" content=\"");
response.concat(redirectDelay);
response.concat("; url=/\" />");
response.concat("\n</head>\n");
response.concat("<body>\n");
buildHtmlHeader(response, header);
response.concat(message);

response.concat("</body></html>");
}

Expand Down
2 changes: 1 addition & 1 deletion src/WebCfgServerConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// escaped by https://www.cescaper.com/
// source: https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css

const char stylecss[] = ":root{--nc-font-sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';--nc-font-mono:Consolas,monaco,'Ubuntu Mono','Liberation Mono','Courier New',Courier,monospace;--nc-tx-1:#000000;--nc-tx-2:#1A1A1A;--nc-bg-1:#FFFFFF;--nc-bg-2:#F6F8FA;--nc-bg-3:#E5E7EB;--nc-lk-1:#0070F3;--nc-lk-2:#0366D6;--nc-lk-tx:#FFFFFF;--nc-ac-1:#79FFE1;--nc-ac-tx:#0C4047}@media (prefers-color-scheme:dark){:root{--nc-tx-1:#ffffff;--nc-tx-2:#eeeeee;--nc-bg-1:#000000;--nc-bg-2:#111111;--nc-bg-3:#222222;--nc-lk-1:#3291FF;--nc-lk-2:#0070F3;--nc-lk-tx:#FFFFFF;--nc-ac-1:#7928CA;--nc-ac-tx:#FFFFFF}}*{margin:0;padding:0}address,area,article,aside,audio,blockquote,datalist,details,dl,fieldset,figure,iframe,img,input,meter,nav,ol,optgroup,option,output,p,pre,progress,ruby,section,table,textarea,ul,video{margin-bottom:1rem}button,html,input,select{font-family:var(--nc-font-sans)}body{margin:0 auto;max-width:750px;padding:2rem;border-radius:6px;overflow-x:hidden;word-break:normal;overflow-wrap:anywhere;background:var(--nc-bg-1);color:var(--nc-tx-2);font-size:1.03rem;line-height:1.5}::selection{background:var(--nc-ac-1);color:var(--nc-ac-tx)}h1,h2,h3,h4,h5,h6{line-height:1;color:var(--nc-tx-1);padding-top:.875rem}h1,h2,h3{color:var(--nc-tx-1);padding-bottom:2px;margin-bottom:8px;border-bottom:1px solid var(--nc-bg-2)}h4,h5,h6{margin-bottom:.3rem}h1{font-size:2.25rem}h2{font-size:1.85rem}h3{font-size:1.55rem}h4{font-size:1.25rem}h5{font-size:1rem}h6{font-size:.875rem}a{color:var(--nc-lk-1)}a:hover{color:var(--nc-lk-2)}abbr:hover{cursor:help}blockquote{padding:1.5rem;background:var(--nc-bg-2);border-left:5px solid var(--nc-bg-3)}abbr{cursor:help}blockquote :last-child{padding-bottom:0;margin-bottom:0}header{background:var(--nc-bg-2);border-bottom:1px solid var(--nc-bg-3);padding:2rem 1.5rem;margin:-2rem calc(0px - (50vw - 50%)) 2rem;padding-left:calc(50vw - 50%);padding-right:calc(50vw - 50%)}header h1,header h2,header h3{padding-bottom:0;border-bottom:0}header>:first-child{margin-top:0;padding-top:0}header>:last-child{margin-bottom:0}a button,button,input[type=button],input[type=reset],input[type=submit]{font-size:1rem;display:inline-block;padding:6px 12px;text-align:center;text-decoration:none;white-space:nowrap;background:var(--nc-lk-1);color:var(--nc-lk-tx);border:0;border-radius:4px;box-sizing:border-box;cursor:pointer;color:var(--nc-lk-tx)}a button[disabled],button[disabled],input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled]{cursor:default;opacity:.5;cursor:not-allowed}.button:focus,.button:hover,button:focus,button:hover,input[type=button]:focus,input[type=button]:hover,input[type=reset]:focus,input[type=reset]:hover,input[type=submit]:focus,input[type=submit]:hover{background:var(--nc-lk-2)}code,kbd,pre,samp{font-family:var(--nc-font-mono)}code,kbd,pre,samp{background:var(--nc-bg-2);border:1px solid var(--nc-bg-3);border-radius:4px;padding:3px 6px;font-size:.9rem}kbd{border-bottom:3px solid var(--nc-bg-3)}pre{padding:1rem 1.4rem;max-width:100%;overflow:auto}pre code{background:inherit;font-size:inherit;color:inherit;border:0;padding:0;margin:0}code pre{display:inline;background:inherit;font-size:inherit;color:inherit;border:0;padding:0;margin:0}details{padding:.6rem 1rem;background:var(--nc-bg-2);border:1px solid var(--nc-bg-3);border-radius:4px}summary{cursor:pointer;font-weight:700}details[open]{padding-bottom:.75rem}details[open] summary{margin-bottom:6px}details[open]>:last-child{margin-bottom:0}dt{font-weight:700}dd::before{content:'→ '}hr{border:0;border-bottom:1px solid var(--nc-bg-3);margin:1rem auto}fieldset{margin-top:1rem;padding:2rem;border:1px solid var(--nc-bg-3);border-radius:4px}legend{padding:0.5rem}table{border-collapse:collapse;width:100%}td,th{border:1px solid var(--nc-bg-3);text-align:left;padding:.5rem}th{background:var(--nc-bg-2)}tr:nth-child(even){background:var(--nc-bg-2)}table caption{font-weight:700;margin-bottom:.5rem}textarea{max-width:100%}ol,ul{padding-left:2rem}li{margin-top:.4rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}mark{padding:3px 6px;background:var(--nc-ac-1);color:var(--nc-ac-tx)}input,select,textarea{padding:6px 12px;margin-bottom:.5rem;background:var(--nc-bg-2);color:var(--nc-tx-2);border:1px solid var(--nc-bg-3);border-radius:4px;box-shadow:none;box-sizing:border-box}img{max-width:100%}td>input{margin-top:0px;margin-bottom:0px}td>textarea{margin-top:0px;margin-bottom:0px}td>select{margin-top:0px;margin-bottom:0px}#tblnav td,th{border:0;border-bottom:1px solid;}.tdbtn{text-align:center;vertical-align: middle;}";
const char stylecss[] = ":root{--nc-font-sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';--nc-font-mono:Consolas,monaco,'Ubuntu Mono','Liberation Mono','Courier New',Courier,monospace;--nc-tx-1:#000;--nc-tx-2:#1a1a1a;--nc-bg-1:#fff;--nc-bg-2:#f6f8fa;--nc-bg-3:#e5e7eb;--nc-lk-1:#0070f3;--nc-lk-2:#0366d6;--nc-lk-tx:#fff;--nc-ac-1:#79ffe1;--nc-ac-tx:#0c4047}@media(prefers-color-scheme:dark){:root{--nc-tx-1:#fff;--nc-tx-2:#eee;--nc-bg-1:#000;--nc-bg-2:#111;--nc-bg-3:#222;--nc-lk-1:#3291ff;--nc-lk-2:#0070f3;--nc-lk-tx:#fff;--nc-ac-1:#7928ca;--nc-ac-tx:#fff}}*{margin:0;padding:0}img,input,option,p,table,textarea,ul{margin-bottom:1rem}button,html,input,select{font-family:var(--nc-font-sans)}body{margin:0 auto;max-width:750px;padding:2rem;border-radius:6px;overflow-x:hidden;word-break:normal;overflow-wrap:anywhere;background:var(--nc-bg-1);color:var(--nc-tx-2);font-size:1.03rem;line-height:1.5}::selection{background:var(--nc-ac-1);color:var(--nc-ac-tx)}h1,h2,h3,h4,h5,h6{line-height:1;color:var(--nc-tx-1);padding-top:.875rem}h1,h2,h3{color:var(--nc-tx-1);padding-bottom:2px;margin-bottom:8px;border-bottom:1px solid var(--nc-bg-2)}h4,h5,h6{margin-bottom:.3rem}h1{font-size:2.25rem}h2{font-size:1.85rem}h3{font-size:1.55rem}h4{font-size:1.25rem}h5{font-size:1rem}h6{font-size:.875rem}a{color:var(--nc-lk-1)}a:hover{color:var(--nc-lk-2)}abbr{cursor:help}abbr:hover{cursor:help}a button,button,input[type=button],input[type=reset],input[type=submit]{font-size:1rem;display:inline-block;padding:6px 12px;text-align:center;text-decoration:none;white-space:nowrap;background:var(--nc-lk-1);color:var(--nc-lk-tx);border:0;border-radius:4px;box-sizing:border-box;cursor:pointer;color:var(--nc-lk-tx)}a button[disabled],button[disabled],input[type=button][disabled],input[type=reset][disabled],input[type=submit][disabled]{cursor:default;opacity:.5;cursor:not-allowed}.button:focus,.button:hover,button:focus,button:hover,input[type=button]:focus,input[type=button]:hover,input[type=reset]:focus,input[type=reset]:hover,input[type=submit]:focus,input[type=submit]:hover{background:var(--nc-lk-2)}table{border-collapse:collapse;width:100%}td,th{border:1px solid var(--nc-bg-3);text-align:left;padding:.5rem}th{background:var(--nc-bg-2)}tr:nth-child(even){background:var(--nc-bg-2)}textarea{max-width:100%}input,select,textarea{padding:6px 12px;margin-bottom:.5rem;background:var(--nc-bg-2);color:var(--nc-tx-2);border:1px solid var(--nc-bg-3);border-radius:4px;box-shadow:none;box-sizing:border-box}img{max-width:100%}td>input{margin-top:0;margin-bottom:0}td>textarea{margin-top:0;margin-bottom:0}td>select{margin-top:0;margin-bottom:0}#tblnav td,th{border:0;border-bottom:1px solid}.tdbtn{text-align:center;vertical-align:middle}.warning{color:red}@media only screen and (max-width:600px){.adapt td{display:block}.adapt input[type=text],.adapt input[type=password],.adapt input[type=submit],.adapt textarea,.adapt select{width:100%}.adapt td:has(input[type=checkbox]){text-align:center}.adapt input[type=checkbox]{width:1.5em;height:1.5em}.adapt table td:first-child{border-bottom:0}.adapt table td:last-child{border-top:0}}";

// converted to char array by https://notisrac.github.io/FileToCArray/
const unsigned char favicon_32x32[] = {
Expand Down