]> git.draconx.ca Git - homepage.git/blob - lib/colourmap.scss
Implement dark mode without using CSS variables.
[homepage.git] / lib / colourmap.scss
1 // Nick's web site: SCSS colourmap helpers for automatic light/dark styles.
2 //
3 // Copyright © 2022 Nick Bowler
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
18 // database of colour names
19 $colourmap: ();
20
21 // database of combination CSS properties that should be translated to
22 // colour-only properties (e.g., border-top -> border-top-color).
23 $_colourpropmap:
24     ( border-top: border-top-color
25     , border-bottom: border-bottom-color
26     , border-left: border-left-color
27     , border-right: border-right-color
28     , border: border-color
29     );
30
31 // Define a named set of colours.  Pass keyword arguments with each keyword
32 // being a colour name and the argument is a list of one or two colours.
33 //
34 // When two colours are specified, the first should be for "light" backgrounds
35 // and the second for "dark".
36 //
37 // For example:
38 //
39 //   @include defcolours($bg: white black, $fg: black white);
40 @mixin defcolours($args...) {
41     @each $colour, $list in keywords($args) {
42         $colourmap: map-merge($colourmap, ($colour: $list)) !global;
43     }
44 }
45
46 // Obtain the colour value for a previously-defined colour name.  By
47 // default, its primary (light) colour value is returned; this can be
48 // changed with the $num keyword parameter.
49 //
50 // The $pre and $post keyword arguments may be used to supplement the
51 // result with additional tokens either before or after the colour
52 // value, respectively, as might be used for combined properties
53 // such as border, outline, etc.
54 @function getcolour($colour, $pre: (), $post: (), $num: 1) {
55     @return join(append($pre, nth(map-get($colourmap, $colour), $num)), $post);
56 }
57
58 @mixin usecolour_dark_($prop, $colour, $pre: (), $post: ()) {
59     @if (length(map-get($colourmap, $colour)) > 1) {
60         $transprop: map-get($_colourpropmap, $prop);
61         @if $transprop {
62             #{$transprop}: getcolour($colour, $num: 2)
63         } @else {
64             #{$prop}: getcolour($colour, $pre, $post, 2);
65         }
66     }
67 }
68
69 // Sets the given CSS property to the specified colour name.  The $pre
70 // and $post keyword arguments may be used to supplement the property
71 // value, as with the getcolour function.
72 //
73 // For two-value colours, the property is set using CSS variables to
74 // adapt the colour based on the user's preference for a light or dark
75 // background.  The rules will fall back to the static (light) colour
76 // if CSS variables are not supported.
77 //
78 // For example:
79 //
80 //   @include usecolour(border, fg, $pre: solid 1px);
81 @mixin usecolour($prop, $colour, $pre: (), $post: ()) {
82     #{$prop}: getcolour($colour, $pre, $post);
83     @at-root {
84         @media (prefers-color-scheme: dark) {
85             & { @include usecolour_dark_($prop, $colour, $pre, $post); }
86         }
87     }
88 }
89
90 // Convenience helper to assign multiple colour properties at once, for
91 // the common case where the $pre and $post arguments to usecolour are
92 // not required.
93 //
94 // Takes any number of keyword arguments with the keyword being the
95 // property name and the value being the colour name.
96 //
97 // For example:
98 //
99 //   @include usecolours($background-color: bg, $color: fg);
100 @mixin usecolours($args...) {
101     @each $prop, $colour in keywords($args) {
102         #{$prop}: getcolour($colour);
103     }
104     @at-root {
105         @media (prefers-color-scheme: dark) {
106             & {
107                 @each $prop, $colour in keywords($args) {
108                     @include usecolour_dark_($prop, $colour);
109                 }
110             }
111         }
112     }
113 }