#+TITLE: Theme library for Nyxt
#+PROPERTY: :results silent

* Overview

This general purpose theme library provides the means to customize the colors
and fonts of Nyxt's UI.  Besides exposing the set of tweakable options,
opinionated defaults are provided.

Owing to its flexibility, it can be used to theme other projects.

** Palette's rationale

5 conceptual color groups are defined:

- ~background~ :: color that covers large surfaces.
- ~primary~ :: color of interface elements that contrasts with ~background~;
  neutral and harmonious relative to ~secondary~.
- ~secondary~ :: color of interface elements that contrasts with ~background~'s
  foreground; neutral and harmonious relative to ~primary~.
- ~accent~ :: color that highlights actionable interface elements or draws
  particular attention.
- ~warning~ :: color that highlights errors or invalid operations.

Each color has a corresponding on-color pair.  In other words, each color pair
features a foreground and background value.  This eases attaining constant
contrast ratio among all pairs.

On top of that, these pairs are doubled by another pair (~alt-color~,
~on-alt-color~), that provides a nuanced variation of (~color~, ~on-color~).

For instance, within the conceptual group of background colors, the following
are available: ~background~, ~on-background~, ~alt-background~,
~on-alt-background~.

This amounts to 20 available colors in the palette, not necessarily distinct.

This rationale is loosely based on [[https://m2.material.io/design/material-theming/implementing-your-theme.html][Google Material Design Guidelines]].

** Fonts

~font-family~ is the only configurable option at the moment.

# TODO Define both a title font and text font.

** Example

#+begin_src lisp
;; When you omit the values for on-colors, they're automatically set to
;; either black or white, according to what achieves a better contrast.
(defvar my-theme (make-instance 'theme:theme
                                :background-color "black"
                                :primary-color "#e5cfef"
                                :secondary-color "#7042a2"
                                :accent-color "#b8e2b8"
                                :font-family "Iosevka"))
;; =>
;; BACKGROUND-COLOR        = "black"
;; ON-BACKGROUND-COLOR     = "white"
;; BACKGROUND-ALT-COLOR    = "#eeeeee"
;; ON-BACKGROUND-ALT-COLOR = "black"
;; PRIMARY-COLOR           = "#e5cfef"
;; ON-PRIMARY-COLOR        = "black"
;; PRIMARY-ALT-COLOR       = "#686868"
;; ON-PRIMARY-ALT-COLOR    = "white"
;; SECONDARY-COLOR         = "#7042a2"
;; ON-SECONDARY-COLOR      = "white"
;; SECONDARY-ALT-COLOR     = "#909090"
;; ON-SECONDARY-ALT-COLOR  = "black"
;; ACCENT-COLOR            = "#b8e2b8"
;; ON-ACCENT-COLOR         = "black"
;; ACCENT-ALT-COLOR        = "#178DCC"
;; ON-ACCENT-ALT-COLOR     = "black"
;; WARNING-COLOR           = "#AF1923"
;; ON-WARNING-COLOR        = "white"
;; WARNING-ALT-COLOR       = "#D2232E"
;; ON-WARNING-ALT-COLOR    = "white"
;; FONT-FAMILY             = "Iosevka"

;; Set the theme in Nyxt's config file
(define-configuration browser ((theme my-theme)))
#+end_src

* Defaults

** Colors

We suggest following the WCGA (Web Content Accessibility Guidelines) with
respect to contrast ratios.  The lowest standard (Level AA) requires a ratio of
4.5:1, while a higher standard requires 7:1.

*** Light theme

The scheme below gives a taste of the default light palette.

[[file:default-light-theme.png]]

Find the contrast ratio of each pair of colors in the table below.

| Color Name       | Value   | ~on-*~ Value | Contrast |
|------------------+---------+--------------+----------|
| ~background~     | #FFFFFF | #000000      |    21.00 |
| ~background-alt~ | #EEEEEE | #000000      |    18.10 |
|------------------+---------+--------------+----------|
| ~primary~        | #555555 | #FFFFFF      |     7.46 |
| ~primary-alt~    | #686868 | #FFFFFF      |     5.57 |
|------------------+---------+--------------+----------|
| ~secondary~      | #A6A6A6 | #000000      |     8.63 |
| ~secondary-alt~  | #909090 | #000000      |     6.58 |
|------------------+---------+--------------+----------|
| ~accent~         | #37A8E4 | #000000      |     7.88 |
| ~accent-alt~     | #178DCC | #000000      |     5.72 |
|------------------+---------+--------------+----------|
| ~warning~        | #AF1923 | #FFFFFF      |     7.03 |
| ~warning-alt~    | #D2232E | #FFFFFF      |     5.22 |
#+TBLFM: $4='(contrast $2 $3);%.2f

*** Dark theme

The scheme below gives a taste of the default dark palette.

[[file:default-dark-theme.png]]

Find the contrast ratio of each pair of colors in the table below.

| Color Name       | Value   | ~on-*~ Value | Contrast |
|------------------+---------+--------------+----------|
| ~background~     | #000000 | #FFFFFF      |    21.00 |
| ~background-alt~ | #333333 | #FFFFFF      |    12.63 |
|------------------+---------+--------------+----------|
| ~primary~        | #E48D4E | #000000      |     8.22 |
| ~primary-alt~    | #D7752F | #000000      |     6.47 |
|------------------+---------+--------------+----------|
| ~secondary~      | #874215 | #FFFFFF      |     7.45 |
| ~secondary-alt~  | #A55D2F | #FFFFFF      |     4.99 |
|------------------+---------+--------------+----------|
| ~accent~         | #571FD2 | #FFFFFF      |     8.29 |
| ~accent-alt~     | #763DF2 | #FFFFFF      |     5.65 |
|------------------+---------+--------------+----------|
| ~warning~        | #FCBA04 | #000000      |    12.16 |
| ~warning-alt~    | #FCA904 | #000000      |    10.82 |
#+TBLFM: $4='(contrast $2 $3);%.2f

** Fonts

The font family defaults to Helvetica Neue and Helvetica and is used for
headings in the built-in help system.

** Remarks

~on-colors~, when omitted, are set to either white or black, depending on what
results in a higher contrast ratio with its corresponding ~color~.

One might be tempted to think that ~on-colors~ are meant to be used solely for
text, but the principle holds more generality, when placing tiny elements over
huge surfaces.

Take blue and yellow, colors that have a poor contrast ratio.  Consider that,
(1) you inscribe a blue circle that covers most of the yellow square's surface,
and (2) you were to draw a tiny blue cross on the same yellow background.  In
situation (1), you still properly discern the circle, whereas in (2) you'd
struggle to see it.

* COMMENT TBLFM Code
Auxiliary code to update contrast ratios on the tables shown in this document.

Instructions:
- Evaluate the cell below;
- Run command =org-table-recalculate-buffer-tables=.

#+begin_src emacs-lisp
(defun contrast (c1 c2)
  "Measure WCAG contrast ratio between C1 and C2.
C1 and C2 are color values written in hexadecimal RGB."
  (cl-flet ((wcag-formula (hex)
                          (cl-loop for k in '(0.2126 0.7152 0.0722)
                                   for x in (color-name-to-rgb hex)
                                   sum (* k (if (<= x 0.03928)
                                                (/ x 12.92)
                                              (expt (/ (+ x 0.055) 1.055) 2.4))))))
    (let ((ct (/ (+ (wcag-formula c1) 0.05)
                 (+ (wcag-formula c2) 0.05))))
      (max ct (/ ct)))))
#+end_src
