@kolkrabbi/kol-framework 0.27.0 → 0.28.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-framework",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "KOL app shell — sidenav, layout, theme toggle, footer, heroes + brand color layer. SideNav takes nav data as props so the tree stays app-local.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,8 +20,8 @@
20
20
  "react-router-dom": "^6.0.0 || ^7.0.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@kolkrabbi/kol-icons": "^0.18.0",
24
- "@kolkrabbi/kol-component": "^0.80.0"
23
+ "@kolkrabbi/kol-component": "^0.99.0",
24
+ "@kolkrabbi/kol-icons": "^0.20.0"
25
25
  },
26
26
  "files": [
27
27
  "src",
@@ -64,6 +64,11 @@ import { useTheme } from './theme.js'
64
64
  * sees) and one click commits it. The reset lives behind a modifier — alt- or
65
65
  * shift-click — which is the "separate affordance" the ruling allows, with no
66
66
  * new chrome and no third stop. */
67
+ /* The VISIBLE label names the TARGET, not the state (ThemeToggleLabelTarget,
68
+ * user 2026-08-27: "should it not say dark mode in light mode and vice versa?
69
+ * as to what the button actually does when you click it?"). A button's label
70
+ * names its action — the aria-label / title always said "Switch to …"; the eye
71
+ * was told the opposite. Rendered as MODE_LABEL[next], never MODE_LABEL[shown]. */
67
72
  const MODE_LABEL = { light: 'Light mode', dark: 'Dark mode' }
68
73
  const SLOT = { dark: 0, light: 1 }
69
74
 
@@ -171,7 +176,7 @@ export default function ThemeToggle({
171
176
  {iconSwap(bare ? glyphWithText : glyphSolo)}
172
177
  </span>
173
178
  <span className="kol-sidenav-hop-label flex-1 min-w-0 text-left">
174
- {MODE_LABEL[shown]}
179
+ {MODE_LABEL[next]}
175
180
  </span>
176
181
  </button>
177
182
  )
@@ -186,7 +191,7 @@ export default function ThemeToggle({
186
191
  return (
187
192
  <button {...shared} className={cls.replace(/\s+/g, ' ').trim()}>
188
193
  {!iconRight && iconSwap(glyph)}
189
- {label && <span className={fullWidth ? 'flex-1 min-w-0 text-left' : ''}>{MODE_LABEL[shown]}</span>}
194
+ {label && <span className={fullWidth ? 'flex-1 min-w-0 text-left' : ''}>{MODE_LABEL[next]}</span>}
190
195
  {iconRight && iconSwap(glyph)}
191
196
  </button>
192
197
  )
@@ -207,7 +212,7 @@ export default function ThemeToggle({
207
212
  return (
208
213
  <button {...shared} className={`kol-theme-toggle ${fillCls} kol-btn-${size} ${mono} gap-2 ${width} ${className}`.replace(/\s+/g, ' ').trim()}>
209
214
  {!iconRight && iconSwap(glyphWithText)}
210
- <span className={fullWidth ? 'flex-1 min-w-0 text-left' : ''}>{MODE_LABEL[shown]}</span>
215
+ <span className={fullWidth ? 'flex-1 min-w-0 text-left' : ''}>{MODE_LABEL[next]}</span>
211
216
  {iconRight && iconSwap(glyphWithText)}
212
217
  </button>
213
218
  )
package/src/index.js CHANGED
@@ -13,7 +13,7 @@ export { default as SideNav } from './SideNav.jsx'
13
13
  export { default as useDragResize } from './useDragResize.js'
14
14
  export { default as ShellHeader, HEADER_ICON } from './ShellHeader.jsx'
15
15
  export { default as ThemeToggle } from './ThemeToggle.jsx'
16
- export { useTheme, applyTheme, getInitialTheme, THEME_STORAGE_KEY } from './theme.js'
16
+ export { useTheme, applyTheme, getInitialTheme, THEME_STORAGE_KEY, THEME_BOOT_SCRIPT } from './theme.js'
17
17
  export { default as Layout } from './Layout.jsx'
18
18
  export { default as PageSection } from './PageSection.jsx'
19
19
  export { default as PortalFooter } from './PortalFooter.jsx'
package/src/theme.js CHANGED
@@ -18,6 +18,22 @@ import { useEffect, useState } from 'react'
18
18
 
19
19
  export const THEME_STORAGE_KEY = 'kol-theme'
20
20
 
21
+ /* THE NO-FLASH BOOT SNIPPET (ShellHomeSystem, 2026-08-27 — every app hand-wrote
22
+ * it against this file's key): inline it in index.html before the app script.
23
+ * USER law: explicit choice > system > light — a saved light/dark stamps
24
+ * `data-theme` before first paint; no saved choice leaves the page un-stamped
25
+ * so the theme CSS follows the OS live. The versioned cutover (v3, 2026-07-30)
26
+ * discards stamps written by the old two-state toggle, once. */
27
+ export const THEME_BOOT_SCRIPT = `try {
28
+ if (localStorage.getItem('kol-theme-v') !== '3') {
29
+ localStorage.removeItem('kol-theme')
30
+ localStorage.removeItem('theme')
31
+ localStorage.setItem('kol-theme-v', '3')
32
+ }
33
+ var t = localStorage.getItem('kol-theme')
34
+ if (t === 'light' || t === 'dark') document.documentElement.dataset.theme = t
35
+ } catch (e) {}`
36
+
21
37
  function getExplicitTheme() {
22
38
  const attr = document.documentElement.dataset.theme
23
39
  if (attr === 'light' || attr === 'dark') return attr