diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index edafab8dddcd3e1b4f89a4ec122b3387673be153..fcc2f064866cf01059e3e852aad47717f72d4ea0 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 87296200822829222291be623b7656dfed65eec1..8e002a0049c73b1fc855820c639e7e0a1632097e 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 53d6d35ba8cc4445899e28251be6a6d978e3cf09..e91d18d2c3258b8a8e53217440ca5190a4e99d86 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 ea770a076f3c436c3bcd0e366fa3c2c1e5daaf90..f54019a2a8723e765961dc050b17d015a0eb8a6a 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 cba984519da7d332c82350920f2ad0aba2591ad1..0114c44f4cd9dcd8097df192f729e4c1d9510e09 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 de4dde89f0a05e0f9e1ece4bf121d2bc9723d360..db9962d6ef167cd656c382553a07d87dcacba3b5 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 b14d7a613c888c7165c0ba6dfe2f5d283a6f880b..0e37d04df924ad3f20436e7293550752e7da2821 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 6fa7f4303f4469bb00290e6231e76205590ebf3f..0e50603ebc530a846b916a0095e8dca6eb5cc4b4 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 2eabba4924301c20c1db8dcf9e4bfcc4899f2482..98a28df363f5f88b800e4225c554856068648a86 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 = [