@kolkrabbi/kol-framework 0.34.0 → 0.36.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.34.0",
3
+ "version": "0.36.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.24.0",
24
- "@kolkrabbi/kol-component": "^0.119.0"
23
+ "@kolkrabbi/kol-component": "^0.142.0",
24
+ "@kolkrabbi/kol-icons": "^0.25.0"
25
25
  },
26
26
  "files": [
27
27
  "src",
@@ -84,9 +84,13 @@ export default function ThemeToggle({
84
84
  label = true,
85
85
  iconRight = false,
86
86
  fullWidth = false,
87
+ /* `tone="sunken"` (ControlToneSunken, kol-website 2026-08-28): the control set's dark well + fg-96
88
+ * ink, so the toggle beside a sunken Dropdown matches it; `inverse` aliased. Rules in kol-theme. */
89
+ tone = 'default',
87
90
  className = '',
88
91
  }) {
89
92
  const { theme, mode, cycle, clear } = useTheme()
93
+ const extra = `${tone === 'sunken' || tone === 'inverse' ? 'kol-tone-sunken' : ''} ${className}`.trim()
90
94
 
91
95
  /* Label and slot follow the RESOLVED theme, not the stored choice: when the
92
96
  * page is unset, what the user is looking at is the OS's answer, and the
@@ -149,7 +153,7 @@ export default function ThemeToggle({
149
153
  /* ── DEPRECATED aliases — old chrome verbatim (0.6.x) ── */
150
154
  if (variant === 'icon') {
151
155
  return (
152
- <button {...shared} className={`kol-theme-toggle kol-theme-toggle-none kol-btn-${size} kol-btn-icon ${className}`.trim()}>
156
+ <button {...shared} className={`kol-theme-toggle kol-theme-toggle-none kol-btn-${size} kol-btn-icon ${extra}`.trim()}>
153
157
  {iconSwap(glyphSolo)}
154
158
  </button>
155
159
  )
@@ -171,7 +175,7 @@ export default function ThemeToggle({
171
175
  ? `w-full inline-flex items-center justify-start gap-2 ${barePadY} px-6 ${mono} bg-transparent text-emphasis`
172
176
  : 'kol-theme-toggle kol-theme-toggle-subtle kol-btn-md kol-mono-14 w-full justify-start gap-2'
173
177
  return (
174
- <button {...shared} className={`${chromeCls} ${className}`.trim()}>
178
+ <button {...shared} className={`${chromeCls} ${extra}`.trim()}>
175
179
  <span className="inline-flex items-center justify-center shrink-0" aria-hidden="true">
176
180
  {iconSwap(bare ? glyphWithText : glyphSolo)}
177
181
  </span>
@@ -187,7 +191,7 @@ export default function ThemeToggle({
187
191
 
188
192
  if (variant === 'flush') {
189
193
  const glyph = label ? glyphWithText : glyphSolo
190
- const cls = `kol-theme-toggle kol-theme-toggle-flush gap-2 ${mono} ${width} ${className}`
194
+ const cls = `kol-theme-toggle kol-theme-toggle-flush gap-2 ${mono} ${width} ${extra}`
191
195
  return (
192
196
  <button {...shared} className={cls.replace(/\s+/g, ' ').trim()}>
193
197
  {!iconRight && iconSwap(glyph)}
@@ -204,13 +208,13 @@ export default function ThemeToggle({
204
208
  if (!label) {
205
209
  // icon-only pins the square box per rung — geometry condition, not a variant
206
210
  return (
207
- <button {...shared} className={`kol-theme-toggle ${fillCls} kol-btn-${size} kol-btn-icon ${width} ${className}`.replace(/\s+/g, ' ').trim()}>
211
+ <button {...shared} className={`kol-theme-toggle ${fillCls} kol-btn-${size} kol-btn-icon ${width} ${extra}`.replace(/\s+/g, ' ').trim()}>
208
212
  {iconSwap(glyphSolo)}
209
213
  </button>
210
214
  )
211
215
  }
212
216
  return (
213
- <button {...shared} className={`kol-theme-toggle ${fillCls} kol-btn-${size} ${mono} gap-2 ${width} ${className}`.replace(/\s+/g, ' ').trim()}>
217
+ <button {...shared} className={`kol-theme-toggle ${fillCls} kol-btn-${size} ${mono} gap-2 ${width} ${extra}`.replace(/\s+/g, ' ').trim()}>
214
218
  {!iconRight && iconSwap(glyphWithText)}
215
219
  <span className={fullWidth ? 'flex-1 min-w-0 text-left' : ''}>{MODE_LABEL[next]}</span>
216
220
  {iconRight && iconSwap(glyphWithText)}
@@ -1,4 +1,12 @@
1
1
  import { useEffect, useMemo, useRef, useState } from 'react'
2
+ /* THE SAME GRAB AS THE SHELL RAIL (OneGrabGestureBothRails, kol-fxr 2026-08-30 —
3
+ * user: *"why dont we use the grab animation and other sidenav settings to be
4
+ * consistent?"*). fxr's /labs shows both rails at once and only the left one
5
+ * woke as the cursor neared it, because each package had built the gesture
6
+ * itself. The hook lives in kol-component, the one package both can reach —
7
+ * kol-shell dropped its framework peer in 0.16.0, so neither could import the
8
+ * other's. */
9
+ import { useGrabEdge } from '@kolkrabbi/kol-component'
2
10
 
3
11
  /* Grab-edge resize + collapse for SideNav — THE single control since 0.17.0
4
12
  * (user build order 2026-08-09, completing the SideNavGrabResize brief: the
@@ -102,6 +110,11 @@ export default function useDragResize(ref, options = {}) {
102
110
  const dir = side === 'right' ? -1 : 1
103
111
  const names = useMemo(() => buildNames(token), [token])
104
112
 
113
+ /* the handle's own ref, so the shared gesture can find it. Returned in
114
+ * `grabProps`, so a consumer already spreading those gets the wake, the
115
+ * travel and the dwell with no change. */
116
+ const grabRef = useRef(null)
117
+ useGrabEdge(grabRef)
105
118
  const drag = useRef(null) // { startX, startW, snapPx, maxPx, moved } during a drag
106
119
  const defaultPx = useRef(null)
107
120
  const collapsedPx = useRef(null)
@@ -249,6 +262,11 @@ export default function useDragResize(ref, options = {}) {
249
262
  collapsed,
250
263
  toggleCollapsed,
251
264
  grabProps: {
265
+ ref: grabRef,
266
+ /* the pill is drawn by `.kol-rail-grab` (kol-animation.css) — the hook
267
+ * only supplies `is-near` and `--kol-rail-grab-y`. A consumer's own
268
+ * className, spread after this, still wins. */
269
+ className: 'kol-rail-grab',
252
270
  role: 'separator',
253
271
  'aria-orientation': 'vertical',
254
272
  'aria-label': 'Resize navigation',