From f84c44bad496d0d4aa179dd6484128553c1466c1 Mon Sep 17 00:00:00 2001
From: Alexandre <44178713+alexbelgium@users.noreply.github.com>
Date: Sat, 8 Jun 2024 11:44:21 +0200
Subject: [PATCH] Feature proposal : darkmode (#85)
* Create dark-style.css
* Add call to get_color_scheme
* Add call to get_color_scheme
* Create get_color_scheme function
* Add selection box in config.php
* Accept both lower and uppercase COLOR_SCHEME
* Add COLOR_SCHEME
* Add COLOR_SCHEME
* Change birdnet.conf after color scheme selection
* Move css to static to enable it
* Adapt css location to static
* Improve opacity value
* Fix logo background issues on smaller screens
* Avoid overlap of logos
* Ensure clean look (same margin as before but different location)
* Default background as transparent to avoid white on white
* Avoid black on dark ; improve readability
* Last element of green is homogenized to grayscale
* Enable site reload after color_scheme change
---
homepage/index.php | 3 +-
homepage/static/dark-style.css | 934 +++++++++++++++++++++++++++++
homepage/views.php | 3 +-
scripts/common.php | 9 +
scripts/config.php | 22 +-
scripts/install_config.sh | 6 +
scripts/update_birdnet_snippets.sh | 4 +
7 files changed, 978 insertions(+), 3 deletions(-)
create mode 100644 homepage/static/dark-style.css
diff --git a/homepage/index.php b/homepage/index.php
index 3a2e8ba..211bef1 100644
--- a/homepage/index.php
+++ b/homepage/index.php
@@ -6,6 +6,7 @@ $_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
require_once 'scripts/common.php';
$config = get_config();
$site_name = get_sitename();
+$color_scheme = get_color_scheme();
set_timezone();
?>
@@ -13,7 +14,7 @@ set_timezone();
-
+
diff --git a/homepage/static/dark-style.css b/homepage/static/dark-style.css
new file mode 100644
index 0000000..230ecfd
--- /dev/null
+++ b/homepage/static/dark-style.css
@@ -0,0 +1,934 @@
+@font-face {
+ font-family: 'Roboto Flex' ;
+ src: url('static/RobotoFlex-Regular.ttf') format('truetype');
+}
+
+* {
+ font-family: 'Roboto Flex', sans-serif;
+ box-sizing: border-box;
+ font-size: medium;
+ color: #f5f5f5;
+ background-color: transparent;
+}
+
+a {
+ text-decoration: none;
+ color: #f5f5f5;
+}
+
+a:hover {
+ font-weight: bold;
+ color: #ffffff;
+}
+
+h3 {
+ text-align: center;
+ margin-bottom: 12px;
+}
+
+iframe {
+ border: none;
+ height: 85%;
+ width: 100%;
+ position: fixed;
+}
+
+body {
+ margin: 0;
+ background-color: #1a1a1a;
+}
+
+table {
+ background-color: transparent;
+ border-collapse: collapse;
+ border-spacing: 0;
+ margin-left: auto;
+ margin-right: auto;
+ box-shadow: 0px 0px 17px 1px rgba(255, 255, 255, 0.10);
+ border-radius:3px;
+ overflow: hidden;
+}
+
+td {
+ padding: 10px;
+ vertical-align: top;
+ background-color: #2b2b2b;
+ font-weight: lighter;
+ text-align: center;
+}
+
+th {
+ padding: 12px;
+ font-weight: bold;
+ height: auto;
+ text-align: center;
+}
+
+audio, video{
+ max-height: 100%;
+ max-width: 100%;
+}
+
+label {
+ font-weight: bold;
+}
+
+hr {
+ border-color:#f5f5f5;
+}
+
+button {
+ background-color: transparent;
+ border: none;
+ color: #f5f5f5;
+ cursor: pointer;
+ transition:background-color 0.2s;
+}
+
+.disabled{
+ cursor: not-allowed;
+ pointer-events: none;
+ opacity: 0.5;
+}
+
+button:hover {
+ color: #ffffff;
+}
+
+.row {
+ display: flex;
+}
+
+.centered {
+ text-align: center;
+ display: block;
+ width: auto;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.banner {
+ height: 7%;
+ text-align: center;
+ background-color: #2b2b2b;
+}
+
+.banner h1 {
+ padding: .5em;
+ letter-spacing: 5px;
+}
+
+.banner audio,.banner form {
+ float: right;
+ width: 120px;
+ margin-left: -120px;
+ margin-right: auto;
+}
+
+.banner button {
+ padding-top: 8px;
+ font-weight: bold;
+ letter-spacing: 1px;
+}
+
+.banner a {
+ text-decoration: none;
+ color: #f5f5f5;
+ font-size: x-large;
+}
+
+.logo img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: 10px;
+ opacity: 0.7;
+}
+
+.modal {
+ display: none;
+ position: fixed;
+ z-index: 1;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ background-color: rgba(0,0,0,0.4);
+}
+
+.modal-content {
+ background-color: #2b2b2b;
+ margin: 15% auto;
+ padding: 20px;
+ border: 1px solid #888;
+ width: 80%;
+ text-align: center; /* Center the content */
+}
+
+@media (max-width: 768px) {
+ .modal-content {
+ width: 95%;
+ margin: 10% auto;
+ }
+ #labelDropdown {
+ max-width: 100%;
+ overflow-x: auto;
+ }
+}
+
+.topnav {
+ background-color: #2b2b2b;
+ display: flex;
+ flex: 65%;
+ width: 65%;
+ min-width: min-content;
+ justify-content: space-between;
+ margin-left: auto;
+ margin-right: auto;
+ margin-bottom: 15px;
+ box-shadow: 0px 0px 28px 1px rgba(255, 255, 255, 0.10) !important;
+ border-radius: 4px;
+}
+
+.topimage {
+ width:175px;
+ display:initial !important;
+ margin-left: 60px;
+}
+
+.topnav form {
+ margin: 0;
+ padding: 0;
+}
+
+.topnav button {
+ background-color: transparent;
+ text-align: center;
+ padding: 14px 16px;
+ width: auto;
+ vertical-align: middle;
+ color: #f5f5f5;
+}
+
+.topnav button:hover {
+ background-color: #3b3b3b;
+ color: #f5f5f5;
+}
+
+.topnav button.active {
+ background-color: #04AA6D;
+ color: white;
+}
+
+.topnav .button-hover {
+ background-color: #3b3b3b !important;
+ color: #f5f5f5;
+}
+
+.topnav .icon {
+ display: none;
+ color: #f5f5f5;
+}
+
+.overview th {
+ background-color: #2b2b2b;
+ text-align: center;
+ padding: 12px;
+}
+
+.overview td {
+ vertical-align: middle;
+}
+
+.overview div img {
+ max-height: 100%;
+ display: flex;
+ justify-content: center;
+ margin-right: auto;
+ margin-left: auto;
+ opacity: 0.7;
+}
+
+.overview .chart {
+ margin-top: 10px;
+}
+.overview-stats {
+ display: flex;
+ justify-content: center;
+}
+
+.left-column {
+ flex: 10%;
+ padding-left: 10px;
+}
+
+.right-column {
+ flex: 90%;
+ margin-right: 10%;
+}
+
+.stats td {
+ vertical-align: middle;
+}
+
+.stats table {
+ height: auto;
+}
+
+.stats button:hover {
+ color: #ffffff;
+}
+
+.overview button, .center button{
+ font-weight: bold;
+ color: #ffffff;
+}
+
+.history table,.history img {
+ width: auto;
+ margin-left: auto;
+ margin-right: auto;
+ opacity: 0.7;
+}
+
+.views {
+ transition: opacity 0.3s;
+ -webkit-transition: opacity 0.3s;
+}
+
+.views .centered button {
+ background-color: #3b3b3b;
+ padding: 12px;
+ transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow:0px 3px 1px -2px rgb(255, 255, 255, 0.20), 0px 2px 2px 0px rgb(255, 255, 255, 0.14), 0px 1px 5px 0px rgb(255, 255, 255, 0.12);
+ color: #f5f5f5;
+}
+
+.views .centered button:hover {
+ background-color: #4b4b4b;
+ color: #f5f5f5;
+ box-shadow:0px 2px 4px -1px rgb(255, 255, 255, 0.20), 0px 4px 5px 0px rgb(255, 255, 255, 0.14), 0px 1px 10px 0px rgb(255, 255, 255, 0.12);
+}
+
+.settings {
+ padding: 12px;
+}
+
+.settings h2 {
+ margin-top: 0px;
+ color: #f5f5f5;
+}
+
+.settings p {
+ margin-bottom: 0px;
+ color: #f5f5f5;
+}
+
+.settings h3 {
+ text-align: left;
+ color: #f5f5f5;
+}
+
+.settings button {
+ background-color: #3b3b3b;
+ padding: 12px;
+ color: #f5f5f5;
+}
+
+.settings button:hover {
+ background-color: #4b4b4b;
+ color: #f5f5f5;
+}
+
+.float button {
+ margin-top: 6px;
+ margin-bottom: 6px;
+ color: #f5f5f5;
+}
+
+.customlabels,.customlabels2 {
+ float:left;
+}
+
+.customlabels table {
+ height: 100%;
+}
+
+.customlabels td,.customlabels2 td {
+ border:none;
+ background-color: transparent;
+ vertical-align: middle;
+ color: #f5f5f5;
+}
+
+.customlabels button,.customlabels2 button {
+ padding: 12px;
+ background-color: #3b3b3b;
+ color: #f5f5f5;
+ width: 100%;
+}
+
+.column1, .column3 {
+ width: 45%;
+}
+
+.smaller {
+ width: 100%;
+ display: none;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.column2 {
+ text-align: center;
+ width: 10%;
+ height: 80%;
+}
+
+.column4 {
+ text-align: justify;
+ width: 10%;
+ height: 50%;
+}
+
+.column1 form,.column3 form {
+ width: 80%;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.column1 select,.column3 select {
+ height: 80%;
+ width: 100%;
+ background-color: #333; /* dark background */
+ color: #fff; /* white text */
+}
+
+.spectrogram {
+ width:50%
+}
+
+.full {
+ width:100%;
+}
+
+.logbutton, .navbuttons {
+ float: left;
+}
+
+.systemcontrols form,.servicecontrols form {
+ /*text-align: center;*/
+}
+
+.servicecontrols button {
+ background-color: #3b3b3b;
+ padding: 12px;
+ width: 50%;
+ color: #f5f5f5;
+}
+
+.systemcontrols button {
+ background-color: #3b3b3b;
+ display: block;
+ padding: 12px;
+ width: 50%;
+ margin: 16px auto;
+ color: #f5f5f5;
+}
+
+.servicecontrols button {
+ width: 20%;
+ color: #f5f5f5;
+}
+
+.btn-group-center {
+ text-align:center;
+ /*align-content: center;*/
+ margin: 16px auto;
+ position:relative;
+ /*display:inline-block;*/
+}
+
+.slider {
+ -webkit-appearance: none;
+ width: 33%;
+ height: 15px;
+ border-radius: 5px;
+ outline: none;
+ opacity: 0.7;
+ -webkit-transition: .2s;
+ transition: opacity .2s;
+}
+
+.slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 25px;
+ height: 25px;
+ border-radius: 50%;
+ background: #04AA6D;
+ cursor: pointer;
+}
+
+.slider::-moz-range-thumb {
+ width: 25px;
+ height: 25px;
+ border-radius: 50%;
+ background: #04AA6D;
+ cursor: pointer;
+}
+
+#body::-webkit-scrollbar {
+ /*display:none*/
+}
+
+@media screen and (max-width: 1290px) {
+ .column1,.column2,.column3,.column4 {
+ height: 90%
+ }
+ .left-column {
+ display: none;
+ }
+ .right-column {
+ flex: 100%;
+ margin: 0;
+ }
+ img {
+ max-width: 100%;
+ }
+ .overview {
+ overflow-x: hidden;
+ }
+ .overview .right-column .chart img {
+ margin-left: 5%;
+ margin-right: auto;
+ margin-top: 10px;
+ opacity: 0.7;
+ }
+}
+@media screen and (max-width: 1000px) {
+ .customlabels form,.customlabels2 form {
+ width: 95%;
+ }
+ .column1, .column3 {
+ width: 50%;
+ height: 100%;
+ }
+ .column1 select,.column3 select {
+ height: 70%;
+ background-color: #333; /* dark background */
+ color: #fff; /* white text */
+ }
+ .column2,.column4 {
+ display: none;
+ }
+ .smaller{
+ display: block;
+ }
+ .systemcontrols button,.servicecontrols button {
+ width: 60%;
+ padding: 12px;
+ background-color: #3b3b3b;
+ }
+ .topnav {
+ flex: 100%;
+ width: 100%;
+ }
+ .topnav button {display: none;}
+ .topnav button.icon {
+ padding: 0;
+ margin: 0;
+ display: block;
+ width: 100%;
+ margin-left: auto;
+ margin-right: auto;
+ }
+ .banner {
+ height: auto;
+ }
+ .banner img {
+ display: none;
+ }
+ .logo img {
+ display: block;
+ width: 60px;
+ height: 60px;
+ }
+ .topnav.responsive {position: relative;}
+ .topnav.responsive button {
+ display: block;
+ text-align: center;
+ }
+}
+
+@media screen and (max-width: 800px) {
+ .column1, .column3 {
+ width: 100%;
+ }
+ .systemcontrols button,.servicecontrols button {
+ width: 80%;
+ padding: 12px;
+ background-color: #3b3b3b;
+ }
+ .stats img {
+ width: 100%;
+ margin-left:auto;
+ margin-right:auto;
+ opacity: 0.7;
+ }
+ .overview img {
+ width: 100%
+ }
+ .banner {
+ height: auto;
+ margin-left: auto;
+ text-align: center;
+ }
+ .banner img {
+ display: none;
+ }
+ .stream {
+ float: right;
+ display: block;
+ width: 100px;
+ }
+ .logo img {
+ display: block;
+ width: 60px;
+ height: 60px;
+ }
+ .play table,.overview table,.stats table {
+ width: 100%;
+ }
+ .topnav {
+ flex: 100%;
+ width: 100%;
+ flex-direction: column;
+ }
+ .topnav button {
+ font-size: large;
+ width: 100%
+ }
+ .topnav button {display: none;}
+ .topnav button.icon {
+ margin: 0;
+ padding: 0;
+ display: block;
+ width: 100%;
+ margin-left: auto;
+ margin-right: auto;
+ }
+ .topnav.responsive {position: relative;}
+ .topnav.responsive button {
+ display: block;
+ }
+ .left-column {
+ display: none;
+ }
+ .left {
+ display:none;
+ }
+}
+
+.copyimage {
+ position:absolute;
+ top:7px;
+ right:7px;
+ width:25px !important;
+ height:25px !important;
+}
+
+.copyimage-mobile {
+ width: 16px !important;
+ height: 16px !important;
+}
+
+.relative {
+ position:relative;
+}
+
+.sortbutton {
+ margin-top:10px;
+ font-size:x-large;
+ background:#3b3b3b;
+ padding:5px;
+ box-shadow: 0px 0px 5px 2px rgba(0, 0, 0, 0.10);
+}
+
+button.legacyview {
+ display: none;
+ color:gray;
+ margin:5px;
+ float:right;
+ z-index:100;
+ position:relative;
+ font-size:small;
+ background:#3b3b3b;
+ padding:5px;
+ transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow:0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%);
+}
+
+button.legacyview:hover {
+ box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%);
+}
+
+button.loadmore {
+ margin-top:10px;
+ font-size:x-large;
+ background:#3b3b3b;
+ padding:10px;
+ transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow:0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%);
+
+}
+
+button.loadmore:hover {
+ box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%);
+}
+
+#searchterm {
+ transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+#searchterm:hover {
+ box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%);
+}
+
+tr {
+ background-color:#3b3b3b;
+}
+
+.history.centered form {
+ display:flex;
+ justify-content: center;
+}
+
+.history.centered input {
+ margin-right:5px;
+ border:0px;
+}
+
+.centered form#views button {
+ box-shadow: 0px 0px 5px 2px rgba(0, 0, 0, 0.10);
+ margin:2px;
+ transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow:0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%);
+}
+
+.centered form#views button:hover {
+ box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%);
+}
+
+dl {
+ margin: 1em 0 0 1em;
+}
+dt {
+ float: left;
+ clear: left;
+ width: auto;
+ text-align: left;
+ font-weight: bold;
+ color: gray;
+}
+dd::before {
+ content: ": ";
+}
+
+input {
+ box-shadow: 0px 0px 17px 1px rgba(0, 0, 0, 0.10);
+ background-color: #333;
+ color: #fff;
+}
+
+dialog {
+ border:none;
+}
+
+dialog::backdrop {
+ background: repeating-linear-gradient(
+ 30deg,
+ rgba(24, 194, 236, 0.2),
+ rgba(24, 194, 236, 0.2) 1px,
+ rgba(24, 194, 236, 0.3) 1px,
+ rgba(24, 194, 236, 0.3) 20px
+ );
+ backdrop-filter: blur(1px);
+}
+
+.centered_image_container {
+ font-size:19px !important;
+ display:inline-block;
+ position:relative;
+ margin-bottom:3px;
+}
+
+.centered_image_container img.img1 {
+ transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow:0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%);
+ cursor:pointer;
+ height:95%;
+ position:absolute;
+ right:110%;
+ top:0px;
+ border-radius: 5px;
+ width:unset;
+}
+
+.centered_image_container img.img1:hover{
+ opacity:0.8;
+ box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%);
+}
+
+#birdimage {
+ transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow:0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%);
+ cursor:pointer;
+ border-radius: 5px;
+}
+
+#birdimage:hover {
+ opacity:0.8;
+ box-shadow:0px 2px 4px -1px rgb(0 0 0 / 20%), 0px 4px 5px 0px rgb(0 0 0 / 14%), 0px 1px 10px 0px rgb(0 0 0 / 12%);
+}
+
+.centered_image_container * {
+ font-size:19px !important;
+}
+
+.centered_image_container form {
+ margin-bottom:0px;
+}
+
+.brbanner {
+ padding:15px;
+ background-color:#3b3b3b;
+ text-align:center;
+ font-size:large;
+ color: #f5f5f5;
+}
+
+#gain.centered {
+ margin-bottom:10px;
+}
+
+.updatenumber {
+ margin-left:5px;
+ position:absolute;
+ display:inline-block;
+ background-color:#c8191a;
+ color:white;
+ width:20px;
+ line-height:20px;
+ border-radius:12px;
+ text-align:center;
+ font-size:small;
+}
+
+form#views button .updatenumber {
+ position:initial;
+ margin-left:0px;
+}
+
+#detections_table_overview table {
+ width:944px;
+}
+
+#recent_detection_middle_td{
+ width:33%;
+}
+@media screen and (max-width:500px) {
+ #recent_detection_middle_td{
+ width:66%;
+ }
+}
+#recent_detection_middle_td img{
+ width:unset !important;
+ height:75px;
+ float:left;
+}
+
+.settingstable {
+ margin-left:unset;
+ margin-right:unset;
+}
+.settingstable td {
+ text-align:unset;
+}
+.settingstable textarea {
+ width:100%;
+ margin-top:10px;
+}
+.settingstable h2 {
+ font-size:x-large;
+ color: #f5f5f5;
+}
+.plaintable {
+ box-shadow: unset;
+ color: #f5f5f5;
+}
+.plaintable td {
+ padding: unset;
+ color: #f5f5f5;
+}
+
+.brbanner h1 {
+ margin:0px;
+ font-size: xx-large;
+ color: #f5f5f5;
+}
+
+.testbtn {
+ background:#3b3b3b !important;
+ color: #f5f5f5;
+}
+
+pre.bash {
+ background-color: #2b2b2b;
+ color: #f5f5f5;
+ font-size: medium ;
+ font-family: Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
+ width: 100%;
+ display: inline-block;
+}
+pre#timer.bash {
+ display:unset;
+ width:unset;
+}
+
+#toolsbtn {
+ min-width: max-content;
+ color: #f5f5f5;
+}
+
+#showpassword {
+ cursor:pointer;
+ margin-left:2px;
+ height:5px;
+ line-height:5px;
+ padding:3px;
+ background-color:#3b3b3b;
+ color: #f5f5f5;
+}
+
+#newrtspstream{
+ cursor: pointer;
+ margin-left: 2px;
+ height: 5px;
+ line-height: 5px;
+ padding: 3px;
+ background-color: #3b3b3b;
+ color: #f5f5f5;
+}
+
+.exclude_species_list_option_highlight {
+ color: #f5f5f5;
+ background-color: #3b3b3b;
+ font-weight: bolder;
+}
+
+#ddnewline::before {
+ content: none;
+ color: #f5f5f5;
+}
diff --git a/homepage/views.php b/homepage/views.php
index 50f4179..8a5cbe1 100644
--- a/homepage/views.php
+++ b/homepage/views.php
@@ -10,6 +10,7 @@ require_once 'scripts/common.php';
$user = get_user();
$home = get_home();
$config = get_config();
+$color_scheme = get_color_scheme();
set_timezone();
if(is_authenticated() && (!isset($_SESSION['behind']) || !isset($_SESSION['behind_time']) || time() > $_SESSION['behind_time'] + 86400)) {
@@ -52,7 +53,7 @@ elseif ($config["LONGITUDE"] == "0.000") {
BirdNET-Pi DB
-
+