Skip to content

Commit

Permalink
[5.1] Template color scheme configuration (#42221)
Browse files Browse the repository at this point in the history
* Color scheme switch
  • Loading branch information
Fedik committed Mar 11, 2024
1 parent 985b174 commit 37eaa18
Show file tree
Hide file tree
Showing 20 changed files with 235 additions and 25 deletions.
13 changes: 13 additions & 0 deletions administrator/components/com_users/forms/user.xml
Expand Up @@ -187,6 +187,19 @@
<option value="">JOPTION_USE_DEFAULT</option>
</field>

<field
name="colorScheme"
type="list"
label="COM_USERS_USER_COLORSCHEME_LABEL"
default=""
validate="options"
>
<option value="">JOPTION_USE_DEFAULT</option>
<option value="os">COM_USERS_USER_COLORSCHEME_OPTION_FOLLOW_OS</option>
<option value="light">COM_USERS_USER_COLORSCHEME_OPTION_LIGHT</option>
<option value="dark">COM_USERS_USER_COLORSCHEME_OPTION_DARK</option>
</field>

</fieldset>
<!-- User accessibility settings -->
<fieldset
Expand Down
4 changes: 4 additions & 0 deletions administrator/language/en-GB/com_users.ini
Expand Up @@ -399,6 +399,10 @@ COM_USERS_USER_BACKUPCODES_CAPTIVE_PROMPT="If you do not have access to your usu
COM_USERS_USER_BACKUPCODES_DESC="Lets you access the site if all other Multi-factor Authentication methods you have set up fail."
COM_USERS_USER_BATCH_FAILED="An error was encountered while performing the batch operation: %s."
COM_USERS_USER_BATCH_SUCCESS="Batch operation completed."
COM_USERS_USER_COLORSCHEME_LABEL="Dark Mode"
COM_USERS_USER_COLORSCHEME_OPTION_DARK="Use Dark color scheme"
COM_USERS_USER_COLORSCHEME_OPTION_FOLLOW_OS="Follow OS settings"
COM_USERS_USER_COLORSCHEME_OPTION_LIGHT="Use Light color scheme"
COM_USERS_USER_FIELD_BACKEND_LANGUAGE_LABEL="Backend Language"
COM_USERS_USER_FIELD_BACKEND_TEMPLATE_LABEL="Backend Template Style"
COM_USERS_USER_FIELD_BLOCK="Blocked"
Expand Down
2 changes: 2 additions & 0 deletions administrator/language/en-GB/mod_user.ini
Expand Up @@ -8,4 +8,6 @@ MOD_USER_ACCESSIBILITY_SETTINGS="Accessibility Settings"
MOD_USER_EDIT_ACCOUNT="Edit Account"
MOD_USER_MENU="User Menu"
MOD_USER_TITLE="Signed in as %s"
MOD_USER_LIGHT_MODE="Light Mode"
MOD_USER_DARK_MODE="Dark Mode"
MOD_USER_XML_DESCRIPTION="This module shows the User Menu and is intended to be displayed in the 'status' position."
4 changes: 4 additions & 0 deletions administrator/language/en-GB/tpl_atum.ini
Expand Up @@ -6,6 +6,10 @@
ATUM="Atum Administrator template"
TPL_ATUM_BACK_TO_CONTROL_PANEL="Back to Dashboard"
TPL_ATUM_BACKEND_LOGIN="Joomla Administrator Login"
TPL_ATUM_COLORSCHEME_LABEL="Dark Mode"
TPL_ATUM_COLORSCHEME_OPTION_DARK="Use Dark color scheme"
TPL_ATUM_COLORSCHEME_OPTION_FOLLOW_OS="Follow OS settings"
TPL_ATUM_COLORSCHEME_OPTION_LIGHT="Use Light color scheme"
TPL_ATUM_COLORS_HUE="Choose your hue value for the dark template colour"
TPL_ATUM_COLORS_SETTINGS_BG_LIGHT_LABEL="Light Background"
TPL_ATUM_COLORS_SETTINGS_LABEL="Colour Settings"
Expand Down
16 changes: 15 additions & 1 deletion administrator/modules/mod_user/tmpl/default.php
Expand Up @@ -22,12 +22,16 @@
return;
}

$tParams = $app->getTemplate(true)->params;
// Not all templates support a colorScheme
$colorSchemeSwitch = !!$tParams->get('colorScheme');

