#+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

The following semantic color groups are defined:

- ~background~ :: large surfaces.
- ~primary~ :: primary interface elements.
- ~secondary~ :: secondary or decorative interface elements.
- ~action~ :: focus or call to action.
- ~success~ :: successful completion, download, or evaluation.
- ~warning~ :: errors, invalid operations, or consequential actions.
- ~highlight~ :: eye-catching text highlighting.

For each group, 2 variation colors with more and less contrast are defined.
These are intended for cases of complex and overlapping
interfaces. E.g. ~background+~ and ~background-~.

Additionally, a foreground color is defined. E.g. ~on-background~.

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

** Example

#+begin_src lisp
(defvar my-theme
  (make-instance 'theme:theme
                 :background-color "#F0F0F0"
                 :primary-color "#595959"
                 :secondary-color "#E6E6E6"
                 :action-color "#5FCFFF"
                 :highlight-color "#FAC090"
                 :success-color "#AEE5BE"
                 :warning-color "#F3B5AF"
                 :font-family "Iosevka"
                 :monospace-font-family "Iosevka")
  "Example theme.

When the values for on-colors are omitted, they're automatically set to either
black or white, according to what achieves a better contrast.

When the values for color+ and color- are omitted, they fallback on regular
color values.

Note that not all semantic color groups need to be defined.")

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

* Defaults

We suggest following the WCAG (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 (Level AAA) requires 7:1.

The target contrast ratios for the default palette are summarized below.

- Minus colors (e.g. ~background-~) :: >= 4.5:1
- Regular colors (e.g. ~background~) :: >= 6.5:1
- Plus colors (e.g. ~background+~) :: >= 8.5:1

** Light theme

| Color Name    | Value   | ~on-*~ Value | Contrast |
|---------------+---------+--------------+----------|
| ~background+~ | #FFFFFF | #000000      |    21.00 |
| ~background~  | #F8F8F8 | #000000      |    19.77 |
| ~background-~ | #ECECEC | #000000      |    17.78 |
|---------------+---------+--------------+----------|
| ~primary+~    | #474747 | #FFFFFF      |     9.29 |
| ~primary~     | #555555 | #FFFFFF      |     7.46 |
| ~primary-~    | #686868 | #FFFFFF      |     5.57 |
|---------------+---------+--------------+----------|
| ~secondary+~  | #BFBFBF | #000000      |    11.42 |
| ~secondary~   | #A6A6A6 | #000000      |     8.63 |
| ~secondary-~  | #909090 | #000000      |     6.58 |
|---------------+---------+--------------+----------|
| ~action+~     | #72CDFE | #000000      |    11.88 |
| ~action~      | #37A8E4 | #000000      |     7.88 |
| ~action-~     | #178DCC | #000000      |     5.72 |
|---------------+---------+--------------+----------|
| ~highlight+~  | #FFFA66 | #000000      |    19.12 |
| ~highlight~   | #FCE304 | #000000      |    16.13 |
| ~highlight-~  | #FCBA04 | #000000      |    12.16 |
|---------------+---------+--------------+----------|
| ~success+~    | #71FE7D | #000000      |    16.18 |
| ~success~     | #8AEA92 | #000000      |    14.26 |
| ~success-~    | #86D58E | #000000      |    11.92 |
|---------------+---------+--------------+----------|
| ~warning+~    | #88040D | #FFFFFF      |    10.14 |
| ~warning~     | #AF1923 | #FFFFFF      |     7.03 |
| ~warning-~    | #D2232E | #FFFFFF      |     5.22 |
|---------------+---------+--------------+----------|
#+TBLFM: $4='(contrast $2 $3);%.2f

** Dark theme

| Color Name    | Value   | ~on-*~ Value | Contrast |
|---------------+---------+--------------+----------|
| ~background+~ | #000000 | #FFFFFF      |    21.00 |
| ~background~  | #121212 | #FFFFFF      |    18.73 |
| ~background-~ | #333333 | #FFFFFF      |    12.63 |
|---------------+---------+--------------+----------|
| ~primary+~    | #EFA671 | #000000      |    10.36 |
| ~primary~     | #E48D4E | #000000      |     8.22 |
| ~primary-~    | #D7752F | #000000      |     6.47 |
|---------------+---------+--------------+----------|
| ~secondary+~  | #683008 | #FFFFFF      |    10.42 |
| ~secondary~   | #844115 | #FFFFFF      |     7.64 |
| ~secondary-~  | #9F592D | #FFFFFF      |     5.33 |
|---------------+---------+--------------+----------|
| ~action+~     | #481FA2 | #FFFFFF      |    10.54 |
| ~action~      | #571FD2 | #FFFFFF      |     8.29 |
| ~action-~     | #763DF2 | #FFFFFF      |     5.65 |
|---------------+---------+--------------+----------|
| ~highlight+~  | #FC83F2 | #000000      |     9.67 |
| ~highlight~   | #F46DE8 | #000000      |     8.20 |
| ~highlight-~  | #EA43DD | #000000      |     6.35 |
|---------------+---------+--------------+----------|
| ~success+~    | #87FCDF | #000000      |    17.02 |
| ~success~     | #4CFBCF | #000000      |    16.01 |
| ~success-~    | #05F4CD | #000000      |    14.83 |
|---------------+---------+--------------+----------|
| ~warning+~    | #FFD152 | #000000      |    14.49 |
| ~warning~     | #FCBA04 | #000000      |    12.16 |
| ~warning-~    | #FCA904 | #000000      |    10.82 |
|---------------+---------+--------------+----------|
#+TBLFM: $4='(contrast $2 $3);%.2f

** Remarks

The minus and plus colors, when omitted, are set to the corresponding regular
color.

~on-colors~, when omitted, are set to either black or white, depending on which
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
