]> git.draconx.ca Git - homepage.git/blobdiff - lib/colourmap.scss
Automatically adjust colour scheme for "dark mode".
[homepage.git] / lib / colourmap.scss
diff --git a/lib/colourmap.scss b/lib/colourmap.scss
new file mode 100644 (file)
index 0000000..62468d9
--- /dev/null
@@ -0,0 +1,118 @@
+// Nick's web site: SCSS colourmap helpers for automatic light/dark styles.
+//
+// Copyright © 2022 Nick Bowler
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+// database of colour names
+$colourmap: ();
+
+// database of combination CSS properties that should be translated to
+// colour-only properties (e.g., border-top -> border-top-color).
+$_colourpropmap:
+    ( border-top: border-top-color
+    , border-bottom: border-bottom-color
+    , border-left: border-left-color
+    , border-right: border-right-color
+    , border: border-color
+    );
+
+// Define a named set of colours.  Pass keyword arguments with each keyword
+// being a colour name and the argument is a list of one or two colours.
+//
+// When two colours are specified, the first should be for "light" backgrounds
+// and the second for "dark".
+//
+// For example:
+//
+//   @include defcolours($bg: white black, $fg: black white);
+@mixin defcolours($args...) {
+    :root {
+        @each $colour, $list in keywords($args) {
+            $colourmap: map-merge($colourmap, ($colour: $list)) !global;
+            @if length($list) > 1 {
+                #{--colour- + $colour}: nth($list, 1);
+            }
+        }
+    }
+    @media (prefers-color-scheme: dark) {
+        :root {
+            @each $colour, $list in keywords($args) {
+                @if length($list) > 1 {
+                    #{--colour- + $colour}: nth($list, 2);
+                }
+            }
+        }
+    }
+}
+
+// For the given previously-defined colour name, returns its value from
+// primary (light) colour scheme.
+//
+// The $pre and $post keyword arguments may be used to supplement the
+// result with additional tokens either before or after the colour
+// value, respectively, as might be used for combined properties
+// such as border, outline, etc.
+@function getcolour($colour, $pre: (), $post: ()) {
+    @return join(append($pre, nth(map-get($colourmap, $colour), 1)), $post);
+}
+
+@mixin usecolour_var_($prop, $colour, $pre: (), $post: ()) {
+    @if (length(map-get($colourmap, $colour)) > 1) {
+        $transprop: map-get($_colourpropmap, $prop);
+        @if $transprop {
+            #{$transprop}: var(--colour- + $colour)
+        } @else {
+            #{$prop}: join(append($pre, var(--colour- + $colour)), $post);
+        }
+    }
+}
+
+// Sets the given CSS property to the specified colour name.  The $pre
+// and $post keyword arguments may be used to supplement the property
+// value, as with the getcolour function.
+//
+// For two-value colours, the property is set using CSS variables to
+// adapt the colour based on the user's preference for a light or dark
+// background.  The rules will fall back to the static (light) colour
+// if CSS variables are not supported.
+//
+// For example:
+//
+//   @include usecolour(border, fg, $pre: solid 1px);
+@mixin usecolour($prop, $colour, $pre: (), $post: ()) {
+    #{$prop}: getcolour($colour, $pre, $post);
+    @at-root & { @include usecolour_var_($prop, $colour, $pre, $post); }
+}
+
+// Convenience helper to assign multiple colour properties at once, for
+// the common case where the $pre and $post arguments to usecolour are
+// not required.
+//
+// Takes any number of keyword arguments with the keyword being the
+// property name and the value being the colour name.
+//
+// For example:
+//
+//   @include usecolours($background-color: bg, $color: fg);
+@mixin usecolours($args...) {
+    @each $prop, $colour in keywords($args) {
+        #{$prop}: getcolour($colour);
+    }
+    @at-root & {
+        @each $prop, $colour in keywords($args) {
+            @include usecolour_var_($prop, $colour);
+        }
+    }
+}