// Load the Bootstrap Dropdown
HTMLHelper::_('bootstrap.dropdown', '.dropdown-toggle');
?>
<div class="header-item-content dropdown header-profile">
<button class="dropdown-toggle d-flex align-items-center ps-0 py-0" data-bs-toggle="dropdown" type="button"
title="<?php echo Text::_('MOD_USER_MENU'); ?>">
data-bs-auto-close="outside" title="<?php echo Text::_('MOD_USER_MENU'); ?>">
<span class="header-item-icon">
<span class="icon-user-circle" aria-hidden="true"></span>
</span>
Expand All @@ -47,6 +51,16 @@
<span class="icon-user icon-fw" aria-hidden="true"></span>
<?php echo Text::_('MOD_USER_EDIT_ACCOUNT'); ?>
</a>
<?php if ($colorSchemeSwitch) : ?>
<button type="button" class="dropdown-item" data-color-scheme-switch>
<span class="d-dark-scheme-none">
<span class="fa fa-sun icon-fw me-2" aria-hidden="true"></span> <?php echo Text::_('MOD_USER_LIGHT_MODE'); ?>
</span>
<span class="d-light-scheme-none">
<span class="fa fa-moon icon-fw me-2" aria-hidden="true"></span> <?php echo Text::_('MOD_USER_DARK_MODE'); ?>
</span>
</button>
<?php endif; ?>
<?php $route = 'index.php?option=com_users&task=user.edit&id=' . $user->id . '&return=' . base64_encode($uri) . '#attrib-accessibility'; ?>
<a class="dropdown-item" href="<?php echo Route::_($route); ?>">
<span class="icon-universal-access icon-fw" aria-hidden="true"></span>
Expand Down
22 changes: 20 additions & 2 deletions administrator/templates/atum/component.php
Expand Up @@ -10,11 +10,13 @@

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;

/** @var \Joomla\CMS\Document\HtmlDocument $this */

$wa = $this->getWebAssetManager();
$app = Factory::getApplication();
$wa = $this->getWebAssetManager();

// Get the hue value
preg_match('#^hsla?\(([0-9]+)[\D]+([0-9]+)[\D]+([0-9]+)[\D]+([0-9](?:.\d+)?)?\)$#i', $this->params->get('hue', 'hsl(214, 63%, 20%)'), $matches);
Expand Down Expand Up @@ -54,10 +56,26 @@
$this->addHeadLink(HTMLHelper::_('image', 'favicon.ico', '', [], true, 1), 'alternate icon', 'rel', ['type' => 'image/vnd.microsoft.icon']);
$this->addHeadLink(HTMLHelper::_('image', 'joomla-favicon-pinned.svg', '', [], true, 1), 'mask-icon', 'rel', ['color' => '#000']);

$colorScheme = $this->params->get('colorScheme', 'os');
$themeModeAttr = '';

if ($colorScheme) {
$themeModes = ['os' => ' data-color-scheme-os', 'light' => ' data-bs-theme="light" data-color-scheme="light"', 'dark' => ' data-bs-theme="dark" data-color-scheme="dark"'];
// Check for User choose, for now this have a priority over the parameters
$userColorScheme = $app->getInput()->cookie->get('userColorScheme', '');
if ($userColorScheme && !empty($themeModes[$userColorScheme])) {
$themeModeAttr = $themeModes[$userColorScheme];
} else {
// Check parameters first (User and Template), then look if we have detected the OS color scheme (if it set to 'os')
$colorScheme = $app->getIdentity()->getParam('colorScheme', $colorScheme);
$osColorScheme = $colorScheme === 'os' ? $app->getInput()->cookie->get('osColorScheme', '') : '';
$themeModeAttr = ($themeModes[$colorScheme] ?? '') . ($themeModes[$osColorScheme] ?? '');
}
}
?>

<!DOCTYPE html>
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>">
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>"<?php echo $themeModeAttr; ?>>
<head>
<jdoc:include type="metas" />
<jdoc:include type="styles" />
Expand Down
20 changes: 18 additions & 2 deletions administrator/templates/atum/error_full.php
Expand Up @@ -77,13 +77,29 @@
// Set some meta data
$this->setMetaData('viewport', 'width=device-width, initial-scale=1');

$monochrome = (bool) $this->params->get('monochrome');
$monochrome = (bool) $this->params->get('monochrome');
$colorScheme = $this->params->get('colorScheme', 'os');
$themeModeAttr = '';

