@aria-framework/theme 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -116,8 +116,8 @@ to a 64px icon rail. The package ships the **CSS** (in `theme.css`) and the
116
116
  Notes:
117
117
  - `data-storage-key` namespaces the persisted collapsed state per app (default
118
118
  `aria-sidebar-collapsed`). Keep an app's historical key across migrations.
119
- - To avoid a flash of the expanded rail on load, pre-apply the class in an early
120
- script: `if (localStorage.getItem('<key>') === '1') document.documentElement.classList.add('sidebar-collapsed')`.
119
+ - To avoid a flash of the expanded rail on load, load `theme-init.js` (below)
120
+ synchronously in `<head>` it pre-applies the class before first paint.
121
121
  - State classes live on `<html>`: `.sidebar-open` (mobile overlay),
122
122
  `.sidebar-collapsed` (desktop icon rail).
123
123
  - Sidebar tokens are themeable in `tokens.json` under `"sidebar"` (per-scheme
@@ -127,6 +127,25 @@ Notes:
127
127
  scheme (e.g. a permanently-dark login hero panel), use fixed values there —
128
128
  since 0.5.0 these tokens follow the active scheme.
129
129
 
130
+ ## Pre-paint init (`theme-init.js`, since 0.6.0)
131
+
132
+ Resolves the color mode BEFORE first paint (no FOUC) and pre-applies the
133
+ collapsed-sidebar class. Load it synchronously in `<head>` — NOT `defer`red:
134
+
135
+ ```html
136
+ <html data-theme-pref="system"> <!-- server stamps light|dark|system -->
137
+ <head>
138
+ <script src="/theme/theme-init.js" data-storage-key="myapp-sidebar-collapsed"></script>
139
+ ...
140
+ ```
141
+
142
+ - `data-theme-pref="system"` resolves from the OS color scheme and live-updates
143
+ if the OS theme changes; `light`/`dark` are forced modes. The result lands on
144
+ `<html data-bs-theme="...">` (Bootstrap 5.3 convention, what `theme.css` keys on).
145
+ - `data-storage-key` MUST match the key given to `sidebar.js` (same default:
146
+ `aria-sidebar-collapsed`).
147
+ - CSP-safe: external + self-hosted, works under `script-src 'self'`.
148
+
130
149
  ## Mobile (Expo / React Native)
131
150
 
132
151
  ```tsx
@@ -179,6 +198,10 @@ Token change → edit `tokens.json`, run `npm run build`, commit the regenerated
179
198
  `strong` (emphasis text, replaces hardcoded `#fff`) and `accent` (active bar /
180
199
  icon / focus ring). Apps that used `--side-*` for deliberately-always-dark
181
200
  surfaces must pin fixed colors instead.
201
+ - **v0.6.0** — added **`web/theme-init.js`**: pre-paint color-mode resolver
202
+ (`data-theme-pref` light/dark/system + live OS-change sync) and
203
+ collapsed-sidebar pre-apply, parameterized by the same `data-storage-key` as
204
+ `sidebar.js`. Apps can delete their local copies.
182
205
 
183
206
  > **0.x semver reminder:** `^0.4.0` matches `<0.5.0` only — consumers pick up
184
207
  > patches via `npm update`, but every **minor** bump (0.4 → 0.5) requires
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aria-framework/theme",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Aria App Framework — theme module. Field Ops Console design system: tokens.json single source of record + a generator that emits a Bootstrap 5.3 web theme (theme.css + self-hosted fonts) and a React Native palette/provider/primitives. Light + dark.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -26,6 +26,7 @@
26
26
  "exports": {
27
27
  "./theme.css": "./web/theme.css",
28
28
  "./sidebar.js": "./web/sidebar.js",
29
+ "./theme-init.js": "./web/theme-init.js",
29
30
  "./mobile": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
30
31
  "./tokens": { "types": "./dist/tokens.d.ts", "default": "./dist/tokens.js" },
31
32
  "./fonts": { "types": "./dist/fonts.d.ts", "default": "./dist/fonts.js" },
@@ -0,0 +1,38 @@
1
+ /*
2
+ * theme-init.js — resolve the active color mode BEFORE first paint (no FOUC).
3
+ *
4
+ * Load synchronously in <head> (NOT deferred) so it runs before the body
5
+ * renders. The server stamps <html data-theme-pref="light|dark|system"> from
6
+ * the signed-in user's saved preference and a best-guess data-bs-theme. When
7
+ * the preference is "system" we resolve it here from the OS color scheme and
8
+ * keep it in sync if the OS theme changes while the app is open. CSP-safe:
9
+ * external, self-hosted, no inline script.
10
+ *
11
+ * Also pre-applies the collapsed-sidebar preference so a collapsed rail does
12
+ * not flash open on load. The localStorage key comes from this script tag's
13
+ * data-storage-key attribute and MUST match the one passed to sidebar.js:
14
+ *
15
+ * <script src="/theme/theme-init.js" data-storage-key="myapp-sidebar-collapsed"></script>
16
+ */
17
+ (function () {
18
+ var el = document.documentElement;
19
+ var script = document.currentScript;
20
+ var sidebarKey = (script && script.dataset && script.dataset.storageKey) || 'aria-sidebar-collapsed';
21
+
22
+ var pref = el.getAttribute('data-theme-pref') || 'system';
23
+ function resolve() {
24
+ var mode = pref === 'dark' ? 'dark'
25
+ : pref === 'light' ? 'light'
26
+ : (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
27
+ el.setAttribute('data-bs-theme', mode);
28
+ }
29
+ resolve();
30
+ if (pref === 'system' && window.matchMedia) {
31
+ try { window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', resolve); }
32
+ catch (e) { /* older browsers: no live OS-change updates */ }
33
+ }
34
+
35
+ try {
36
+ if (localStorage.getItem(sidebarKey) === '1') el.classList.add('sidebar-collapsed');
37
+ } catch (e) { /* private mode / storage disabled */ }
38
+ })();