@kolkrabbi/kol-framework 0.44.0 → 0.46.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/kol-framework.css +13 -12
- package/package.json +3 -3
- package/src/PageLayout.jsx +2 -1
- package/src/ShellHeader.jsx +5 -5
- package/src/SideNav.jsx +1 -1
- package/src/ThemeToggle.jsx +17 -7
package/kol-framework.css
CHANGED
|
@@ -63,16 +63,17 @@ body {
|
|
|
63
63
|
--kol-sidenav-step: var(--kol-spacing-4); /* keyboard resize increment */
|
|
64
64
|
--kol-sidenav-snap-default: var(--kol-sidenav-step); /* release within this of the default clears the override */
|
|
65
65
|
--kol-sidenav-w-collapsed: 56px;
|
|
66
|
-
/*
|
|
67
|
-
2026-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
the BRAND layout's rail
|
|
75
|
-
--kol-shell-
|
|
66
|
+
/* THE WORKSHOP SHELL'S RAILS — their own pair, 256px each (showcase
|
|
67
|
+
refinement 2026-09-28, user: "should they both be the same width? layout
|
|
68
|
+
feels asymmetrical"). The left rail used to read --kol-sidenav-w, which is
|
|
69
|
+
the DRAGGABLE app sidenav's ladder (264, 320 from 1536 — useDragResize,
|
|
70
|
+
NavRail, the labs rail, the deck editor), so the docs shell rendered
|
|
71
|
+
320 | main | 256: one token, two shells. The workshop rails are not
|
|
72
|
+
draggable and frame the same content, so they are equal and fixed, in px
|
|
73
|
+
like every other layout width. Distinct from --kol-toc-w further down,
|
|
74
|
+
which is the BRAND layout's rail. */
|
|
75
|
+
--kol-shell-nav-w: 256px;
|
|
76
|
+
--kol-shell-toc-w: 256px;
|
|
76
77
|
--kol-container-max: 100%;
|
|
77
78
|
|
|
78
79
|
/* Opacity tokens (--kol-fg-*) + text-role tokens (--kol-text-*) live in
|
|
@@ -176,7 +177,7 @@ body {
|
|
|
176
177
|
}
|
|
177
178
|
/* A nested route GROUP's header (sidenav-nested-groups, kol-studio
|
|
178
179
|
* 2026-08-26): a non-routing label sitting where a leaf sits, its rows one
|
|
179
|
-
* step further in. Type and
|
|
180
|
+
* step further in. Type and color ride the element as utilities
|
|
180
181
|
* (kol-helper-10; text-subtle, text-emphasis when a leaf beneath it is the
|
|
181
182
|
* route); the box lives here. No text-transform — the retired fork's
|
|
182
183
|
* `uppercase` does not return (type law: only the display voice carries one). */
|
|
@@ -736,7 +737,7 @@ body {
|
|
|
736
737
|
.kol-portal-footer a { text-decoration: none; transition: opacity 150ms ease; }
|
|
737
738
|
.kol-portal-footer a:hover { opacity: 0.8; }
|
|
738
739
|
|
|
739
|
-
/* ───── Back link (structural only;
|
|
740
|
+
/* ───── Back link (structural only; PageHero sets its type: kol-helper-12 tracking-widest text-body) ───── */
|
|
740
741
|
|
|
741
742
|
.kol-back-link {
|
|
742
743
|
display: inline-flex;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.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",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"react-router-dom": "^6.0.0 || ^7.0.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@kolkrabbi/kol-
|
|
25
|
-
"@kolkrabbi/kol-
|
|
24
|
+
"@kolkrabbi/kol-icons": "^0.29.1",
|
|
25
|
+
"@kolkrabbi/kol-component": "^0.230.0"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"src",
|
package/src/PageLayout.jsx
CHANGED
|
@@ -55,7 +55,7 @@ export const ShellTocCollapsedContext = createContext(null)
|
|
|
55
55
|
* @param {ReactNode} footer below the outlet on the plane
|
|
56
56
|
* @param {ReactNode} defaultTocContent the TOC rail's content when no page registers one
|
|
57
57
|
* @param {object} sideNav every SideNav prop this layout does not own, spread onto the rail
|
|
58
|
-
* @param {string} pageWash the plane's wash, a CSS
|
|
58
|
+
* @param {string} pageWash the plane's wash, a CSS color (e.g. `'var(--kol-fg-02)'`)
|
|
59
59
|
* @param {boolean} bare plane + outlet only (default false)
|
|
60
60
|
*/
|
|
61
61
|
export default function PageLayout({ navTree = [], header, footer, defaultTocContent, sideNav, pageWash, bare = false }) {
|
|
@@ -117,6 +117,7 @@ export default function PageLayout({ navTree = [], header, footer, defaultTocCon
|
|
|
117
117
|
data-drawer-open={drawerOpen ? 'true' : undefined}
|
|
118
118
|
data-toc={showToc ? 'true' : undefined}
|
|
119
119
|
>
|
|
120
|
+
{/* tip-ok: phone-only (md:hidden) — a hover tip never shows on touch; the aria-label names it */}
|
|
120
121
|
<button
|
|
121
122
|
type="button"
|
|
122
123
|
className="kol-sidenav-hamburger md:hidden fixed top-3 left-3 z-30 w-10 h-10 inline-flex items-center justify-center rounded-full bg-surface-primary border border-fg-08 text-emphasis"
|
package/src/ShellHeader.jsx
CHANGED
|
@@ -128,15 +128,15 @@ export default function ShellHeader({
|
|
|
128
128
|
<div className="flex shrink-0 items-center gap-1">
|
|
129
129
|
{actions}
|
|
130
130
|
{showThemeToggle && (
|
|
131
|
-
|
|
131
|
+
<>
|
|
132
132
|
{/* md at every width (user re-rule 2026-08-09: the whole
|
|
133
133
|
* header glyph row rides md, full ink — the earlier lg-at-lg+
|
|
134
134
|
* split is repealed, which also collapsed the two spans). */}
|
|
135
135
|
<ThemeToggle fill="none" label={false} size="md" />
|
|
136
|
-
|
|
136
|
+
</>
|
|
137
137
|
)}
|
|
138
138
|
{onMenuClick && (
|
|
139
|
-
<Tooltip label="
|
|
139
|
+
<Tooltip label="Menu">
|
|
140
140
|
{/* IconFrame, not Button (user ruling 2026-08-01). Header
|
|
141
141
|
* chrome takes a click but must not light up; the frame has
|
|
142
142
|
* no state rules at all, which is the property Button can
|
|
@@ -193,7 +193,7 @@ export default function ShellHeader({
|
|
|
193
193
|
{(onNavToggle || onTocToggle) && (
|
|
194
194
|
<div className="hidden lg:flex items-center gap-1 pb-2">
|
|
195
195
|
{onNavToggle && (
|
|
196
|
-
<Tooltip label="
|
|
196
|
+
<Tooltip label="Navigation">
|
|
197
197
|
{/* The collapsed tint is a VARIANT SWAP, not a state:
|
|
198
198
|
* `ghost` rests at oq-48, `nav` at oq-64, and both are
|
|
199
199
|
* static classes with no rules to fire. That is the only
|
|
@@ -211,7 +211,7 @@ export default function ShellHeader({
|
|
|
211
211
|
</Tooltip>
|
|
212
212
|
)}
|
|
213
213
|
{onTocToggle && (
|
|
214
|
-
<Tooltip label="
|
|
214
|
+
<Tooltip label="Contents">
|
|
215
215
|
<IconFrame
|
|
216
216
|
name="panel-right"
|
|
217
217
|
variant={tocCollapsed ? 'ghost' : 'nav'}
|
package/src/SideNav.jsx
CHANGED
|
@@ -398,7 +398,7 @@ function SideNavInner({
|
|
|
398
398
|
* the aside's position/height/stack live in .kol-sidenav
|
|
399
399
|
* (kol-framework.css). `sticky … h-dvh z-20` here outranked the 767px
|
|
400
400
|
* drawer rule's `position: fixed`, so every consumer page opened one
|
|
401
|
-
* viewport down on phones. Only
|
|
401
|
+
* viewport down on phones. Only color utilities ride the element. */
|
|
402
402
|
className={`kol-sidenav${background ? ' bg-surface-primary' : ''}${hairline ? ' border-r border-fg-08' : ''}${collapsed ? ' is-collapsed' : ''}${drawerOpen ? ' is-drawer-open' : ''}`}
|
|
403
403
|
>
|
|
404
404
|
{/* NO chip Button in either state (user build order 2026-08-09 — "MAKE
|
package/src/ThemeToggle.jsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Icon } from '@kolkrabbi/kol-icons'
|
|
2
|
+
import { Tooltip } from '@kolkrabbi/kol-component'
|
|
2
3
|
import { useTheme } from './theme.js'
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -88,6 +89,12 @@ export default function ThemeToggle({
|
|
|
88
89
|
* ink, so the toggle beside a sunken Dropdown matches it; `inverse` aliased. Rules in kol-theme. */
|
|
89
90
|
tone = 'default',
|
|
90
91
|
className = '',
|
|
92
|
+
/* THE TOGGLE CARRIES ITS OWN TOOLTIP (2026-09-28). It set a native `title`
|
|
93
|
+
* AND the header wrapped it in a KOL Tooltip, so hovering showed both — the
|
|
94
|
+
* browser's "Switch to light mode · following your system" under the DS's
|
|
95
|
+
* "Toggle theme". One tip now, the DS one, with the richer text; `false`
|
|
96
|
+
* leaves the button bare for a caller that wraps it itself. */
|
|
97
|
+
tooltip = true,
|
|
91
98
|
}) {
|
|
92
99
|
const { theme, mode, cycle, clear } = useTheme()
|
|
93
100
|
/* six tones (tone-is-the-ground-axis, 2026-09-03); unset inherits the wrapper's */
|
|
@@ -148,13 +155,16 @@ export default function ThemeToggle({
|
|
|
148
155
|
const shared = {
|
|
149
156
|
type: 'button',
|
|
150
157
|
onClick,
|
|
151
|
-
'aria-label': `Switch to ${next} mode`,
|
|
152
|
-
title: `Switch to ${next} mode${resetHint}`,
|
|
158
|
+
'aria-label': `Switch to ${next} mode${resetHint}`,
|
|
153
159
|
}
|
|
160
|
+
/* ONE WORD (2026-09-30, open questions Round 1 Q4 — ruled many times); the alt-click hint
|
|
161
|
+
* stays in the aria-label and the S sheet, not the tooltip */
|
|
162
|
+
const tip = 'Theme'
|
|
163
|
+
const wrap = (node) => (tooltip ? <Tooltip label={tip}>{node}</Tooltip> : node)
|
|
154
164
|
|
|
155
165
|
/* ── DEPRECATED aliases — old chrome verbatim (0.6.x) ── */
|
|
156
166
|
if (variant === 'icon') {
|
|
157
|
-
return (
|
|
167
|
+
return wrap(
|
|
158
168
|
<button {...shared} className={`kol-theme-toggle kol-theme-toggle-none kol-btn-${size} kol-btn-icon ${extra}`.trim()}>
|
|
159
169
|
{iconSwap(glyphSolo)}
|
|
160
170
|
</button>
|
|
@@ -176,7 +186,7 @@ export default function ThemeToggle({
|
|
|
176
186
|
const chromeCls = bare
|
|
177
187
|
? `w-full inline-flex items-center justify-start gap-2 ${barePadY} px-6 ${mono} bg-transparent text-emphasis`
|
|
178
188
|
: 'kol-theme-toggle kol-theme-toggle-subtle kol-btn-md kol-mono-14 w-full justify-start gap-2'
|
|
179
|
-
return (
|
|
189
|
+
return wrap(
|
|
180
190
|
<button {...shared} className={`${chromeCls} ${extra}`.trim()}>
|
|
181
191
|
<span className="inline-flex items-center justify-center shrink-0" aria-hidden="true">
|
|
182
192
|
{iconSwap(bare ? glyphWithText : glyphSolo)}
|
|
@@ -194,7 +204,7 @@ export default function ThemeToggle({
|
|
|
194
204
|
if (variant === 'flush') {
|
|
195
205
|
const glyph = label ? glyphWithText : glyphSolo
|
|
196
206
|
const cls = `kol-theme-toggle kol-theme-toggle-flush gap-2 ${mono} ${width} ${extra}`
|
|
197
|
-
return (
|
|
207
|
+
return wrap(
|
|
198
208
|
<button {...shared} className={cls.replace(/\s+/g, ' ').trim()}>
|
|
199
209
|
{!iconRight && iconSwap(glyph)}
|
|
200
210
|
{label && <span className={fullWidth ? 'flex-1 min-w-0 text-left' : ''}>{MODE_LABEL[next]}</span>}
|
|
@@ -209,13 +219,13 @@ export default function ThemeToggle({
|
|
|
209
219
|
const fillCls = fill === 'none' ? 'kol-theme-toggle-none' : 'kol-theme-toggle-subtle'
|
|
210
220
|
if (!label) {
|
|
211
221
|
// icon-only pins the square box per rung — geometry condition, not a variant
|
|
212
|
-
return (
|
|
222
|
+
return wrap(
|
|
213
223
|
<button {...shared} className={`kol-theme-toggle ${fillCls} kol-btn-${size} kol-btn-icon ${width} ${extra}`.replace(/\s+/g, ' ').trim()}>
|
|
214
224
|
{iconSwap(glyphSolo)}
|
|
215
225
|
</button>
|
|
216
226
|
)
|
|
217
227
|
}
|
|
218
|
-
return (
|
|
228
|
+
return wrap(
|
|
219
229
|
<button {...shared} className={`kol-theme-toggle ${fillCls} kol-btn-${size} ${mono} gap-2 ${width} ${extra}`.replace(/\s+/g, ' ').trim()}>
|
|
220
230
|
{!iconRight && iconSwap(glyphWithText)}
|
|
221
231
|
<span className={fullWidth ? 'flex-1 min-w-0 text-left' : ''}>{MODE_LABEL[next]}</span>
|