+// 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);
+ }
+ }
+}