From d57d070fd9e07f672cd5fb20f6a3ca6c9cf22aa6 Mon Sep 17 00:00:00 2001 From: Scott de Jonge <sdejonge@gitlab.com> Date: Fri, 10 May 2024 08:20:51 +0000 Subject: [PATCH] Add automatic color mode Add support for system mode to set mode from prefers-color-scheme Changelog: added --- app/assets/javascripts/main.js | 17 +++++++++++++++++ .../components/profile_preferences.vue | 10 +++------- app/helpers/preferences_helper.rb | 4 ++++ app/views/layouts/_head.html.haml | 7 +++++++ lib/gitlab/color_modes.rb | 4 +++- lib/gitlab/gon_helper.rb | 1 + locale/gitlab.pot | 3 +++ .../components/profile_preferences_spec.js | 15 +++++++++++++++ spec/frontend/profile/preferences/mock_data.js | 2 ++ 9 files changed, 55 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index edafab8dddcd3..fcc2f064866cf 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -49,6 +49,23 @@ if (process.env.NODE_ENV !== 'production' && gon?.test_env) { import(/* webpackMode: "eager" */ './test_utils'); } +if (gon?.user_color_mode === 'gl-system') { + const root = document.documentElement; + // eslint-disable-next-line @gitlab/require-i18n-strings + if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + root.classList.add('gl-dark'); + } + + // eslint-disable-next-line @gitlab/require-i18n-strings + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { + if (e.matches) { + root.classList.add('gl-dark'); + } else { + root.classList.remove('gl-dark'); + } + }); +} + document.addEventListener('beforeunload', () => { // Unbind scroll events // eslint-disable-next-line @gitlab/no-global-event-off diff --git a/app/assets/javascripts/profile/preferences/components/profile_preferences.vue b/app/assets/javascripts/profile/preferences/components/profile_preferences.vue index 8729620082282..8e002a0049c73 100644 --- a/app/assets/javascripts/profile/preferences/components/profile_preferences.vue +++ b/app/assets/javascripts/profile/preferences/components/profile_preferences.vue @@ -47,7 +47,7 @@ export default { data() { return { isSubmitEnabled: true, - darkModeOnCreate: null, + colorModeOnCreate: null, schemeOnCreate: null, }; }, @@ -55,7 +55,7 @@ export default { this.formEl.addEventListener('ajax:beforeSend', this.handleLoading); this.formEl.addEventListener('ajax:success', this.handleSuccess); this.formEl.addEventListener('ajax:error', this.handleError); - this.darkModeOnCreate = this.darkModeSelected(); + this.colorModeOnCreate = this.getSelectedColorMode(); this.schemeOnCreate = this.getSelectedScheme(); }, beforeDestroy() { @@ -64,10 +64,6 @@ export default { this.formEl.removeEventListener('ajax:error', this.handleError); }, methods: { - darkModeSelected() { - const mode = this.getSelectedColorMode(); - return mode ? mode.css_class === 'gl-dark' : null; - }, getSelectedColorMode() { const modeId = new FormData(this.formEl).get('user[color_mode_id]'); const mode = this.colorModes.find((item) => item.id === Number(modeId)); @@ -88,7 +84,7 @@ export default { // Reload the page if the theme has changed from light to dark mode or vice versa // or if color scheme has changed to correctly load all required styles. if ( - this.darkModeOnCreate !== this.darkModeSelected() || + this.colorModeOnCreate !== this.getSelectedColorMode() || this.schemeOnCreate !== this.getSelectedScheme() ) { window.location.reload(); diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index 53d6d35ba8cc4..e91d18d2c3258 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -79,6 +79,10 @@ def user_application_dark_mode? user_application_color_mode == 'gl-dark' end + def user_application_system_mode? + user_application_color_mode == 'gl-system' + end + def user_theme_primary_color Gitlab::Themes.for_user(current_user).primary_color end diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index ea770a076f3c4..f54019a2a8723 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -30,6 +30,13 @@ = stylesheet_link_tag_defer "application_dark" = yield :page_specific_styles = stylesheet_link_tag_defer "application_utilities_dark" + - elsif user_application_system_mode? + %meta{ name: 'color-scheme', content: 'light dark' } + = stylesheet_link_tag "application", media: "(prefers-color-scheme: light)" + = stylesheet_link_tag "application_dark", media: "(prefers-color-scheme: dark)" + = yield :page_specific_styles + = stylesheet_link_tag "application_utilities", media: "(prefers-color-scheme: light)" + = stylesheet_link_tag "application_utilities_dark", media: "(prefers-color-scheme: dark)" - else = stylesheet_link_tag_defer "application" = yield :page_specific_styles diff --git a/lib/gitlab/color_modes.rb b/lib/gitlab/color_modes.rb index cba984519da7d..0114c44f4cd9d 100644 --- a/lib/gitlab/color_modes.rb +++ b/lib/gitlab/color_modes.rb @@ -7,6 +7,7 @@ module ColorModes # Color mode ID used when no `default_color_mode` configuration setting is provided. APPLICATION_DEFAULT = 1 APPLICATION_DARK = 2 + APPLICATION_SYSTEM = 3 # Struct class representing a single Mode Mode = Struct.new(:id, :name, :css_class) @@ -14,7 +15,8 @@ module ColorModes def self.available_modes [ Mode.new(APPLICATION_DEFAULT, s_('ColorMode|Light'), 'gl-light'), - Mode.new(APPLICATION_DARK, s_('ColorMode|Dark (Experiment)'), 'gl-dark') + Mode.new(APPLICATION_DARK, s_('ColorMode|Dark (Experiment)'), 'gl-dark'), + Mode.new(APPLICATION_SYSTEM, s_('ColorMode|Auto (Experiment)'), 'gl-system') ] end diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index de4dde89f0a05..db9962d6ef167 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -13,6 +13,7 @@ def add_gon_variables gon.asset_host = ActionController::Base.asset_host gon.webpack_public_path = webpack_public_path gon.relative_url_root = Gitlab.config.gitlab.relative_url_root + gon.user_color_mode = Gitlab::ColorModes.for_user(current_user).css_class gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class gon.markdown_surround_selection = current_user&.markdown_surround_selection gon.markdown_automatic_lists = current_user&.markdown_automatic_lists diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b14d7a613c888..0e37d04df924a 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12645,6 +12645,9 @@ msgstr "" msgid "Color" msgstr "" +msgid "ColorMode|Auto (Experiment)" +msgstr "" + msgid "ColorMode|Dark (Experiment)" msgstr "" diff --git a/spec/frontend/profile/preferences/components/profile_preferences_spec.js b/spec/frontend/profile/preferences/components/profile_preferences_spec.js index 6fa7f4303f446..0e50603ebc530 100644 --- a/spec/frontend/profile/preferences/components/profile_preferences_spec.js +++ b/spec/frontend/profile/preferences/components/profile_preferences_spec.js @@ -14,6 +14,7 @@ import { colorModes, lightColorModeId, darkColorModeId, + autoColorModeId, themes, themeId1, } from '../mock_data'; @@ -242,5 +243,19 @@ describe('ProfilePreferences component', () => { expect(window.location.reload).toHaveBeenCalledTimes(1); }); + + it('reloads the page when switching from auto to light mode', async () => { + selectColorModeId(autoColorModeId); + setupWrapper(); + + selectColorModeId(lightColorModeId); + dispatchBeforeSendEvent(); + await nextTick(); + + dispatchSuccessEvent(); + await nextTick(); + + expect(window.location.reload).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/spec/frontend/profile/preferences/mock_data.js b/spec/frontend/profile/preferences/mock_data.js index 2eabba4924301..98a28df363f5f 100644 --- a/spec/frontend/profile/preferences/mock_data.js +++ b/spec/frontend/profile/preferences/mock_data.js @@ -21,10 +21,12 @@ export const bodyClasses = 'ui-light-indigo ui-light gl-dark'; export const lightColorModeId = 1; export const darkColorModeId = 2; +export const autoColorModeId = 3; export const colorModes = [ { id: lightColorModeId, css_class: 'gl-light' }, { id: darkColorModeId, css_class: 'gl-dark' }, + { id: autoColorModeId, css_class: 'gl-system' }, ]; export const themes = [ -- GitLab