@kolkrabbi/kol-framework 0.5.0 → 0.5.2
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 +2 -2
- package/src/ShellHeader.jsx +1 -1
- package/src/ThemeToggle.jsx +11 -8
- package/src/theme.js +8 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-framework",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
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",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"app-shell"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@kolkrabbi/kol-component": "0.
|
|
30
|
+
"@kolkrabbi/kol-component": "0.12.0",
|
|
31
31
|
"@kolkrabbi/kol-icons": "0.7.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
package/src/ShellHeader.jsx
CHANGED
package/src/ThemeToggle.jsx
CHANGED
|
@@ -5,9 +5,9 @@ import { useTheme } from './theme.js'
|
|
|
5
5
|
* Theme toggle — horizontal icon-swap button.
|
|
6
6
|
*
|
|
7
7
|
* Variants:
|
|
8
|
-
* icon —
|
|
9
|
-
*
|
|
10
|
-
* icon buttons.
|
|
8
|
+
* icon — icon-only button on the DS button ladder: `size` 'sm'|'md'|'lg'
|
|
9
|
+
* → 14/16/18px glyph, box + hover from the ghost .kol-btn chrome,
|
|
10
|
+
* so the toggle sizes exactly like sibling icon buttons in a bar.
|
|
11
11
|
* hop — full-width labeled Button-primary-styled sidenav row
|
|
12
12
|
* (bg-fg-04, on-primary text). For sidenav rows where it pairs
|
|
13
13
|
* with other Button-primary "hop" entries.
|
|
@@ -22,7 +22,7 @@ import { useTheme } from './theme.js'
|
|
|
22
22
|
* horizontally — the split-circle glyph makes the slide itself read as
|
|
23
23
|
* the flip (same-icon slide, per design).
|
|
24
24
|
*/
|
|
25
|
-
export default function ThemeToggle({ variant = 'icon', className = '' }) {
|
|
25
|
+
export default function ThemeToggle({ variant = 'icon', size = 'md', className = '' }) {
|
|
26
26
|
const { isDark, toggle } = useTheme()
|
|
27
27
|
|
|
28
28
|
const next = isDark ? 'light' : 'dark'
|
|
@@ -69,17 +69,20 @@ export default function ThemeToggle({ variant = 'icon', className = '' }) {
|
|
|
69
69
|
)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
// variant === 'icon' (default) —
|
|
73
|
-
// (
|
|
72
|
+
// variant === 'icon' (default) — geometry from the DS button ladder
|
|
73
|
+
// (sm 14 / md 16 / lg 18 glyph), chrome = ghost icon-only .kol-btn.
|
|
74
|
+
// Overrules the 2026-07-15 "36×36/20 reference" one-off (user ruling
|
|
75
|
+
// 2026-07-28: the button ladder is the reference, bar icons included).
|
|
76
|
+
const glyph = size === 'sm' ? 14 : size === 'lg' ? 18 : 16
|
|
74
77
|
return (
|
|
75
78
|
<button
|
|
76
79
|
type="button"
|
|
77
80
|
onClick={handleToggle}
|
|
78
81
|
aria-label={`Switch to ${next} mode`}
|
|
79
82
|
title={`Switch to ${next} mode`}
|
|
80
|
-
className={`
|
|
83
|
+
className={`kol-btn kol-btn-ghost kol-btn-${size} kol-btn-icon ${className}`.trim()}
|
|
81
84
|
>
|
|
82
|
-
{iconSwap(
|
|
85
|
+
{iconSwap(glyph)}
|
|
83
86
|
</button>
|
|
84
87
|
)
|
|
85
88
|
}
|
package/src/theme.js
CHANGED
|
@@ -2,10 +2,11 @@ import { useEffect, useState } from 'react'
|
|
|
2
2
|
|
|
3
3
|
/* Theme-state machinery — the logic behind ThemeToggle, exported so app code
|
|
4
4
|
* (navbars, heroes, anything theme-aware) can read/set the theme without
|
|
5
|
-
* mounting the toggle UI. Policy
|
|
6
|
-
*
|
|
7
|
-
* saved user toggle, and absent both the
|
|
8
|
-
*
|
|
5
|
+
* mounting the toggle UI. Policy: the DS is light-first (USER law, theme
|
|
6
|
+
* 0.9.2 dropped the OS-follow auto-dark CSS); an explicit choice is an
|
|
7
|
+
* app-set data-theme attribute or a saved user toggle, and absent both the
|
|
8
|
+
* theme is light — never the OS preference, so state can't disagree with
|
|
9
|
+
* what the CSS renders. `data-theme` + localStorage are written ONLY through
|
|
9
10
|
* applyTheme (a user action) — mounting a reader leaves the DOM untouched. */
|
|
10
11
|
|
|
11
12
|
export const THEME_STORAGE_KEY = 'kol-theme'
|
|
@@ -22,8 +23,7 @@ function getExplicitTheme() {
|
|
|
22
23
|
|
|
23
24
|
export function getInitialTheme() {
|
|
24
25
|
if (typeof document === 'undefined') return 'light'
|
|
25
|
-
return getExplicitTheme()
|
|
26
|
-
?? (window.matchMedia?.('(prefers-color-scheme: dark)')?.matches ? 'dark' : 'light')
|
|
26
|
+
return getExplicitTheme() ?? 'light'
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/** Stamp + persist an explicit theme choice. */
|
|
@@ -37,8 +37,8 @@ export function applyTheme(theme) {
|
|
|
37
37
|
*
|
|
38
38
|
* All instances observe the html `data-theme` attribute, so any writer
|
|
39
39
|
* (this hook's setTheme, ThemeToggle, an app boot script) updates every
|
|
40
|
-
* reader. When the page has no explicit choice,
|
|
41
|
-
*
|
|
40
|
+
* reader. When the page has no explicit choice, the theme is light
|
|
41
|
+
* (light-first law). Returns { theme, isDark, setTheme, toggle }.
|
|
42
42
|
*/
|
|
43
43
|
export function useTheme() {
|
|
44
44
|
const [theme, setThemeState] = useState(getInitialTheme)
|
|
@@ -62,18 +62,8 @@ export function useTheme() {
|
|
|
62
62
|
})
|
|
63
63
|
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] })
|
|
64
64
|
|
|
65
|
-
/* No explicit choice anywhere — follow the system, like the theme CSS. */
|
|
66
|
-
const mq = window.matchMedia?.('(prefers-color-scheme: dark)')
|
|
67
|
-
const onChange = (e) => {
|
|
68
|
-
try { if (localStorage.getItem(THEME_STORAGE_KEY)) return } catch { /* storage blocked */ }
|
|
69
|
-
if (document.documentElement.dataset.theme) return
|
|
70
|
-
setThemeState(e.matches ? 'dark' : 'light')
|
|
71
|
-
}
|
|
72
|
-
mq?.addEventListener?.('change', onChange)
|
|
73
|
-
|
|
74
65
|
return () => {
|
|
75
66
|
observer.disconnect()
|
|
76
|
-
mq?.removeEventListener?.('change', onChange)
|
|
77
67
|
}
|
|
78
68
|
}, [])
|
|
79
69
|
|