diff --git a/.storybook/preview-body.html b/.storybook/preview-body.html index 1870d95b8f..7a92b6f95f 100644 --- a/.storybook/preview-body.html +++ b/.storybook/preview-body.html @@ -1,2 +1,2 @@ - + \ No newline at end of file diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d80c050b85..e1b8ebf38d 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -153,10 +153,8 @@ module ApplicationHelper tag.meta(content: content, property: property) end - def body_classes + def html_classes output = [] - output << content_for(:body_classes) - output << "theme-#{current_theme.parameterize}" output << 'system-font' if current_account&.user&.setting_system_font_ui output << 'custom-scrollbars' unless current_account&.user&.setting_system_scrollbars_ui output << (current_account&.user&.setting_reduce_motion ? 'reduce-motion' : 'no-reduce-motion') @@ -164,6 +162,12 @@ module ApplicationHelper output.compact_blank.join(' ') end + def body_classes + output = [] + output << content_for(:body_classes) + output.compact_blank.join(' ') + end + def cdn_host Rails.configuration.action_controller.asset_host end diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss index e16e5368e7..a38653cf1c 100644 --- a/app/javascript/styles/application.scss +++ b/app/javascript/styles/application.scss @@ -1,7 +1,2 @@ -@use 'mastodon/css_variables'; @use 'mastodon/variables'; @use 'common'; - -html { - color-scheme: dark; -} diff --git a/app/javascript/styles/common.scss b/app/javascript/styles/common.scss index d8a7f90a32..b56fa3f579 100644 --- a/app/javascript/styles/common.scss +++ b/app/javascript/styles/common.scss @@ -3,6 +3,7 @@ @use 'fonts/roboto-mono'; @use 'mastodon/reset'; +@use 'mastodon/theme'; @use 'mastodon/basics'; @use 'mastodon/branding'; @use 'mastodon/containers'; diff --git a/app/javascript/styles/contrast.scss b/app/javascript/styles/contrast.scss index af73c88fef..d89eaa610f 100644 --- a/app/javascript/styles/contrast.scss +++ b/app/javascript/styles/contrast.scss @@ -1,8 +1,3 @@ -@use 'mastodon/css_variables'; @use 'mastodon/variables'; @use 'common'; -@use 'contrast/diff'; - -html { - color-scheme: dark; -} +@use 'mastodon/high-contrast'; diff --git a/app/javascript/styles/mastodon-light.scss b/app/javascript/styles/mastodon-light.scss index 494efdbbde..11a92fb873 100644 --- a/app/javascript/styles/mastodon-light.scss +++ b/app/javascript/styles/mastodon-light.scss @@ -1,9 +1,4 @@ -@use 'mastodon-light/css_variables'; @use 'mastodon/variables' with ( $emojis-requiring-inversion: 'chains' ); @use 'common'; - -html { - color-scheme: light; -} diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss index db584f67f1..d28150f12a 100644 --- a/app/javascript/styles/mastodon/basics.scss +++ b/app/javascript/styles/mastodon/basics.scss @@ -3,6 +3,20 @@ html { color: var(--color-text-primary); background: var(--color-bg-ambient); + + &.custom-scrollbars { + scrollbar-color: var(--color-text-secondary) var(--color-bg-secondary); + } + + --outline-focus-default: 2px solid var(--color-text-brand); + --avatar-border-radius: 8px; + + // Variable for easily inverting directional UI elements, + --text-x-direction: 1; + + &.rtl { + --text-x-direction: -1; + } } html.has-modal { @@ -37,7 +51,7 @@ body { -webkit-tap-highlight-color: rgba(0, 0, 0, 0%); -webkit-tap-highlight-color: transparent; - &.system-font { + .system-font & { // system-ui => standard property (Chrome/Android WebView 56+, Opera 43+, Safari 11+) // -apple-system => Safari <11 specific // BlinkMacSystemFont => Chrome <56 on macOS specific diff --git a/app/javascript/styles/contrast/diff.scss b/app/javascript/styles/mastodon/high-contrast.scss similarity index 63% rename from app/javascript/styles/contrast/diff.scss rename to app/javascript/styles/mastodon/high-contrast.scss index f809c7cdc3..f55e7fae3b 100644 --- a/app/javascript/styles/contrast/diff.scss +++ b/app/javascript/styles/mastodon/high-contrast.scss @@ -1,17 +1,3 @@ -:root { - /* TEXT TOKENS */ - - --color-text-primary: var(--color-grey-50); - --color-text-secondary: var(--color-grey-300); - --color-text-tertiary: var(--color-grey-400); - --color-text-brand: var(--color-indigo-300); - --color-text-status-links: var(--color-text-brand); - - /* BORDER TOKENS */ - - --border-strength-primary: 18%; -} - .status__content a, .reply-indicator__content a, .edit-indicator__content a, diff --git a/app/javascript/styles/mastodon/reset.scss b/app/javascript/styles/mastodon/reset.scss index 3644b94cdf..2c3efbddc4 100644 --- a/app/javascript/styles/mastodon/reset.scss +++ b/app/javascript/styles/mastodon/reset.scss @@ -52,7 +52,3 @@ table { border-collapse: collapse; border-spacing: 0; } - -html:has(body.custom-scrollbars) { - scrollbar-color: var(--color-text-secondary) var(--color-bg-secondary); -} diff --git a/app/javascript/styles/mastodon/rtl.scss b/app/javascript/styles/mastodon/rtl.scss index 48935b75d7..a78f95f66d 100644 --- a/app/javascript/styles/mastodon/rtl.scss +++ b/app/javascript/styles/mastodon/rtl.scss @@ -1,6 +1,6 @@ @use 'variables' as *; -body.rtl { +html.rtl { direction: rtl; .reactions-bar { diff --git a/app/javascript/styles/mastodon/theme/_base.scss b/app/javascript/styles/mastodon/theme/_base.scss new file mode 100644 index 0000000000..94d592aa70 --- /dev/null +++ b/app/javascript/styles/mastodon/theme/_base.scss @@ -0,0 +1,27 @@ +@mixin palette { + --color-black: #000; + --color-grey-950: #181821; + --color-grey-800: #292938; + --color-grey-700: #444664; + --color-grey-600: #545778; + --color-grey-500: #696d91; + --color-grey-400: #8b8dac; + --color-grey-300: #b4b6cb; + --color-grey-200: #d8d9e3; + --color-grey-100: #f0f0f5; + --color-grey-50: #f0f1ff; + --color-white: #fff; + --color-indigo-600: #6147e6; + --color-indigo-400: #8886ff; + --color-indigo-300: #a5abfd; + --color-indigo-200: #c8cdfe; + --color-indigo-100: #e0e3ff; + --color-indigo-50: #f0f1ff; + --color-red-500: #ff637e; + --color-red-600: #ec003f; + --color-yellow-400: #ffb900; + --color-yellow-600: #e17100; + --color-yellow-700: #bb4d00; + --color-green-400: #05df72; + --color-green-600: #00a63e; +} diff --git a/app/javascript/styles/mastodon/css_variables.scss b/app/javascript/styles/mastodon/theme/_dark.scss similarity index 85% rename from app/javascript/styles/mastodon/css_variables.scss rename to app/javascript/styles/mastodon/theme/_dark.scss index 98b61d2f0b..e6fd6d3cc1 100644 --- a/app/javascript/styles/mastodon/css_variables.scss +++ b/app/javascript/styles/mastodon/theme/_dark.scss @@ -1,31 +1,6 @@ -@use 'theme_utils' as utils; - -:root { - --color-black: #000; - --color-grey-950: #181821; - --color-grey-800: #292938; - --color-grey-700: #444664; - --color-grey-600: #545778; - --color-grey-500: #696d91; - --color-grey-400: #8b8dac; - --color-grey-300: #b4b6cb; - --color-grey-200: #d8d9e3; - --color-grey-100: #f0f0f5; - --color-grey-50: #f0f1ff; - --color-white: #fff; - --color-indigo-600: #6147e6; - --color-indigo-400: #8886ff; - --color-indigo-300: #a5abfd; - --color-indigo-200: #c8cdfe; - --color-indigo-100: #e0e3ff; - --color-indigo-50: #f0f1ff; - --color-red-500: #ff637e; - --color-red-600: #ec003f; - --color-yellow-400: #ffb900; - --color-yellow-600: #e17100; - --color-green-400: #05df72; - --color-green-600: #00a63e; +@use 'utils'; +@mixin tokens { /* TEXT TOKENS */ --color-text-primary: var(--color-grey-50); @@ -127,7 +102,7 @@ // Warning --overlay-strength-warning: 10%; - --color-bg-warning-base: var(--color-yellow-600); + --color-bg-warning-base: var(--color-yellow-700); --color-bg-warning-base-hover: color-mix( in oklab, var(--color-bg-warning-base), @@ -212,18 +187,18 @@ --rich-text-container-color: rgb(87 24 60 / 100%); --rich-text-text-color: rgb(255 175 212 / 100%); --rich-text-decorations-color: rgb(128 58 95 / 100%); - - /* MISCELLANEOUS */ - - --outline-focus-default: 2px solid var(--color-text-brand); - --avatar-border-radius: 8px; } -body { - // Variable for easily inverting directional UI elements, - --text-x-direction: 1; +@mixin contrast-overrides { + /* TEXT TOKENS */ - &.rtl { - --text-x-direction: -1; - } + --color-text-primary: var(--color-grey-50); + --color-text-secondary: var(--color-grey-300); + --color-text-tertiary: var(--color-grey-400); + --color-text-brand: var(--color-indigo-300); + --color-text-status-links: var(--color-text-brand); + + /* BORDER TOKENS */ + + --border-strength-primary: 18%; } diff --git a/app/javascript/styles/mastodon-light/css_variables.scss b/app/javascript/styles/mastodon/theme/_light.scss similarity index 86% rename from app/javascript/styles/mastodon-light/css_variables.scss rename to app/javascript/styles/mastodon/theme/_light.scss index a96773f76c..f0dc1bdfbc 100644 --- a/app/javascript/styles/mastodon-light/css_variables.scss +++ b/app/javascript/styles/mastodon/theme/_light.scss @@ -1,31 +1,6 @@ -@use '../mastodon/theme_utils' as utils; - -:root { - --color-black: #000; - --color-grey-950: #181821; - --color-grey-800: #292938; - --color-grey-700: #444664; - --color-grey-600: #545778; - --color-grey-500: #696d91; - --color-grey-400: #8b8dac; - --color-grey-300: #b4b6cb; - --color-grey-200: #d8d9e3; - --color-grey-100: #f0f0f5; - --color-grey-50: #f0f1ff; - --color-white: #fff; - --color-indigo-600: #6147e6; - --color-indigo-400: #8886ff; - --color-indigo-300: #a5abfd; - --color-indigo-200: #c8cdfe; - --color-indigo-100: #e0e3ff; - --color-indigo-50: #f0f1ff; - --color-red-500: #ff637e; - --color-red-600: #ec003f; - --color-yellow-400: #ffb900; - --color-yellow-600: #e17100; - --color-green-400: #05df72; - --color-green-600: #00a63e; +@use 'utils'; +@mixin tokens { /* TEXT TOKENS */ --color-text-primary: var(--color-grey-950); @@ -124,7 +99,7 @@ // Warning --overlay-strength-warning: 10%; - --color-bg-warning-base: var(--color-yellow-600); + --color-bg-warning-base: var(--color-yellow-700); --color-bg-warning-base-hover: color-mix( in oklab, var(--color-bg-warning-base), @@ -207,9 +182,18 @@ --rich-text-container-color: rgb(255 216 231 / 100%); --rich-text-text-color: rgb(114 47 83 / 100%); --rich-text-decorations-color: rgb(255 175 212 / 100%); - - /* MISCELLANEOUS */ - - --outline-focus-default: 2px solid var(--color-text-brand); - --avatar-border-radius: 8px; +} + +@mixin contrast-overrides { + /* TEXT TOKENS */ + + --color-text-primary: var(--color-black); + --color-text-secondary: var(--color-grey-800); + --color-text-tertiary: var(--color-grey-700); + --color-text-brand: var(--color-indigo-600); + + /* BORDER TOKENS */ + + --border-strength-primary: 30%; + --color-border-on-bg-secondary: var(--color-grey-300); } diff --git a/app/javascript/styles/mastodon/_theme_utils.scss b/app/javascript/styles/mastodon/theme/_utils.scss similarity index 100% rename from app/javascript/styles/mastodon/_theme_utils.scss rename to app/javascript/styles/mastodon/theme/_utils.scss diff --git a/app/javascript/styles/mastodon/theme/index.scss b/app/javascript/styles/mastodon/theme/index.scss new file mode 100644 index 0000000000..d78b7e2a99 --- /dev/null +++ b/app/javascript/styles/mastodon/theme/index.scss @@ -0,0 +1,48 @@ +@use 'base'; +@use 'dark'; +@use 'light'; + +html { + @include base.palette; + + &[data-user-theme='system'] { + color-scheme: dark light; + + @media (prefers-color-scheme: dark) { + @include dark.tokens; + + @media (prefers-contrast: more) { + @include dark.contrast-overrides; + } + } + + @media (prefers-color-scheme: light) { + @include light.tokens; + + @media (prefers-contrast: more) { + @include light.contrast-overrides; + } + } + } +} + +.theme-dark, +html:where( + :not([data-user-theme='mastodon-light'], [data-user-theme='system']) +) { + color-scheme: dark; + + @include dark.tokens; +} + +html[data-user-theme='contrast'], +html[data-user-theme='contrast'] .theme-dark { + @include dark.contrast-overrides; +} + +.theme-light, +html:where([data-user-theme='mastodon-light']) { + color-scheme: light; + + @include light.tokens; +} diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 3418f6d953..0bfa9e74f9 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,5 +1,5 @@ !!! 5 -%html{ lang: I18n.locale } +%html{ lang: I18n.locale, class: html_classes, 'data-user-theme': current_theme.parameterize } %head %meta{ charset: 'utf-8' }/ %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1, viewport-fit=cover' }/ diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 942cc5103c..46dbd80de0 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -3,8 +3,8 @@ require 'rails_helper' RSpec.describe ApplicationHelper do - describe 'body_classes' do - context 'with a body class string from a controller' do + describe 'html_classes' do + context 'with non-default user settings' do before do user = Fabricate :user user.settings['web.use_system_font'] = true @@ -15,19 +15,11 @@ RSpec.describe ApplicationHelper do end it 'uses the current theme and user settings classes in the result' do - expect(helper.body_classes) - .to match(/theme-default/) - .and match(/system-font/) + expect(helper.html_classes) + .to match(/system-font/) .and match(/reduce-motion/) end - it 'includes values set via content_for' do - helper.content_for(:body_classes) { 'admin' } - - expect(helper.body_classes) - .to match(/admin/) - end - private def controller_helpers @@ -35,13 +27,22 @@ RSpec.describe ApplicationHelper do def current_account @current_account ||= Fabricate(:account, user: User.last) end - - def current_theme = 'default' end end end end + describe 'body_classes' do + context 'with a body class string from a controller' do + it 'includes values set via content_for' do + helper.content_for(:body_classes) { 'admin' } + + expect(helper.body_classes) + .to match(/admin/) + end + end + end + describe 'locale_direction' do it 'adds rtl body class if locale is Arabic' do I18n.with_locale(:ar) do