Commit 2d023cd6 authored by Bauke's avatar Bauke 🐦 Committed by Deimos

Use CSS custom properties for theming

parent 68870119
......@@ -45,6 +45,7 @@
// Note: if you add a new theme, you may also want to add a new theme-color
// meta tag inside the base.jinja2 template, so mobile browsers can match
@import "themes/theme_mixins";
@import "themes/theme_base";
@import "themes/default";
@import "themes/black";
......
......@@ -49,4 +49,8 @@ body.theme-atom-one-dark {
@include use-theme($theme-atom-one-dark);
}
@include theme-preview-block($theme-atom-one-dark, "atom-one-dark");
@include theme-preview-block(
"atom-one-dark",
map-get($theme-atom-one-dark, "foreground-primary"),
map-get($theme-atom-one-dark, "background-primary")
);
......@@ -28,4 +28,8 @@ body.theme-black {
@include use-theme($theme-black);
}
@include theme-preview-block($theme-black, "black");
@include theme-preview-block(
"black",
map-get($theme-black, "foreground-primary"),
map-get($theme-black, "background-primary")
);
......@@ -3,6 +3,7 @@ $default-theme: (
"alert": #e66b00,
"background-primary": #fff,
"background-secondary": #eee,
"black": #000,
"border": #ccc,
"button": #1460aa,
"comment-label-exemplary": #1460aa,
......@@ -26,11 +27,15 @@ $default-theme: (
"syntax-string": #2aa198, // Solarized
"topic-tag-spoiler": #e66b00,
"warning": #e66b00,
"white": #fff
);
// define the default theme using the base values
body {
@include use-theme($default-theme);
}
@include theme-preview-block($default-theme, "white");
@include theme-preview-block(
"white",
map-get($default-theme, "foreground-primary"),
map-get($default-theme, "background-primary")
);
......@@ -51,4 +51,8 @@ body.theme-dracula {
@include use-theme($theme-dracula);
}
@include theme-preview-block($theme-dracula, "dracula");
@include theme-preview-block(
"dracula",
map-get($theme-dracula, "foreground-primary"),
map-get($theme-dracula, "background-primary")
);
......@@ -92,47 +92,63 @@ $gruvbox-base: (
);
// Dark theme definition
$gruvbox-dark: (
"alert": $gb-dm-light-orange,
"background-input": $gb-dm-bg1,
"background-primary": $gb-dm-bg0,
"background-secondary": $gb-dm-bg1,
"border": $gb-dm-bg2,
"error": $gb-dm-light-red,
"foreground-highlight": $gb-dm-fg0,
"foreground-primary": $gb-dm-fg1,
"foreground-secondary": $gb-dm-fg4,
"link": $gb-dm-light-blue,
"link-visited": $dark-purple,
"success": $dark-green,
"warning": $gb-dm-light-yellow,
);
$theme-gruvbox-dark:
map-merge(
$gruvbox-base,
(
"alert": $gb-dm-light-orange,
"background-input": $gb-dm-bg1,
"background-primary": $gb-dm-bg0,
"background-secondary": $gb-dm-bg1,
"border": $gb-dm-bg2,
"error": $gb-dm-light-red,
"foreground-highlight": $gb-dm-fg0,
"foreground-primary": $gb-dm-fg1,
"foreground-secondary": $gb-dm-fg4,
"link": $gb-dm-light-blue,
"link-visited": $dark-purple,
"success": $dark-green,
"warning": $gb-dm-light-yellow,
)
);
body.theme-gruvbox-dark {
@include use-theme(map-merge($gruvbox-base, $gruvbox-dark));
@include use-theme($theme-gruvbox-dark);
}
@include theme-preview-block(map-merge($gruvbox-base, $gruvbox-dark), "gruvbox-dark");
@include theme-preview-block(
"gruvbox-dark",
map-get($theme-gruvbox-dark, "foreground-primary"),
map-get($theme-gruvbox-dark, "background-primary")
);
// Light theme definition
$gruvbox-light: (
"alert": $dark-orange,
"background-input": $gb-lm-bg1,
"background-primary": $gb-lm-bg0,
"background-secondary": $gb-lm-bg1,
"border": $gb-lm-bg2,
"error": $dark-red,
"foreground-highlight": $gb-lm-fg0,
"foreground-primary": $gb-lm-fg1,
"foreground-secondary": $gb-lm-fg4,
"link": $gb-lm-light-blue,
"link-visited": $gb-lm-light-purple,
"success": $dark-green,
"warning": $gb-lm-light-yellow,
);
$theme-gruvbox-light:
map-merge(
$gruvbox-base,
(
"alert": $dark-orange,
"background-input": $gb-lm-bg1,
"background-primary": $gb-lm-bg0,
"background-secondary": $gb-lm-bg1,
"border": $gb-lm-bg2,
"error": $dark-red,
"foreground-highlight": $gb-lm-fg0,
"foreground-primary": $gb-lm-fg1,
"foreground-secondary": $gb-lm-fg4,
"link": $gb-lm-light-blue,
"link-visited": $gb-lm-light-purple,
"success": $dark-green,
"warning": $gb-lm-light-yellow,
)
);
body.theme-gruvbox-light {
@include use-theme(map-merge($gruvbox-base, $gruvbox-light));
@include use-theme($theme-gruvbox-light);
}
@include theme-preview-block(map-merge($gruvbox-base, $gruvbox-light), "gruvbox-light");
@include theme-preview-block(
"gruvbox-light",
map-get($theme-gruvbox-light, "foreground-primary"),
map-get($theme-gruvbox-light, "background-primary")
);
......@@ -34,7 +34,7 @@ $fg-light: $base0;
$fg-lightest: $base1;
// Shared between both "light" and "dark" variants
$solarized-base: (
$theme-solarized-base: (
"alert": $orange,
"comment-label-exemplary": $blue,
"comment-label-joke": $green,
......@@ -55,43 +55,53 @@ $solarized-base: (
);
// Dark theme definition
$solarized-dark: (
"background-input": #001f27,
"background-primary": $bg-darkest,
"background-secondary": $bg-dark,
"border": #33555e,
"foreground-highlight": $fg-lightest,
"foreground-primary": $fg-light,
"foreground-secondary": $fg-darkest,
);
$theme-solarized-dark:
map-merge(
$theme-solarized-base,
(
"background-input": #001f27,
"background-primary": $bg-darkest,
"background-secondary": $bg-dark,
"border": #33555e,
"foreground-highlight": $fg-lightest,
"foreground-primary": $fg-light,
"foreground-secondary": $fg-darkest,
)
);
body.theme-solarized-dark {
@include use-theme(map-merge($solarized-base, $solarized-dark));
@include use-theme($theme-solarized-dark);
}
@include theme-preview-block(
map-merge($solarized-base, $solarized-dark),
"solarized-dark"
"solarized-dark",
map-get($theme-solarized-dark, "foreground-primary"),
map-get($theme-solarized-dark, "background-primary")
);
// Light theme definition
$solarized-light: (
"background-input": #fefbf1,
"background-primary": $bg-lightest,
"background-secondary": $bg-light,
"border": #cbc5b6,
"foreground-highlight": $fg-darkest,
"foreground-primary": $fg-dark,
"foreground-secondary": $fg-lightest,
"stripe-target": $yellow,
"warning": $orange,
);
$theme-solarized-light:
map-merge(
$theme-solarized-base,
(
"background-input": #fefbf1,
"background-primary": $bg-lightest,
"background-secondary": $bg-light,
"border": #cbc5b6,
"foreground-highlight": $fg-darkest,
"foreground-primary": $fg-dark,
"foreground-secondary": $fg-lightest,
"stripe-target": $yellow,
"warning": $orange,
)
);
body.theme-solarized-light {
@include use-theme(map-merge($solarized-base, $solarized-light));
@include use-theme($theme-solarized-light);
}
@include theme-preview-block(
map-merge($solarized-base, $solarized-light),
"solarized-light"
"solarized-light",
map-get($theme-solarized-light, "foreground-primary"),
map-get($theme-solarized-light, "background-primary")
);
This diff is collapsed.
// Copyright (c) 2020 Tildes contributors <[email protected]>
// SPDX-License-Identifier: AGPL-3.0-or-later
@mixin label-button($color) {
color: $color;
border-color: $color;
&:hover {
color: $color;
}
&.btn-used:hover {
background-color: $color;
color: var(--white);
}
}
@mixin theme-preview-block($name, $foreground, $background) {
.theme-preview-block-#{$name} {
background-color: $background;
color: $foreground;
border: 1px solid;
}
}
@mixin theme-special-label($background-color, $foreground-color, $border-color) {
background-color: $background-color;
color: $foreground-color;
border: 1px solid $border-color;
padding: 0 0.2rem;
line-height: 0.9rem;
a,
a:hover,
a:visited {
color: $foreground-color;
}
}
@function map-get-fallback($map, $preferred-key, $fallback-key) {
// map-get that will fall back to a second key if the first isn't set
@if (map-has-key($map, $preferred-key)) {
@return map-get($map, $preferred-key);
}
@return map-get($map, $fallback-key);
}
@function init-theme($theme) {
// check to make sure the theme has all of the essential colors set
$essential-keys:
"alert"
"background-primary"
"background-secondary"
"comment-label-exemplary"
"comment-label-joke"
"comment-label-noise"
"comment-label-offtopic"
"comment-label-malice"
"error"
"foreground-primary"
"foreground-secondary"
"link"
"link-visited"
"success"
"warning";
@each $key in $essential-keys {
@if (not map-has-key($theme, $key)) {
@error "Missing essential key in theme: #{$key}";
}
}
// colors that simply fall back to another if not defined
$background-input: map-get-fallback($theme, "background-input", "background-primary");
$border: map-get-fallback($theme, "border", "foreground-secondary");
$button: map-get-fallback($theme, "button", "link");
$button-used: map-get-fallback($theme, "button-used", "link-visited");
// stylelint-disable-next-line
$foreground-highlight: map-get-fallback($theme, "foreground-highlight", "foreground-primary");
$stripe-mine: map-get-fallback($theme, "stripe-mine", "link-visited");
$stripe-target: map-get-fallback($theme, "stripe-target", "warning");
$syntax-builtin: map-get-fallback($theme, "syntax-builtin", "foreground-primary");
$syntax-comment: map-get-fallback($theme, "syntax-comment", "foreground-primary");
$syntax-constant: map-get-fallback($theme, "syntax-constant", "foreground-primary");
$syntax-keyword: map-get-fallback($theme, "syntax-keyword", "foreground-primary");
$syntax-literal: map-get-fallback($theme, "syntax-literal", "foreground-primary");
$syntax-string: map-get-fallback($theme, "syntax-string", "foreground-primary");
$topic-tag-nsfw: map-get-fallback($theme, "topic-tag-nsfw", "error");
$topic-tag-spoiler: map-get-fallback($theme, "topic-tag-spoiler", "warning");
// foreground-extreme: if not defined, use white on a dark bg and black on a light one
$foreground-extreme: map-get($theme, "foreground-extreme");
$foreground-extreme:
choose-by-brightness(
map-get($theme, "background-primary"),
#000,
#fff,
) !default;
// foreground-middle: if not defined, mix foreground-primary and foreground-secondary
$foreground-middle: map-get($theme, "foreground-middle");
$foreground-middle:
mix(
map-get($theme, "foreground-primary"),
map-get($theme, "foreground-secondary")
) !default;
// link-hover: if not defined, darken the link color slightly
$link-hover: map-get($theme, "link-hover");
$link-hover: darken(map-get($theme, "link"), 5%) !default;
$new-theme:
map-merge(
$theme,
(
"background-input": $background-input,
"border": $border,
"button": $button,
"button-used": $button-used,
"foreground-extreme": $foreground-extreme,
"foreground-highlight": $foreground-highlight,
"foreground-middle": $foreground-middle,
"link-hover": $link-hover,
"stripe-mine": $stripe-mine,
"stripe-target": $stripe-target,
"syntax-builtin": $syntax-builtin,
"syntax-comment": $syntax-comment,
"syntax-constant": $syntax-constant,
"syntax-keyword": $syntax-keyword,
"syntax-literal": $syntax-literal,
"syntax-string": $syntax-string,
"topic-tag-nsfw": $topic-tag-nsfw,
"topic-tag-spoiler": $topic-tag-spoiler,
)
);
@return map-merge($default-theme, $new-theme);
}
@mixin use-theme($selected-theme) {
$theme: init-theme($selected-theme);
$is-light: is-color-bright(map-get($theme, "background-primary"));
// When creating CSS custom properties and using any of Sass' capabilities
// you'll have to interpolate it with the Sass syntax `#{...}` as seen below.
--alert: #{map-get($theme, "alert")};
--background-input: #{map-get($theme, "background-input")};
--background-mixed:
#{mix(
map-get($theme, "background-primary"),
map-get($theme, "background-secondary")
)};
--background-primary: #{map-get($theme, "background-primary")};
--background-secondary: #{map-get($theme, "background-secondary")};
--border: #{map-get($theme, "border")};
--button: #{map-get($theme, "button")};
--button-by-brightness:
#{choose-by-brightness(
map-get($theme, "button"),
map-get($theme, "black"),
map-get($theme, "white")
)};
--button-transparent: #{rgba(map-get($theme, "button"), 0.2)};
--button-darkened-3: #{darken(map-get($theme, "button"), 3%)};
--button-darkened-8: #{darken(map-get($theme, "button"), 8%)};
--button-darkened-10: #{darken(map-get($theme, "button"), 10%)};
--button-used: #{map-get($theme, "button-used")};
--button-used-darkened-3: #{darken(map-get($theme, "button-used"), 3%)};
--button-used-darkened-8: #{darken(map-get($theme, "button-used"), 8%)};
--error: #{map-get($theme, "error")};
--error-by-brightness:
#{choose-by-brightness(
map-get($theme, "error"),
map-get($theme, "black"),
map-get($theme, "white")
)};
--foreground-extreme:
#{choose-by-brightness(
map-get($theme, "background-primary"),
map-get($theme, "black"),
map-get($theme, "white")
)};
--foreground-highlight: #{map-get($theme, "foreground-highlight")};
--foreground-mixed:
#{mix(
map-get($theme, "foreground-primary"),
map-get($theme, "foreground-highlight")
)};
--foreground-primary: #{map-get($theme, "foreground-primary")};
--foreground-secondary: #{map-get($theme, "foreground-secondary")};
--link: #{map-get($theme, "link")};
--link-hover: #{darken(map-get($theme, "link"), 5%)};
--link-visited: #{map-get($theme, "link-visited")};
--stripe-mine: #{map-get($theme, "stripe-mine")};
--stripe-target: #{map-get($theme, "stripe-target")};
--success: #{map-get($theme, "success")};
--syntax-builtin: #{map-get($theme, "syntax-builtin")};
--syntax-comment: #{map-get($theme, "syntax-comment")};
--syntax-constant: #{map-get($theme, "syntax-constant")};
--syntax-keyword: #{map-get($theme, "syntax-keyword")};
--syntax-literal: #{map-get($theme, "syntax-literal")};
--syntax-string: #{map-get($theme, "syntax-string")};
// Colors for the special topic tags
@if $is-light {
--topic-tag-nsfw: #{map-get($theme, "topic-tag-nsfw")};
--topic-tag-nsfw-foreground: #{map-get($theme, "white")};
--topic-tag-nsfw-border: transparent;
--topic-tag-spoiler: #{map-get($theme, "topic-tag-spoiler")};
--topic-tag-spoiler-foreground: #{map-get($theme, "white")};
--topic-tag-spoiler-border: transparent;
} @else {
--topic-tag-nsfw: transparent;
--topic-tag-nsfw-foreground: #{map-get($theme, "topic-tag-nsfw")};
--topic-tag-nsfw-border: #{map-get($theme, "topic-tag-nsfw")};
--topic-tag-spoiler: transparent;
--topic-tag-spoiler-foreground: #{map-get($theme, "topic-tag-spoiler")};
--topic-tag-spoiler-border: #{map-get($theme, "topic-tag-spoiler")};
}
--warning: #{map-get($theme, "warning")};
// Colors for warning toasts
@if $is-light {
--warning-background: #{rgba(map-get($theme, "warning"), 0.9)};
--warning-foreground: #{map-get($theme, "black")};
} @else {
--warning-background: transparent;
--warning-foreground: #{map-get($theme, "warning")};
}
// Colors that were hardcoded in previously.
--white: #{map-get($theme, "white")};
// Variables for the comment labels.
@if $is-light {
--background-label-exemplary: #{map-get($theme, "comment-label-exemplary")};
--background-label-joke: #{map-get($theme, "comment-label-joke")};
--background-label-noise: #{map-get($theme, "comment-label-noise")};
--background-label-offtopic: #{map-get($theme, "comment-label-offtopic")};
--background-label-malice: #{map-get($theme, "comment-label-malice")};
--comment-label-exemplary: #{map-get($theme, "comment-label-exemplary")};
--comment-label-joke: #{map-get($theme, "comment-label-joke")};
--comment-label-noise: #{map-get($theme, "comment-label-noise")};
--comment-label-offtopic: #{map-get($theme, "comment-label-offtopic")};
--comment-label-malice: #{map-get($theme, "comment-label-malice")};
--foreground-label-exemplary: #{map-get($theme, "white")};
--foreground-label-joke: #{map-get($theme, "white")};
--foreground-label-noise: #{map-get($theme, "white")};
--foreground-label-offtopic: #{map-get($theme, "white")};
--foreground-label-malice: #{map-get($theme, "white")};
} @else {
--background-label-exemplary: transparent;
--background-label-joke: transparent;
--background-label-noise: transparent;
--background-label-offtopic: transparent;
--background-label-malice: transparent;
--comment-label-exemplary: #{map-get($theme, "comment-label-exemplary")};
--comment-label-joke: #{map-get($theme, "comment-label-joke")};
--comment-label-noise: #{map-get($theme, "comment-label-noise")};
--comment-label-offtopic: #{map-get($theme, "comment-label-offtopic")};
--comment-label-malice: #{map-get($theme, "comment-label-malice")};
--foreground-label-exemplary: #{map-get($theme, "comment-label-exemplary")};
--foreground-label-joke: #{map-get($theme, "comment-label-joke")};
--foreground-label-noise: #{map-get($theme, "comment-label-noise")};
--foreground-label-offtopic: #{map-get($theme, "comment-label-offtopic")};
--foreground-label-malice: #{map-get($theme, "comment-label-malice")};
}
}
@mixin syntax-highlighting {
.highlight {
.syntax-c {
color: var(--syntax-comment);
} // Comment
.syntax-err {
color: var(--foreground);
} // Error
.syntax-g {
color: var(--foreground);
} // Generic
.syntax-k {
color: var(--syntax-keyword);
} // Keyword
.syntax-l {
color: var(--foreground);
} // Literal
.syntax-n {
color: var(--foreground);
} // Name
.syntax-o {
color: var(--syntax-comment);
} // Operator
.syntax-x {
color: var(--syntax-constant);
} // Other
.syntax-p {
color: var(--foreground);
} // Punctuation
.syntax-cm {
color: var(--syntax-comment);
} // Comment.Multiline
.syntax-cp {
color: var(--syntax-comment);
} // Comment.Preproc
.syntax-c1 {
color: var(--syntax-comment);
} // Comment.Single
.syntax-cs {
color: var(--syntax-comment);
} // Comment.Special
.syntax-gd {
color: var(--syntax-comment);
} // Generic.Deleted
.syntax-ge {
color: var(--foreground);
font-style: italic;
} // Generic.Emph
.syntax-gr {
color: var(--syntax-constant);
} // Generic.Error
.syntax-gh {
color: var(--syntax-constant);
} // Generic.Heading
.syntax-gi {
color: var(--syntax-comment);
} // Generic.Inserted