if ($colorScheme) {
$themeModes = ['os' => ' data-color-scheme-os', 'light' => ' data-bs-theme="light" data-color-scheme="light"', 'dark' => ' data-bs-theme="dark" data-color-scheme="dark"'];
// Check for User choose, for now this have a priority over the parameters
$userColorScheme = $app->getInput()->cookie->get('userColorScheme', '');
if ($userColorScheme && !empty($themeModes[$userColorScheme])) {
$themeModeAttr = $themeModes[$userColorScheme];
} else {
// Check parameters first (User and Template), then look if we have detected the OS color scheme (if it set to 'os')
$colorScheme = $app->getIdentity()->getParam('colorScheme', $colorScheme);
$osColorScheme = $colorScheme === 'os' ? $app->getInput()->cookie->get('osColorScheme', '') : '';
$themeModeAttr = ($themeModes[$colorScheme] ?? '') . ($themeModes[$osColorScheme] ?? '');
}
}

// @see administrator/templates/atum/html/layouts/status.php
$statusModules = LayoutHelper::render('status', ['modules' => 'status']);
?>
<!DOCTYPE html>
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>">
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>"<?php echo $themeModeAttr; ?>>
<head>
<jdoc:include type="metas" />
<jdoc:include type="styles" />
Expand Down
20 changes: 18 additions & 2 deletions administrator/templates/atum/error_login.php
Expand Up @@ -81,13 +81,29 @@
// Set some meta data
$this->setMetaData('viewport', 'width=device-width, initial-scale=1');

$monochrome = (bool) $this->params->get('monochrome');
$monochrome = (bool) $this->params->get('monochrome');
$colorScheme = $this->params->get('colorScheme', 'os');
$themeModeAttr = '';

if ($colorScheme) {
$themeModes = ['os' => ' data-color-scheme-os', 'light' => ' data-bs-theme="light" data-color-scheme="light"', 'dark' => ' data-bs-theme="dark" data-color-scheme="dark"'];
// Check for User choose, for now this have a priority over the parameters
$userLastMode = $app->getInput()->cookie->get('userColorScheme', '');
if ($userLastMode && !empty($themeModes[$userLastMode])) {
$themeModeAttr = $themeModes[$userLastMode];
} else {
// Check parameters first (User and Template), then look if we have detected the OS color scheme (if it set to 'os')
$colorScheme = $app->getIdentity()->getParam('colorScheme', $colorScheme);
$lastMode = $colorScheme === 'os' ? $app->getInput()->cookie->get('osColorScheme', '') : '';
$themeModeAttr = ($colorScheme === 'os' ? $themeModes['os'] : '') . ($themeModes[$lastMode] ?? '');
}
}

