mirror of
https://github.com/glitch-soc/mastodon.git
synced 2025-12-11 06:20:42 +00:00
Handle dark/light/contrast theme modes in common CSS (#37095)
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
<html class="no-reduce-motion">
|
||||
<html class="no-reduce-motion theme-light">
|
||||
</html>
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,2 @@
|
||||
@use 'mastodon/css_variables';
|
||||
@use 'mastodon/variables';
|
||||
@use 'common';
|
||||
|
||||
html {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
@use 'fonts/roboto-mono';
|
||||
|
||||
@use 'mastodon/reset';
|
||||
@use 'mastodon/theme';
|
||||
@use 'mastodon/basics';
|
||||
@use 'mastodon/branding';
|
||||
@use 'mastodon/containers';
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
@use 'mastodon/css_variables';
|
||||
@use 'mastodon/variables';
|
||||
@use 'common';
|
||||
@use 'contrast/diff';
|
||||
|
||||
html {
|
||||
color-scheme: dark;
|
||||
}
|
||||
@use 'mastodon/high-contrast';
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
@use 'mastodon-light/css_variables';
|
||||
@use 'mastodon/variables' with (
|
||||
$emojis-requiring-inversion: 'chains'
|
||||
);
|
||||
@use 'common';
|
||||
|
||||
html {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@use 'variables' as *;
|
||||
|
||||
body.rtl {
|
||||
html.rtl {
|
||||
direction: rtl;
|
||||
|
||||
.reactions-bar {
|
||||
|
||||
27
app/javascript/styles/mastodon/theme/_base.scss
Normal file
27
app/javascript/styles/mastodon/theme/_base.scss
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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%;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
48
app/javascript/styles/mastodon/theme/index.scss
Normal file
48
app/javascript/styles/mastodon/theme/index.scss
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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' }/
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user