@kolkrabbi/kol-framework 0.10.3 → 0.13.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 +23 -1
- package/package.json +3 -3
- package/src/AppShell.jsx +1 -1
- package/src/ShellHeader.jsx +70 -25
- package/src/index.js +1 -1
package/kol-framework.css
CHANGED
|
@@ -31,8 +31,21 @@ body {
|
|
|
31
31
|
|
|
32
32
|
:root {
|
|
33
33
|
--kol-topnav-h: 0px;
|
|
34
|
-
|
|
34
|
+
/* 16rem — the rail width, one number (2026-07-31). It read 260 while every
|
|
35
|
+
grid track that had to line up with it read 256, so the shell header's
|
|
36
|
+
brand block sat 4 off the sidebar column it exists to align to. */
|
|
37
|
+
--kol-sidenav-w: 16rem;
|
|
35
38
|
--kol-sidenav-w-collapsed: 56px;
|
|
39
|
+
/* The workshop shell's RIGHT rail. EQUAL TO THE LEFT (user ruling
|
|
40
|
+
2026-08-01) — the two rails frame the same content and a lopsided frame is
|
|
41
|
+
a frame nobody drew on purpose; it read 14rem against the left's 16rem.
|
|
42
|
+
Kept as its own token rather than aliased: the right rail can be ruled
|
|
43
|
+
different later without touching the sidenav.
|
|
44
|
+
The track is RESERVED whether or not the rail has anything in it — a rail
|
|
45
|
+
that vanishes on a heading-less page re-flows main, so the same shell
|
|
46
|
+
renders at two widths. Distinct from --kol-toc-w further down, which is
|
|
47
|
+
the BRAND layout's rail: same concept, two shells, two numbers. */
|
|
48
|
+
--kol-shell-toc-w: 16rem;
|
|
36
49
|
--kol-container-max: 100%;
|
|
37
50
|
|
|
38
51
|
/* Opacity tokens (--kol-fg-*) + text-role tokens (--kol-text-*) live in
|
|
@@ -44,6 +57,15 @@ body {
|
|
|
44
57
|
Intentional: site chrome uses browser-ease for a softer feel. */
|
|
45
58
|
--kol-t-btn: background 200ms ease, color 200ms ease, border-color 200ms ease;
|
|
46
59
|
|
|
60
|
+
/* Shell CHROME inset (2026-07-31, user ruling: "24, it's what lives in
|
|
61
|
+
kolkrabbi") — a separate concern from the page ladder below, and
|
|
62
|
+
deliberately tighter: chrome is not page content. The shell header and the
|
|
63
|
+
shell content frame both read this, so they cannot disagree; before it,
|
|
64
|
+
the header carried bare Tailwind steps and the frame carried the page ramp.
|
|
65
|
+
FLAT, not stepped — one value at every width, on the user's word.
|
|
66
|
+
Do NOT reach for this on page containers — that is --kol-pad-section-x. */
|
|
67
|
+
--kol-pad-chrome-x: var(--kol-spacing-6);
|
|
68
|
+
|
|
47
69
|
/* Stepped page / section / band padding tokens.
|
|
48
70
|
Used by chrome (topnav, sidenav) and landing bands. Scaled at 768 / 1024
|
|
49
71
|
breakpoints so one set of rules covers mobile → desktop without clamp(). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"app-shell"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@kolkrabbi/kol-component": "0.
|
|
31
|
-
"@kolkrabbi/kol-icons": "0.
|
|
30
|
+
"@kolkrabbi/kol-component": "0.24.0",
|
|
31
|
+
"@kolkrabbi/kol-icons": "0.10.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"react": "^18.3.0 || ^19.0.0",
|
package/src/AppShell.jsx
CHANGED
|
@@ -53,7 +53,7 @@ export default function AppShell({ navTree = [], getActivePage, header, footer,
|
|
|
53
53
|
</button>
|
|
54
54
|
|
|
55
55
|
<div
|
|
56
|
-
className="kol-sidenav-backdrop fixed inset-0 z-20
|
|
56
|
+
className="kol-sidenav-backdrop fixed inset-0 z-20 kol-overlay-scrim opacity-0 pointer-events-none transition-opacity duration-200 md:hidden"
|
|
57
57
|
onClick={() => setDrawerOpen(false)}
|
|
58
58
|
aria-hidden="true"
|
|
59
59
|
/>
|
package/src/ShellHeader.jsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Icon } from '@kolkrabbi/kol-icons'
|
|
2
|
-
import { SearchInput, Tooltip } from '@kolkrabbi/kol-component'
|
|
2
|
+
import { IconFrame, SearchInput, Tooltip, glyphSize } from '@kolkrabbi/kol-component'
|
|
3
3
|
import ThemeToggle from './ThemeToggle.jsx'
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -42,8 +42,35 @@ import ThemeToggle from './ThemeToggle.jsx'
|
|
|
42
42
|
* @param {string} className extra classes on the outer bar
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
/* ONE header-action glyph size (user 2026-08-01: "icon in navbar is small and
|
|
46
|
+
* wrong"). The row mixed sizes: the ThemeToggle draws its solo glyph at 24 on
|
|
47
|
+
* the `lg` rung while search, GitHub and the hamburger sat at 18 — four
|
|
48
|
+
* controls, two scales, in a row eight inches wide.
|
|
49
|
+
*
|
|
50
|
+
* It is NOT an independent number. Every header control is a `lg` rung, and
|
|
51
|
+
* `SOLO.lg` (hooks/glyphLadders.js) is 24 — this constant is that ladder rung
|
|
52
|
+
* under a name the chrome can import. Naming it once was not enough on its own:
|
|
53
|
+
* it shipped exported and the two rail toggles still hardcoded 18 three rows
|
|
54
|
+
* below it, which is the whole reason the row looked wrong after the fix. The
|
|
55
|
+
* durable answer is that no header control writes a glyph size at all — it
|
|
56
|
+
* writes `size="lg"` and the ladder resolves it.
|
|
57
|
+
*
|
|
58
|
+
* @deprecated 2026-08-01 — **zero consumers as of this change.** Every header
|
|
59
|
+
* control now names its rung and takes the glyph from `SOLO`. Kept exported
|
|
60
|
+
* because it is public API of a published package (breaking an export needs a
|
|
61
|
+
* changeset + major bump); a consumer still importing it gets the right number,
|
|
62
|
+
* it is just no longer how the chrome decides. Passing it as `iconSize` is the
|
|
63
|
+
* anti-pattern this whole change removes: it pins the glyph while leaving the
|
|
64
|
+
* SQUARE on whatever rung the call site forgot to set, which is precisely how
|
|
65
|
+
* the search trigger ended up drawing an lg glyph in an md box. */
|
|
66
|
+
export const HEADER_ICON = 24
|
|
67
|
+
|
|
68
|
+
/* `iconBtnCls` lived here — a private class string re-implementing the icon
|
|
69
|
+
* button box (h-9 w-9, rounded, transparent, hover wash) on three call sites,
|
|
70
|
+
* with a near-duplicate of it hand-written in the showcase's GitHub link. Four
|
|
71
|
+
* containers, one job. Deleted 2026-08-01: the box has an owner, and it is
|
|
72
|
+
* `Button variant="ghost" quiet iconOnly` — the shape the search trigger in
|
|
73
|
+
* ShellLayout had been calling correctly the entire time. */
|
|
47
74
|
|
|
48
75
|
export default function ShellHeader({
|
|
49
76
|
brand,
|
|
@@ -66,7 +93,11 @@ export default function ShellHeader({
|
|
|
66
93
|
<header className={`kol-shell-header sticky top-0 z-50 shrink-0 bg-surface-primary ${className}`.trim()}>
|
|
67
94
|
{/* Row 1: brand block + controls */}
|
|
68
95
|
<div className="border-b border-fg-08">
|
|
69
|
-
|
|
96
|
+
{/* Chrome inset from --kol-pad-chrome-x, not Tailwind steps
|
|
97
|
+
* (2026-07-31): the header sat on its own improvised steps while the
|
|
98
|
+
* shell content frame sat on the page ramp, so the sidebar hung right
|
|
99
|
+
* of the wordmark above it. One token, both surfaces. */}
|
|
100
|
+
<div className="w-full py-4" style={{ paddingInline: 'var(--kol-pad-chrome-x)' }}>
|
|
70
101
|
<div className="flex items-center justify-between">
|
|
71
102
|
{/* kol-shell-header-brand reserves the nav-column width at lg+ so
|
|
72
103
|
* the Row-2 tabs align to the shell grid. */}
|
|
@@ -82,19 +113,24 @@ export default function ShellHeader({
|
|
|
82
113
|
* quiet square = button geometry + fill none + no label
|
|
83
114
|
* (was the old `icon` variant default). */}
|
|
84
115
|
<span className="hidden lg:inline-flex"><ThemeToggle fill="none" label={false} size="lg" /></span>
|
|
85
|
-
<span className="lg:hidden"><ThemeToggle fill="none" label={false} /></span>
|
|
116
|
+
<span className="lg:hidden"><ThemeToggle fill="none" label={false} size="lg" /></span>
|
|
86
117
|
</Tooltip>
|
|
87
118
|
)}
|
|
88
119
|
{onMenuClick && (
|
|
89
120
|
<Tooltip label="Open navigation menu">
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
121
|
+
{/* IconFrame, not Button (user ruling 2026-08-01). Header
|
|
122
|
+
* chrome takes a click but must not light up; the frame has
|
|
123
|
+
* no state rules at all, which is the property Button can
|
|
124
|
+
* only approximate by dropping its own. `nav` rests at
|
|
125
|
+
* oq-64 — the `text-fg-64` this control hand-wrote for
|
|
126
|
+
* months. */}
|
|
127
|
+
<IconFrame
|
|
128
|
+
name="hamburger"
|
|
129
|
+
variant="nav"
|
|
130
|
+
size="lg"
|
|
93
131
|
onClick={onMenuClick}
|
|
94
132
|
aria-label="Open navigation menu"
|
|
95
|
-
|
|
96
|
-
<Icon name="hamburger" size={18} />
|
|
97
|
-
</button>
|
|
133
|
+
/>
|
|
98
134
|
</Tooltip>
|
|
99
135
|
)}
|
|
100
136
|
</div>
|
|
@@ -105,7 +141,7 @@ export default function ShellHeader({
|
|
|
105
141
|
{/* Row 2: section tabs + inline search + rail toggles */}
|
|
106
142
|
{hasTabRow && (
|
|
107
143
|
<div className="border-b border-fg-08">
|
|
108
|
-
<div className="w-full
|
|
144
|
+
<div className="w-full" style={{ paddingInline: 'var(--kol-pad-chrome-x)' }}>
|
|
109
145
|
<div className="kol-shell-header-tabs">
|
|
110
146
|
<nav className="flex flex-1 gap-6" aria-label="Sections">
|
|
111
147
|
{nav.map((item) => {
|
|
@@ -118,7 +154,12 @@ export default function ShellHeader({
|
|
|
118
154
|
aria-current={active ? 'page' : undefined}
|
|
119
155
|
onClick={onNavigate ? (event) => onNavigate(event, item) : undefined}
|
|
120
156
|
>
|
|
121
|
-
{
|
|
157
|
+
{/* The ladder, not a literal (2026-08-01). This hardcoded
|
|
158
|
+
* 14 — the same value ADJACENT.sm carries, but written
|
|
159
|
+
* out, so the tab could not follow the rule it was
|
|
160
|
+
* meant to. The tab's own type is 14/18, which is that
|
|
161
|
+
* rung. */}
|
|
162
|
+
{item.icon && <Icon name={item.icon} size={glyphSize('sm')} />}
|
|
122
163
|
{item.label}
|
|
123
164
|
</a>
|
|
124
165
|
)
|
|
@@ -135,28 +176,32 @@ export default function ShellHeader({
|
|
|
135
176
|
<div className="hidden lg:flex items-center gap-1 pb-2">
|
|
136
177
|
{onNavToggle && (
|
|
137
178
|
<Tooltip label="Toggle navigation sidebar">
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
179
|
+
{/* The collapsed tint is a VARIANT SWAP, not a state:
|
|
180
|
+
* `ghost` rests at oq-48, `nav` at oq-64, and both are
|
|
181
|
+
* static classes with no rules to fire. That is the only
|
|
182
|
+
* honest way to express it on a frame — Button's `quiet`
|
|
183
|
+
* is an opacity transition, i.e. exactly the kind of
|
|
184
|
+
* behaviour this control was ruled out of. */}
|
|
185
|
+
<IconFrame
|
|
186
|
+
name="panel-left"
|
|
187
|
+
variant={navCollapsed ? 'ghost' : 'nav'}
|
|
188
|
+
size="sm"
|
|
141
189
|
onClick={onNavToggle}
|
|
142
190
|
aria-label="Toggle navigation sidebar"
|
|
143
191
|
aria-pressed={!navCollapsed}
|
|
144
|
-
|
|
145
|
-
<Icon name="panel-left" size={18} />
|
|
146
|
-
</button>
|
|
192
|
+
/>
|
|
147
193
|
</Tooltip>
|
|
148
194
|
)}
|
|
149
195
|
{onTocToggle && (
|
|
150
196
|
<Tooltip label="Toggle table of contents sidebar">
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
197
|
+
<IconFrame
|
|
198
|
+
name="panel-right"
|
|
199
|
+
variant={tocCollapsed ? 'ghost' : 'nav'}
|
|
200
|
+
size="sm"
|
|
154
201
|
onClick={onTocToggle}
|
|
155
202
|
aria-label="Toggle table of contents sidebar"
|
|
156
203
|
aria-pressed={!tocCollapsed}
|
|
157
|
-
|
|
158
|
-
<Icon name="panel-right" size={18} />
|
|
159
|
-
</button>
|
|
204
|
+
/>
|
|
160
205
|
</Tooltip>
|
|
161
206
|
)}
|
|
162
207
|
</div>
|
package/src/index.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
export { default as AppShell } from './AppShell.jsx'
|
|
11
11
|
export { ShellTocContext, ShellTocCollapsedContext } from './AppShell.jsx'
|
|
12
12
|
export { default as SideNav } from './SideNav.jsx'
|
|
13
|
-
export { default as ShellHeader } from './ShellHeader.jsx'
|
|
13
|
+
export { default as ShellHeader, HEADER_ICON } from './ShellHeader.jsx'
|
|
14
14
|
export { default as ThemeToggle } from './ThemeToggle.jsx'
|
|
15
15
|
export { useTheme, applyTheme, getInitialTheme, THEME_STORAGE_KEY } from './theme.js'
|
|
16
16
|
export { default as Layout } from './Layout.jsx'
|