1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! Colour mode: light, dark, or follow the system.
//!
//! The admin panel needs this, and so does any public site an application
//! builds, so the mechanism lives here rather than inside either surface. The
//! admin is one consumer of it, not its owner.
//!
//! What is offered is the **mechanism and its contract**, never an appearance:
//! no icons, no toggle markup, no colour tokens. A site brings its own control
//! and its own palette and keys them off the contract below.
//!
//! # The contract
//!
//! ```text
//! <html data-theme="light"> the resolved mode; write CSS against this
//! localStorage["lat-mode"] the reader's choice: light | dark | auto
//! window.latMode() read the current choice
//! window.latSetMode(mode) persist a choice and apply it
//! window.latApplyMode(mode) apply without persisting
//! ```
//!
//! `auto` follows the operating system *and keeps following it*: a page left
//! open when the system switches at dusk changes with it rather than waiting for
//! a reload. An explicit `light` or `dark` is the reader overriding the system,
//! so it is left alone.
//!
//! # Using it from a page
//!
//! Put [`boot_markup`] in `<head>` before any stylesheet. It must be inline and
//! blocking: a deferred script runs after first paint, and a dark-mode reader
//! would see the page flash white first.
//!
//! ```rust
//! # use laterite_core::theme;
//! let head = format!("<head>{}<link rel=\"stylesheet\" href=\"/site.css\"></head>",
//! theme::boot_markup());
//! assert!(head.contains("latApplyMode"));
//! ```
//!
//! Then style against the attribute, and wire any control to `latSetMode`:
//!
//! ```css
//! :root { --bg: white; --fg: black }
//! [data-theme=dark]{ --bg: black; --fg: white }
//! ```
//!
//! ```js
//! document.querySelector('#to-dark').onclick = () => latSetMode('dark');
//! ```
//!
//! # Content Security Policy
//!
//! An inline script needs `script-src 'unsafe-inline'`, or a nonce or hash. Use
//! [`BOOT_JS`] to serve the same source as a file instead, accepting that a
//! separate request may paint before it arrives.
/// The boot script's JavaScript, without a surrounding `<script>` element.
///
/// Serve it as a file when a Content Security Policy forbids inline script, or
/// embed it yourself with a nonce. Otherwise prefer [`boot_markup`].
pub const BOOT_JS: &str = include_str!;
/// The `localStorage` key holding the reader's choice, for a surface that wants
/// to read or clear it without going through the script.
pub const MODE_KEY: &str = "lat-mode";
/// The three modes, as the strings stored under [`MODE_KEY`].
pub const MODES: = ;
/// The boot script wrapped in a `<script>` element, ready to inline in `<head>`
/// before any stylesheet.
///
/// Returns markup, so a template renders it raw (Askama `|safe`, and the
/// equivalent elsewhere). The content is a compile-time constant with no caller
/// input in it, so there is nothing here to escape.