You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

772 lines
21 KiB

// Copyright (c) 2018 Tildes contributors <code@tildes.net>
// SPDX-License-Identifier: AGPL-3.0-or-later
// This file should only contain rules that need to be affected by all the
// different themes, defined inside the `use-theme` mixin below.
// Note that all rules inside the mixin will be included in the compiled CSS
// once for each theme, so they should be kept as minimal as possible.
// Each theme should be defined in its own SCSS file, and consist of a SCSS map
// and a unique `body.theme-<name>` selector.
// The `use-theme` mixin is called inside the body.theme-<name> block and takes
// the theme's map as its only argument, applying each defined color available
// in the map. If a color variable is left undefined in the theme's map, it
// will fall back to the default value from `$theme-base` instead.
@mixin use-theme($theme) {
$theme: init-theme($theme);
color: map-get($theme, "foreground-primary");
background-color: map-get($theme, "background-secondary");
// set $is-light as a bool for whether $background-color seems light or dark
$is-light: lightness(map-get($theme, "background-primary")) > 50;
a {
color: map-get($theme, "link");
&:hover {
color: map-get($theme, "link-hover");
}
&:visited {
color: map-get($theme, "link-visited");
}
code {
color: map-get($theme, "link");
&:hover {
text-decoration: underline;
}
}
&:visited code {
color: map-get($theme, "link-visited");
}
}
a.link-user,
a.link-group {
&:visited {
color: map-get($theme, "link");
}
}
a.logged-in-user-alert {
color: map-get($theme, "alert");
&:visited {
color: map-get($theme, "alert");
}
}
@include syntax-highlighting($is-light);
blockquote {
border-color: map-get($theme, "foreground-highlight");
background-color: map-get($theme, "background-secondary");
}
code, pre {
color: map-get($theme, "foreground-highlight");
background-color: map-get($theme, "background-secondary");
}
fieldset {
border-color: map-get($theme, "border");
}
figure {
border-color: map-get($theme, "border");
}
main {
background-color: map-get($theme, "background-primary");
}
section {
border-color: map-get($theme, "border");
}
tbody tr:nth-of-type(2n+1) {
background-color: map-get($theme, "background-secondary");
}
td {
border-color: map-get($theme, "border");
}
th {
border-color: map-get($theme, "foreground-highlight");
}
.btn {
color: map-get($theme, "button");
background-color: transparent;
border-color: map-get($theme, "button");
&:hover {
background-color: rgba(map-get($theme, "button"), 0.2);
}
}
.btn-back-to-top {
background-color: map-get($theme, "background-primary");
&:visited {
color: map-get($theme, "link");
}
}
.btn-comment-collapse {
color: map-get($theme, "foreground-secondary");
border-color: map-get($theme, "border");
&:hover {
color: map-get($theme, "link");
}
}
.btn.btn-link {
color: map-get($theme, "button");
background-color: transparent;
border-color: transparent;
}
.btn.btn-primary {
$is-button-bg-light: lightness(map-get($theme, "button")) > 50;
@if ($is-button-bg-light) {
color: #000;
} @else {
color: #fff;
}
background-color: map-get($theme, "button");
border-color: map-get($theme, "button");
&:hover {
background-color: darken(map-get($theme, "button"), 10%);
border-color: darken(map-get($theme, "button"), 10%);
}
&:visited {
@if ($is-button-bg-light) {
color: #000;
} @else {
color: #fff;
}
}
}
.btn-used {
color: map-get($theme, "button-used");
border-color: darken(map-get($theme, "button-used"), 3%);
&:hover {
background-color: darken(map-get($theme, "button-used"), 3%);
border-color: darken(map-get($theme, "button-used"), 8%);
color: #fff;
}
}
.btn-comment-label-exemplary {
@include labelbutton(map-get($theme, "comment-label-exemplary"));
}
.btn-comment-label-joke {
@include labelbutton(map-get($theme, "comment-label-joke"));
}
.btn-comment-label-noise {
@include labelbutton(map-get($theme, "comment-label-noise"));
}
.btn-comment-label-offtopic {
@include labelbutton(map-get($theme, "comment-label-offtopic"));
}
.btn-comment-label-malice {
@include labelbutton(map-get($theme, "comment-label-malice"));
}
.comment-branch-counter {
color: map-get($theme, "foreground-secondary");
}
.comment-nav-link, .comment-nav-link:visited {
color: map-get($theme, "foreground-secondary");
}
.comment-votes {
color: map-get($theme, "foreground-secondary");
}
.label-comment-exemplary {
@include theme-special-label(map-get($theme, "comment-label-exemplary"), $is-light);
}
.label-comment-joke {
@include theme-special-label(map-get($theme, "comment-label-joke"), $is-light);
}
.label-comment-noise {
@include theme-special-label(map-get($theme, "comment-label-noise"), $is-light);
}
.label-comment-offtopic {
@include theme-special-label(map-get($theme, "comment-label-offtopic"), $is-light);
}
.label-comment-malice {
@include theme-special-label(map-get($theme, "comment-label-malice"), $is-light);
}
%collapsed-theme {
header {
background-color: map-get($theme, "background-primary");
}
}
.is-comment-collapsed:not(:target) {
@extend %collapsed-theme;
}
.is-comment-collapsed-individual:not(:target) {
& > .comment-itself {
@extend %collapsed-theme;
}
}
.comment {
border-color: map-get($theme, "border");
header {
color: map-get($theme, "foreground-highlight");
background-color: map-get($theme, "background-secondary");
}
&[data-comment-depth="0"] {
border-color: map-get($theme, "border");
}
}
.comment:target > .comment-itself {
border-left-color: map-get($theme, "stripe-target");
}
.comment-nav-link {
color: map-get($theme, "foreground-secondary");
}
.divider {
border-color: map-get($theme, "border");
&[data-content]::after {
color: map-get($theme, "foreground-primary");
background-color: map-get($theme, "background-primary");
}
}
.empty-subtitle {
color: map-get($theme, "foreground-secondary");
}
.foreground-secondary {
color: map-get($theme, "foreground-secondary");
}
.form-input {
color: map-get($theme, "foreground-primary");
background-color: map-get($theme, "background-input");
}
.form-input:not(:focus) {
border-color: map-get($theme, "border");
}
.form-select {
border-color: map-get($theme, "border");
&:not([multiple]):not([size]) {
background-color: map-get($theme, "background-input");
}
}
.form-status-error {
color: map-get($theme, "error");
}
.label-topic-tag {
color: map-get($theme, "foreground-middle");
a,
a:hover,
a:visited {
color: map-get($theme, "foreground-middle");
}
}
.label-topic-tag[class*='label-topic-tag-nsfw'] {
@include theme-special-label(map-get($theme, "topic-tag-nsfw"), $is-light);
}
.label-topic-tag[class*='label-topic-tag-spoiler'] {
@include theme-special-label(map-get($theme, "topic-tag-spoiler"), $is-light);
}
.logged-in-user-username, .logged-in-user-username:visited {
color: map-get($theme, "foreground-primary");
}
.message {
border-color: map-get($theme, "border");
header {
color: map-get($theme, "foreground-highlight");
background-color: map-get($theme, "background-secondary");
}
}
.nav .nav-item {
a {
color: map-get($theme, "link");
&:hover {
color: map-get($theme, "link-hover");
}
}
&.active a {
color: map-get($theme, "link");
}
}
.post-button {
color: map-get($theme, "foreground-secondary");
&:hover {
color: map-get($theme, "foreground-extreme");
}
}
.post-button-used {
color: map-get($theme, "button-used");
}
.sidebar-controls {
background-color: map-get($theme, "background-secondary");
}
#sidebar {
background-color: map-get($theme, "background-primary");
}
.site-header-context, .site-header-context:visited {
color: map-get($theme, "foreground-primary");
}
.site-header-logo, .site-header-logo:visited {
color: map-get($theme, "foreground-highlight");
}
.site-header-sidebar-button.badge[data-badge]::after {
background-color: map-get($theme, "alert");
}
.tab-listing-order {
border-color: map-get($theme, "border");
}
.tab .tab-item {
a {
color: map-get($theme, "foreground-primary");
}
&.active a {
color: map-get($theme, "link");
border-bottom-color: map-get($theme, "link");
}
}
.text-secondary {
color: map-get($theme, "foreground-secondary");
}
.text-warning {
color: map-get($theme, "warning");
}
.toast {
color: map-get($theme, "foreground-highlight");
border-color: map-get($theme, "border");
background-color: map-get($theme, "background-secondary");
}
// Toasts should have colored border + text for dark themes, instead of a
// brightly colored background
@if ($is-light == false) {
.toast-warning {
border-color: map-get($theme, "warning");
color: map-get($theme, "warning");
background-color: transparent;
}
} @else {
.toast-warning {
background-color: rgba(map-get($theme, "warning"), 0.9);
border-color: map-get($theme, "warning");
color: map-get($theme, "foreground-extreme");
}
}
.topic {
border-color: map-get($theme, "border");
}
.topic-listing {
& > li:nth-of-type(2n) {
color: mix(map-get($theme, "foreground-primary"), map-get($theme, "foreground-highlight"));
background-color: mix(map-get($theme, "background-primary"), map-get($theme, "background-secondary"));
}
}
.topic-content-metadata {
color: map-get($theme, "foreground-secondary");
}
.topic-full-byline {
color: map-get($theme, "foreground-secondary");
}
.topic-icon {
border-color: map-get($theme, "link");
}
.topic-info {
color: map-get($theme, "foreground-middle");
}
.topic-info-comments-new {
color: map-get($theme, "alert");
}
.topic-log-entry-time {
color: map-get($theme, "foreground-secondary");
}
.topic-text-excerpt {
color: map-get($theme, "foreground-secondary");
summary::after {
color: map-get($theme, "foreground-secondary");
}
&[open] {
color: map-get($theme, "foreground-primary");
}
}
.topic-voting.btn-used {
border-color: transparent;
&:hover {
background-color: darken(map-get($theme, "button"), 3%);
border-color: darken(map-get($theme, "button"), 8%);
}
}
.is-comment-deleted, .is-comment-removed {
color: map-get($theme, "foreground-secondary");
}
.is-comment-mine > .comment-itself {
border-left-color: map-get($theme, "stripe-mine");
}
.is-comment-new {
& > .comment-itself {
border-left-color: map-get($theme, "alert");
}
.comment-text {
color: map-get($theme, "foreground-highlight");
}
}
.is-comment-exemplary {
& > .comment-itself {
border-left-color: map-get($theme, "comment-label-exemplary");
}
}
.is-message-mine,
.is-topic-mine {
border-left-color: map-get($theme, "stripe-mine");
}
.is-topic-official {
border-left-color: map-get($theme, "alert");
h1 {
a, a:visited {
color: map-get($theme, "alert");
}
}
}
}
@mixin theme-special-label($color, $is-light) {
@if $is-light {
background-color: $color;
color: #fff;
a, a:hover, a:visited {
color: #fff;
}
}
@else {
background-color: transparent;
color: $color;
border: 1px solid $color;
a, a:hover, a:visited {
color: $color;
}
}
}
@mixin labelbutton($color) {
color: $color;
border-color: $color;
&:hover {
color: $color;
}
&.btn-used:hover {
background-color: $color;
color: #fff;
}
}
@mixin syntax-highlighting($is-light) {
@if ($is-light) {
.highlight {
.syntax-c { color: #129b12; } // Comment
.syntax-err { color: #333333; } // Error
.syntax-g { color: #333333; } // Generic
.syntax-k { color: #3329c2; } // Keyword
.syntax-l { color: #333333; } // Literal
.syntax-n { color: #333333; } // Name
.syntax-o { color: #859900; } // Operator
.syntax-x { color: #cb4b16; } // Other
.syntax-p { color: #333333; } // Punctuation
.syntax-cm { color: #129b12; } // Comment.Multiline
.syntax-cp { color: #859900; } // Comment.Preproc
.syntax-c1 { color: #129b12; } // Comment.Single
.syntax-cs { color: #859900; } // Comment.Special
.syntax-gd { color: #2aa198; } // Generic.Deleted
.syntax-ge { color: #333333; font-style: italic; } // Generic.Emph
.syntax-gr { color: #dc322f; } // Generic.Error
.syntax-gh { color: #cb4b16; } // Generic.Heading
.syntax-gi { color: #859900; } // Generic.Inserted
.syntax-go { color: #333333; } // Generic.Output
.syntax-gp { color: #333333; } // Generic.Prompt
.syntax-gs { color: #333333; font-weight: bold; } // Generic.Strong
.syntax-gu { color: #cb4b16; } // Generic.Subheading
.syntax-gt { color: #333333; } // Generic.Traceback
.syntax-kc { color: #cb4b16; } // Keyword.Constant
.syntax-kd { color: #268bd2; } // Keyword.Declaration
.syntax-kn { color: #859900; } // Keyword.Namespace
.syntax-kp { color: #859900; } // Keyword.Pseudo
.syntax-kr { color: #268bd2; } // Keyword.Reserved
.syntax-kt { color: #3329c2; } // Keyword.Type
.syntax-ld { color: #333333; } // Literal.Date
.syntax-m { color: #2aa198; } // Literal.Number
.syntax-s { color: #2aa198; } // Literal.String
.syntax-na { color: #333333; } // Name.Attribute
.syntax-nb { color: #B58900; } // Name.Builtin
.syntax-nc { color: #268bd2; } // Name.Class
.syntax-no { color: #cb4b16; } // Name.Constant
.syntax-nd { color: #268bd2; } // Name.Decorator
.syntax-ni { color: #cb4b16; } // Name.Entity
.syntax-ne { color: #cb4b16; } // Name.Exception
.syntax-nf { color: #268bd2; } // Name.Function
.syntax-nl { color: #333333; } // Name.Label
.syntax-nn { color: #333333; } // Name.Namespace
.syntax-nx { color: #333333; } // Name.Other
.syntax-py { color: #333333; } // Name.Property
.syntax-nt { color: #268bd2; } // Name.Tag
.syntax-nv { color: #268bd2; } // Name.Variable
.syntax-ow { color: #859900; } // Operator.Word
.syntax-w { color: #333333; } // Text.Whitespace
.syntax-mf { color: #2aa198; } // Literal.Number.Float
.syntax-mh { color: #2aa198; } // Literal.Number.Hex
.syntax-mi { color: #2aa198; } // Literal.Number.Integer
.syntax-mo { color: #2aa198; } // Literal.Number.Oct
.syntax-sb { color: #129b12; } // Literal.String.Backtick
.syntax-sc { color: #2aa198; } // Literal.String.Char
.syntax-sd { color: #333333; } // Literal.String.Doc
.syntax-s2 { color: #2aa198; } // Literal.String.Double
.syntax-se { color: #cb4b16; } // Literal.String.Escape
.syntax-sh { color: #333333; } // Literal.String.Heredoc
.syntax-si { color: #2aa198; } // Literal.String.Interpol
.syntax-sx { color: #2aa198; } // Literal.String.Other
.syntax-sr { color: #dc322f; } // Literal.String.Regex
.syntax-s1 { color: #2aa198; } // Literal.String.Single
.syntax-ss { color: #2aa198; } // Literal.String.Symbol
.syntax-bp { color: #268bd2; } // Name.Builtin.Pseudo
.syntax-vc { color: #268bd2; } // Name.Variable.Class
.syntax-vg { color: #268bd2; } // Name.Variable.Global
.syntax-vi { color: #268bd2; } // Name.Variable.Instance
.syntax-il { color: #2aa198; } // Literal.Number.Integer.Long
}
} @else {
.highlight {
.syntax-c { color: #759299; }
.syntax-err { color: #93a1a1; }
.syntax-g { color: #93a1a1; }
.syntax-k { color: #859900; }
.syntax-l { color: #93a1a1; }
.syntax-n { color: #93a1a1; }
.syntax-o { color: #859900; }
.syntax-x { color: #e9662f; }
.syntax-p { color: #93a1a1; }
.syntax-cm { color: #759299; }
.syntax-cp { color: #859900; }
.syntax-c1 { color: #759299; }
.syntax-cs { color: #859900; }
.syntax-gd { color: #2aa198; }
.syntax-ge { color: #93a1a1; font-style: italic; }
.syntax-gr { color: #e8625f; }
.syntax-gh { color: #e9662f; }
.syntax-gi { color: #859900; }
.syntax-go { color: #93a1a1; }
.syntax-gp { color: #93a1a1; }
.syntax-gs { color: #93a1a1; font-weight: bold; }
.syntax-gu { color: #e9662f; }
.syntax-gt { color: #93a1a1; }
.syntax-kc { color: #e9662f; }
.syntax-kd { color: #3294da; }
.syntax-kn { color: #859900; }
.syntax-kp { color: #859900; }
.syntax-kr { color: #3294da; }
.syntax-kt { color: #e8625f; }
.syntax-ld { color: #93a1a1; }
.syntax-m { color: #2aa198; }
.syntax-s { color: #2aa198; }
.syntax-na { color: #93a1a1; }
.syntax-nb { color: #B58900; }
.syntax-nc { color: #3294da; }
.syntax-no { color: #e9662f; }
.syntax-nd { color: #3294da; }
.syntax-ni { color: #e9662f; }
.syntax-ne { color: #e9662f; }
.syntax-nf { color: #3294da; }
.syntax-nl { color: #93a1a1; }
.syntax-nn { color: #93a1a1; }
.syntax-nx { color: #93a1a1; }
.syntax-py { color: #93a1a1; }
.syntax-nt { color: #3294da; }
.syntax-nv { color: #3294da; }
.syntax-ow { color: #859900; }
.syntax-w { color: #93a1a1; }
.syntax-mf { color: #2aa198; }
.syntax-mh { color: #2aa198; }
.syntax-mi { color: #2aa198; }
.syntax-mo { color: #2aa198; }
.syntax-sb { color: #759299; }
.syntax-sc { color: #2aa198; }
.syntax-sd { color: #93a1a1; }
.syntax-s2 { color: #2aa198; }
.syntax-se { color: #e9662f; }
.syntax-sh { color: #93a1a1; }
.syntax-si { color: #2aa198; }
.syntax-sx { color: #2aa198; }
.syntax-sr { color: #e8625f; }
.syntax-s1 { color: #2aa198; }
.syntax-ss { color: #2aa198; }
.syntax-bp { color: #3294da; }
.syntax-vc { color: #3294da; }
.syntax-vg { color: #3294da; }
.syntax-vi { color: #3294da; }
.syntax-il { color: #2aa198; }
}
}
}
@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"
"warning"
;
@each $key in $essential-keys {
@if (not map-has-key($theme, $key)) {
@error "Missing essential key in theme: #{$key}";
}
}
// set $is-light as a bool for whether background-primary seems light or dark
$is-light: lightness(map-get($theme, "background-primary")) > 50;
// 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");
$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");
$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 background and black on a light one
$foreground-extreme: map-get($theme, "foreground-extreme");
$foreground-extreme: if($is-light, #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;
@return 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,
"topic-tag-nsfw": $topic-tag-nsfw,
"topic-tag-spoiler": $topic-tag-spoiler,
));
}