diff --git a/src/lib/components/ThemeToggle/index.js b/src/lib/components/ThemeToggle/index.js new file mode 100644 index 00000000000..9273bb5fa86 --- /dev/null +++ b/src/lib/components/ThemeToggle/index.js @@ -0,0 +1,86 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Element that allows a user to toggle the current theme. + * @extends {HTMLElement} + * @final + */ +class ThemeToggle extends HTMLElement { + constructor() { + super(); + + this.STORAGE_KEY = 'user-color-scheme'; + this.COLOR_MODE_KEY = '--color-mode'; + } + + connectedCallback() { + this.toggleSwitch = this.querySelector('[role="switch"]'); + + if (this.toggleSwitch) { + // On change, calculate the new setting, toggle state changes and store in storage + this.toggleSwitch.addEventListener('change', () => { + const setting = this.toggleSwitch.checked ? 'dark' : 'light'; + this.applySetting(setting); + localStorage.setItem(this.STORAGE_KEY, setting); + }); + + this.applySetting(); + } + } + + applySetting(passedSetting) { + // Attempts to load the setting from local storage + const currentSetting = + passedSetting || localStorage.getItem(this.STORAGE_KEY); + + if (currentSetting) { + this.setToggleSwitchStatus(currentSetting); + window.applyThemeSetting(currentSetting); + } + // If no storage setting, we set up media query-based state change + else { + // Set the checkbox to on if we're already in dark preference + if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + this.setToggleSwitchStatus('dark'); + } + + // Listen for changes to the preference and set checkbox state accordingly + window + .matchMedia('(prefers-color-scheme: dark)') + .addEventListener('change', (evt) => { + this.setToggleSwitchStatus(evt.matches ? 'dark' : 'light'); + }); + } + } + + // Sets the correct aria checked role and checked state + setToggleSwitchStatus(currentSetting) { + const isDarkMode = currentSetting === 'dark'; + this.toggleSwitch.setAttribute( + 'aria-checked', + isDarkMode ? 'true' : 'false', + ); + + this.toggleSwitch.checked = isDarkMode; + } +} + +if ('customElements' in window) { + customElements.define('theme-toggle', ThemeToggle); +} + +export default ThemeToggle; diff --git a/src/lib/components/base.js b/src/lib/components/base.js index 958c021a99a..f203aa847c5 100644 --- a/src/lib/components/base.js +++ b/src/lib/components/base.js @@ -9,5 +9,6 @@ import './Header'; import './LanguageSelect'; import './NavigationDrawer'; import './SnackbarContainer'; +import './ThemeToggle'; import './Search'; import './SearchResults'; diff --git a/src/scss/blocks/_theme-toggle.scss b/src/scss/blocks/_theme-toggle.scss new file mode 100644 index 00000000000..63f6c9712c3 --- /dev/null +++ b/src/scss/blocks/_theme-toggle.scss @@ -0,0 +1,20 @@ +/// THIS IS VERY MUCH A TEMPORARY +/// BIT OF CSS UNTIL WE SLING SOME +/// PROPER UI DESIGN ON THE THEME TOGGLE +.theme-toggle { + display: flex; + align-items: center; + gap: 1rem; + + button { + border: 2px solid var(--color-stroke); + padding: 0.5rem 1rem; + + @include apply-utility('bg', 'action-bg'); + @include apply-utility('color', 'action-text'); + } + + [aria-pressed='true'] { + border-color: get-color('core-primary-glare'); + } +} diff --git a/src/scss/next.scss b/src/scss/next.scss index 4160670d4e2..b4f10eeaefd 100644 --- a/src/scss/next.scss +++ b/src/scss/next.scss @@ -276,6 +276,7 @@ a:active { background: var(--color-core-text); } + /// COMPOSITIONS @import 'compositions/auto-grid'; @import 'compositions/cluster'; @@ -283,6 +284,9 @@ a:active { @import 'compositions/sidebar'; @import 'compositions/switcher'; +/// BLOCK CLASSES +@import 'blocks/theme-toggle'; + /// UTILITY CLASSES @import 'utilities/flow'; @import 'utilities/focus-ring'; diff --git a/src/site/_data/design/themes.js b/src/site/_data/design/themes.js index 69fa813bbb4..41c7f27318b 100644 --- a/src/site/_data/design/themes.js +++ b/src/site/_data/design/themes.js @@ -10,6 +10,7 @@ */ module.exports = { colorKeys: { + MODE: 'mode', CORE_TEXT: 'core-text', CORE_BG: 'core-bg', MID_TEXT: 'mid-text', @@ -32,6 +33,7 @@ module.exports = { }, getDark() { return { + MODE: 'dark', CORE_TEXT: 'shades-light', CORE_BG: 'shades-dim', MID_TEXT: 'shades-gray-glare', @@ -55,6 +57,7 @@ module.exports = { }, getLight() { return { + MODE: 'light', CORE_TEXT: 'shades-dark', CORE_BG: 'shades-light-bright', MID_TEXT: 'shades-gray', @@ -91,9 +94,15 @@ module.exports = { { name: 'dark-toggle', key: 'prefix', - value: '[data-theme="dark"]', + value: '[data-user-theme="dark"]', tokens: this.getDark(), }, + { + name: 'light-toggle', + key: 'prefix', + value: '[data-user-theme="light"]', + tokens: this.getLight(), + }, ]; }, }; diff --git a/src/site/_data/design/tokens.json b/src/site/_data/design/tokens.json index cdc2c95f474..01cc2448d71 100644 --- a/src/site/_data/design/tokens.json +++ b/src/site/_data/design/tokens.json @@ -130,8 +130,8 @@ }, { "name": "Light", - "hex": "#f3f3f3", - "hsl": "hsl(0, 0%, 95%)" + "hex": "#f9f9f9", + "hsl": "hsl(0, 0%, 97%)" }, { "name": "Light Bright", diff --git a/src/site/_includes/partials/head.njk b/src/site/_includes/partials/head.njk index 3150254bc1a..2f72ec8d6e3 100644 --- a/src/site/_includes/partials/head.njk +++ b/src/site/_includes/partials/head.njk @@ -2,6 +2,18 @@ {% if CSS_ORIGIN === 'next' %} + {# hashForProd() will cache bust the css in dev with a random string. #} {% else %} diff --git a/src/site/content/en/design-system/temp-styleguide.njk b/src/site/content/en/design-system/temp-styleguide.njk index c38377bfa85..1b03908b21c 100644 --- a/src/site/content/en/design-system/temp-styleguide.njk +++ b/src/site/content/en/design-system/temp-styleguide.njk @@ -4,11 +4,21 @@ permalink: '/design-system/temp-styleguide/index.html' ---
+

Global CSS Temp styleguide

This is just a temporary dumping ground for the Global CSS to save time, working with the design system which still relies on the old CSS.


+

Toggle theme

+ + + + +

Headings

Heading level 1