diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerBase.php b/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerBase.php index 44cafc3dcf..a6e2cd0489 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerBase.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Base/ControllerBase.php @@ -383,14 +383,19 @@ class ControllerBase extends ControllerRoot $this->view->menuBreadcrumbs = $menu->getBreadcrumbs(); // set theme in ui_theme template var, let template handle its defaults (if there is no theme). - if ( - $cnf->object()->theme->count() > 0 && !empty($cnf->object()->theme) && + $theme_config = (string)$cnf->object()->theme; + + if ($theme_config === 'opnsense-auto') { + $this->view->ui_theme = 'opnsense'; + $this->view->theme_auto = true; + } elseif ( + $cnf->object()->theme->count() > 0 && !empty($theme_config) && ( - is_dir('/usr/local/opnsense/www/themes/' . (string)$cnf->object()->theme) || + is_dir('/usr/local/opnsense/www/themes/' . $theme_config) || !is_dir('/usr/local/opnsense/www/themes') ) ) { - $this->view->ui_theme = $cnf->object()->theme; + $this->view->ui_theme = $theme_config; } // parse product properties, use template (.in) when not found diff --git a/src/opnsense/mvc/app/views/layouts/default.volt b/src/opnsense/mvc/app/views/layouts/default.volt index 1403586a4e..eb3feb3866 100644 --- a/src/opnsense/mvc/app/views/layouts/default.volt +++ b/src/opnsense/mvc/app/views/layouts/default.volt @@ -215,6 +215,11 @@ + + {% if theme_auto is defined and theme_auto %} + + {% endif %} +
diff --git a/src/opnsense/www/js/opnsense_theme_auto.js b/src/opnsense/www/js/opnsense_theme_auto.js new file mode 100644 index 0000000000..f06104c4cb --- /dev/null +++ b/src/opnsense/www/js/opnsense_theme_auto.js @@ -0,0 +1,59 @@ + /* + * Copyright (C) 2026 Konstantinos Spartalis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +(function() { + if (window.opnsenseAutoThemeEnabled || document.cookie.indexOf('opnsense_theme=opnsense-auto') !== -1) { + + function updateTheme() { + var isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + var activeTheme = isDark ? 'opnsense-dark' : 'opnsense'; + + var links = document.getElementsByTagName('link'); + for (var i = 0; i < links.length; i++) { + if (links[i].href && links[i].href.indexOf('/ui/themes/') !== -1) { + links[i].href = links[i].href.replace(/\/ui\/themes\/[^\/]+\//, '/ui/themes/' + activeTheme + '/'); + } + } + + var imgs = document.getElementsByTagName('img'); + for (var j = 0; j < imgs.length; j++) { + if (imgs[j].src && imgs[j].src.indexOf('/ui/themes/') !== -1) { + imgs[j].src = imgs[j].src.replace(/\/ui\/themes\/[^\/]+\//, '/ui/themes/' + activeTheme + '/'); + } + } + if (typeof d3 !== 'undefined' && typeof nv !== 'undefined') { + window.dispatchEvent(new Event('resize')); + } + } + + updateTheme(); + document.addEventListener('DOMContentLoaded', updateTheme); + + if (window.matchMedia) { + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateTheme); + } + } +})(); \ No newline at end of file diff --git a/src/www/authgui.inc b/src/www/authgui.inc index 67cf31d9b1..5e9662dfee 100644 --- a/src/www/authgui.inc +++ b/src/www/authgui.inc @@ -336,6 +336,12 @@ function display_error_form($text) + + + + + +
@@ -381,6 +387,12 @@ function display_login_form($Login_Error) + + + + + + diff --git a/src/www/head.inc b/src/www/head.inc index 9a34549412..7f2c8c841a 100644 --- a/src/www/head.inc +++ b/src/www/head.inc @@ -32,6 +32,12 @@ $pagetitle .= html_safe(sprintf(' | %s.%s', $config['system']['hostname'], $conf + + + + + +