// @see administrator/templates/atum/html/layouts/status.php
$statusModules = LayoutHelper::render('status', ['modules' => 'status']);
?>
<!DOCTYPE html>
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>">
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>"<?php echo $themeModeAttr; ?>>
<head>
<jdoc:include type="metas" />
<jdoc:include type="styles" />
Expand Down
24 changes: 20 additions & 4 deletions administrator/templates/atum/index.php
Expand Up @@ -92,7 +92,7 @@ function adjustColorLightness($r, $g, $b, $percent)
--link-color-rgb: ' . $r . ',' . $g . ',' . $b . ';
--template-special-color: ' . $this->params->get('special-color', '#001B4C') . ';
}')
->addInlineStyle('@media (prefers-color-scheme: dark) { :root {
->addInlineStyle(':root[data-color-scheme="dark"] {
--link-color: ' . $linkColorDark . ';
--link-color-rgb: ' . $rd . ',' . $gd . ',' . $bd . ';
--link-color-rgb-hvr: ' . $linkColorDarkHvr . ';
Expand All @@ -105,22 +105,38 @@ function adjustColorLightness($r, $g, $b, $percent)
// Set some meta data
$this->setMetaData('viewport', 'width=device-width, initial-scale=1');

$monochrome = (bool) $this->params->get('monochrome');
$monochrome = (bool) $this->params->get('monochrome');
$colorScheme = $this->params->get('colorScheme', 'os');
$themeModeAttr = '';

if ($colorScheme) {
$themeModes = ['os' => ' data-color-scheme-os', 'light' => ' data-bs-theme="light" data-color-scheme="light"', 'dark' => ' data-bs-theme="dark" data-color-scheme="dark"'];
// Check for User choose, for now this have a priority over the parameters
$userColorScheme = $app->getInput()->cookie->get('userColorScheme', '');
if ($userColorScheme && !empty($themeModes[$userColorScheme])) {
$themeModeAttr = $themeModes[$userColorScheme];
} else {
// Check parameters first (User and Template), then look if we have detected the OS color scheme (if it set to 'os')
$colorScheme = $app->getIdentity()->getParam('colorScheme', $colorScheme);
$osColorScheme = $colorScheme === 'os' ? $app->getInput()->cookie->get('osColorScheme', '') : '';
$themeModeAttr = ($themeModes[$colorScheme] ?? '') . ($themeModes[$osColorScheme] ?? '');
}
}

Text::script('TPL_ATUM_MORE_ELEMENTS');

// @see administrator/templates/atum/html/layouts/status.php
$statusModules = LayoutHelper::render('status', ['modules' => 'status']);
?>
<!DOCTYPE html>
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>"<?php echo $a11y_font ? ' class="a11y_font"' : ''; ?>>
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>"<?php echo $a11y_font ? ' class="a11y_font"' : ''; ?><?php echo $themeModeAttr; ?>>
<head>
<jdoc:include type="metas" />
<jdoc:include type="styles" />
<jdoc:include type="scripts" />
</head>

<body data-color-scheme-os class="admin <?php echo $option . ' view-' . $view . ' layout-' . $layout . ($task ? ' task-' . $task : '') . ($monochrome || $a11y_mono ? ' monochrome' : '') . ($a11y_contrast ? ' a11y_contrast' : '') . ($a11y_highlight ? ' a11y_highlight' : ''); ?>">
<body class="admin <?php echo $option . ' view-' . $view . ' layout-' . $layout . ($task ? ' task-' . $task : '') . ($monochrome || $a11y_mono ? ' monochrome' : '') . ($a11y_contrast ? ' a11y_contrast' : '') . ($a11y_highlight ? ' a11y_highlight' : ''); ?>">
<noscript>
<div class="alert alert-danger" role="alert">
<?php echo Text::_('JGLOBAL_WARNJAVASCRIPT'); ?>
Expand Down
20 changes: 18 additions & 2 deletions administrator/templates/atum/login.php
Expand Up @@ -75,7 +75,23 @@
// Set some meta data
$this->setMetaData('viewport', 'width=device-width, initial-scale=1');

$monochrome = (bool) $this->params->get('monochrome');
$monochrome = (bool) $this->params->get('monochrome');
$colorScheme = $this->params->get('colorScheme', 'os');
$themeModeAttr = '';

if ($colorScheme) {
$themeModes = ['os' => ' data-color-scheme-os', 'light' => ' data-bs-theme="light" data-color-scheme="light"', 'dark' => ' data-bs-theme="dark" data-color-scheme="dark"'];
// Check for User choose, for now this have a priority over the parameters
$userColorScheme = $app->getInput()->cookie->get('userColorScheme', '');
if ($userColorScheme && !empty($themeModes[$userColorScheme])) {
$themeModeAttr = $themeModes[$userColorScheme];
} else {
// Check parameters first (User and Template), then look if we have detected the OS color scheme (if it set to 'os')
$colorScheme = $app->getIdentity()->getParam('colorScheme', $colorScheme);
$osColorScheme = $colorScheme === 'os' ? $app->getInput()->cookie->get('osColorScheme', '') : '';
$themeModeAttr = ($themeModes[$colorScheme] ?? '') . ($themeModes[$osColorScheme] ?? '');
}
}

// Add cookie alert message
Text::script('JGLOBAL_WARNCOOKIES');
Expand All @@ -87,7 +103,7 @@

?>
<!DOCTYPE html>
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>">
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>"<?php echo $themeModeAttr; ?>>
<head>
<jdoc:include type="metas" />
<jdoc:include type="styles" />
Expand Down
12 changes: 12 additions & 0 deletions administrator/templates/atum/templateDetails.xml
Expand Up @@ -111,6 +111,18 @@
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="colorScheme"
type="list"
label="TPL_ATUM_COLORSCHEME_LABEL"
default="os"
validate="options"
>
<option value="0">JDISABLED</option>
<option value="os">TPL_ATUM_COLORSCHEME_OPTION_FOLLOW_OS</option>
<option value="light">TPL_ATUM_COLORSCHEME_OPTION_LIGHT</option>
<option value="dark">TPL_ATUM_COLORSCHEME_OPTION_DARK</option>
</field>
</fieldset>

<fieldset name="images" label="TPL_ATUM_IMAGE_SETTINGS_LABEL">
Expand Down
Expand Up @@ -105,8 +105,10 @@ const optionsToExtensions = async (options) => {
readOnly.$j_name = 'readOnly';
extensions.push(readOnly.of(EditorState.readOnly.of(!!options.readOnly)));

// Check for a skin that suits best for the active color scheme
// TODO: Use compartments to update on change of dark mode like: https://discuss.codemirror.net/t/dynamic-light-mode-dark-mode-how/4709
if ('colorSchemeOs' in document.body.dataset && window.matchMedia('(prefers-color-scheme: dark)').matches) {
if (('colorSchemeOs' in document.documentElement.dataset && window.matchMedia('(prefers-color-scheme: dark)').matches)
|| document.documentElement.dataset.colorScheme === 'dark') {
extensions.push(oneDark);
}

Expand Down
18 changes: 11 additions & 7 deletions build/media_source/plg_editors_tinymce/js/tinymce.es6.js
Expand Up @@ -155,17 +155,21 @@ Joomla.JoomlaTinyMCE = {
options.target = element;
}

// Check for a skin that suits best for the active color scheme
const skinLight = options.skin_light;
const skinDark = options.skin_dark;

if ('colorSchemeOs' in document.body.dataset) {
options.skin = (window.matchMedia('(prefers-color-scheme: dark)').matches ? skinDark : skinLight);
} else {
options.skin = skinLight;
}

delete options.skin_light;
delete options.skin_dark;
// Set light as default
options.skin = skinLight;

// For templates with OS preferred color scheme
if ('colorSchemeOs' in document.documentElement.dataset) {
const mql = window.matchMedia('(prefers-color-scheme: dark)');
options.skin = mql.matches ? skinDark : skinLight;
} else if (document.documentElement.dataset.colorScheme === 'dark') {
options.skin = skinDark;
}

// Ensure tinymce is initialised in readonly mode if the textarea has readonly applied
let readOnlyMode = false;
Expand Down
44 changes: 44 additions & 0 deletions build/media_source/templates/administrator/atum/js/template.es6.js
Expand Up @@ -218,7 +218,51 @@ function subheadScrolling() {
}
}

/**
* Watch for Dark mode changes
*
* @since __DEPLOY_VERSION__
*/
function darkModeWatch() {
const docEl = document.documentElement;
// Update data-bs-theme when scheme has been changed
document.addEventListener('joomla:color-scheme-change', () => {
docEl.dataset.bsTheme = docEl.dataset.colorScheme;
});

// Look for User choose with data-color-scheme-switch button
const buttons = document.querySelectorAll('button[data-color-scheme-switch]');
buttons.forEach((button) => {
button.addEventListener('click', (e) => {
e.preventDefault();
const { colorScheme } = docEl.dataset;
const newScheme = colorScheme !== 'dark' ? 'dark' : 'light';
docEl.dataset.colorScheme = newScheme;
document.cookie = `userColorScheme=${newScheme};`;
document.dispatchEvent(new CustomEvent('joomla:color-scheme-change', { bubbles: true }));
});
});

// Look for data-color-scheme-os attribute
const { colorSchemeOs } = docEl.dataset;
if (colorSchemeOs === undefined) return;
// Watch on media changes
const mql = window.matchMedia('(prefers-color-scheme: dark)');
const check = () => {
const newScheme = mql.matches ? 'dark' : 'light';
// Check if theme already was set
if (docEl.dataset.colorScheme === newScheme) return;
docEl.dataset.colorScheme = newScheme;
// Store theme in cookies, so php will know the last choice
document.cookie = `osColorScheme=${newScheme};`;
document.dispatchEvent(new CustomEvent('joomla:color-scheme-change', { bubbles: true }));
};
mql.addEventListener('change', check);
check();
}

// Initialize
darkModeWatch();
headerItemsInDropdown();
reactToResize();
subheadScrolling();
Expand Down
@@ -1,8 +1,8 @@
// Remove prefix added in BS5 for compat with Joomla beta's shipped with Betas
$prefix: "";

// Use dark mode conditionally based on media queries from the browser to match the desktop theme
$color-mode-type: media-query;
// Use dark mode conditionally based on data-bs-theme attribute
$color-mode-type: data;

// Enable dark mode in J5.0
$enable-dark-mode: true;
Expand Down

0 comments on commit 37eaa18

Please sign in to comment.