@kolkrabbi/kol-framework 0.21.1 → 0.23.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 +39 -1
- package/package.json +3 -3
- package/src/ShellHeader.jsx +22 -1
- package/src/SideNav.jsx +6 -1
package/kol-framework.css
CHANGED
|
@@ -19,6 +19,15 @@ body {
|
|
|
19
19
|
color: var(--kol-surface-on-primary);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/* Reserve the scrollbar gutter so a full-bleed surface cannot induce a
|
|
23
|
+
horizontal scroll when a vertical scrollbar appears (2026-08-15, promoted
|
|
24
|
+
from kol-fxr's index.css — a consumer's entry stylesheet is for IMPORTS,
|
|
25
|
+
and this is chrome every app on this framework wants). Consumers on an
|
|
26
|
+
overlay-scrollbar platform pay nothing; the property is inert there. */
|
|
27
|
+
:root {
|
|
28
|
+
scrollbar-gutter: stable;
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
/* The global user-select:none gate + allowlist was REMOVED 2026-07-29 (user
|
|
23
32
|
* law, brand-app ruling): text is selectable everywhere — copyability beats
|
|
24
33
|
* chrome purity. */
|
|
@@ -143,6 +152,25 @@ body {
|
|
|
143
152
|
}
|
|
144
153
|
.kol-sidenav-footer { padding-inline: 1.5rem 1rem; }
|
|
145
154
|
|
|
155
|
+
/* The aside's OWN box (SideNavMobilePosition, kol-website 2026-08-25) — the
|
|
156
|
+
* hop's law above, one element up. `sticky top-0 self-start h-dvh flex
|
|
157
|
+
* flex-col z-20` rode the className, and a utility outranks every rule in
|
|
158
|
+
* this layer — so the 767px drawer block below (`position: fixed`) never won:
|
|
159
|
+
* the aside stayed IN FLOW, the one-column grid handed it an 852px row, and
|
|
160
|
+
* every consumer page opened one viewport down on phones (probe: aside
|
|
161
|
+
* `sticky`, content top=852 at 393; the consumer's stopgap was one media rule
|
|
162
|
+
* re-stating `fixed`). Declared here, the drawer rule beats it by source
|
|
163
|
+
* order — which is how it was written to work. */
|
|
164
|
+
.kol-sidenav {
|
|
165
|
+
position: sticky;
|
|
166
|
+
top: 0;
|
|
167
|
+
align-self: start;
|
|
168
|
+
height: 100dvh;
|
|
169
|
+
display: flex;
|
|
170
|
+
flex-direction: column;
|
|
171
|
+
z-index: var(--kol-z-sticky);
|
|
172
|
+
}
|
|
173
|
+
|
|
146
174
|
/* :root[data-sidenav="collapsed"] — the manual collapse, rebuilt DELIBERATELY
|
|
147
175
|
* 2026-08-09 (lobby: SideNavGrabResize) after the 2026-07-29 removal of the
|
|
148
176
|
* renderer-less stamp. SideNav's grab edge (useDragResize.js) owns the state
|
|
@@ -885,7 +913,15 @@ body {
|
|
|
885
913
|
}
|
|
886
914
|
}
|
|
887
915
|
|
|
888
|
-
/* Tab strip — horizontally scrollable, scrollbar hidden.
|
|
916
|
+
/* Tab strip — horizontally scrollable, scrollbar hidden.
|
|
917
|
+
* Snaps to a tab's start on a flick (WorkshopShellMobile, kol-website
|
|
918
|
+
* 2026-08-25): the strip measured 613px of tabs in a 345px viewport and did
|
|
919
|
+
* scroll, but a swipe could settle on half a label. `proximity`, not
|
|
920
|
+
* `mandatory` — the last tabs can never reach a start position, and mandatory
|
|
921
|
+
* would refuse to show them. ShellHeader.jsx scrolls the active tab into view
|
|
922
|
+
* on load, to the same start edge, so the two never fight. NO edge fade: the
|
|
923
|
+
* 2026-07-28 ruling bans scroll-edge paint (the table wrapper's cover
|
|
924
|
+
* gradients died for it 2026-08-09). */
|
|
889
925
|
.kol-shell-header-tabs {
|
|
890
926
|
display: flex;
|
|
891
927
|
flex-wrap: nowrap;
|
|
@@ -894,6 +930,7 @@ body {
|
|
|
894
930
|
padding-top: 8px;
|
|
895
931
|
scrollbar-width: none;
|
|
896
932
|
-ms-overflow-style: none;
|
|
933
|
+
scroll-snap-type: x proximity;
|
|
897
934
|
}
|
|
898
935
|
|
|
899
936
|
.kol-shell-header-tabs::-webkit-scrollbar {
|
|
@@ -922,6 +959,7 @@ body {
|
|
|
922
959
|
border-bottom: 2px solid transparent;
|
|
923
960
|
transition: color 0.2s ease, border-color 0.2s ease;
|
|
924
961
|
text-decoration: none;
|
|
962
|
+
scroll-snap-align: start;
|
|
925
963
|
}
|
|
926
964
|
|
|
927
965
|
.kol-shell-header-tab:hover {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.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",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"app-shell"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@kolkrabbi/kol-
|
|
32
|
-
"@kolkrabbi/kol-
|
|
31
|
+
"@kolkrabbi/kol-icons": "^0.18.0",
|
|
32
|
+
"@kolkrabbi/kol-component": "^0.68.1"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"react": "^18.3.0 || ^19.0.0",
|
package/src/ShellHeader.jsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useLayoutEffect, useRef } from 'react'
|
|
1
2
|
import { Icon } from '@kolkrabbi/kol-icons'
|
|
2
3
|
import { IconFrame, SearchInput, Tooltip, glyphSize } from '@kolkrabbi/kol-component'
|
|
3
4
|
import ThemeToggle from './ThemeToggle.jsx'
|
|
@@ -89,6 +90,26 @@ export default function ShellHeader({
|
|
|
89
90
|
}) {
|
|
90
91
|
const hasTabRow = nav.length > 0 || Boolean(search) || Boolean(onNavToggle) || Boolean(onTocToggle)
|
|
91
92
|
|
|
93
|
+
/* The current tab must be ON SCREEN (WorkshopShellMobile, kol-website
|
|
94
|
+
* 2026-08-25). The strip scrolls (.kol-shell-header-tabs) — but on a phone
|
|
95
|
+
* the active tab measured x=347–445 in a 345px strip: "Dashboard" was the
|
|
96
|
+
* page you were on and the one tab you could not see. Scrolled to the
|
|
97
|
+
* strip's start edge (the snap position), and ONLY when it is out of view,
|
|
98
|
+
* so a wide viewport never moves. Keyed on the active href, not `nav` —
|
|
99
|
+
* consumers rebuild that array every render. Layout effect: before paint,
|
|
100
|
+
* so a route change never flashes the wrong tab. */
|
|
101
|
+
const tabsRef = useRef(null)
|
|
102
|
+
const activeHref = nav.find((item) => (isActive ? isActive(item.href) : false))?.href
|
|
103
|
+
useLayoutEffect(() => {
|
|
104
|
+
const strip = tabsRef.current
|
|
105
|
+
const tab = strip?.querySelector('[aria-current="page"]')
|
|
106
|
+
if (!strip || !tab) return
|
|
107
|
+
const s = strip.getBoundingClientRect()
|
|
108
|
+
const t = tab.getBoundingClientRect()
|
|
109
|
+
if (t.left >= s.left && t.right <= s.right) return
|
|
110
|
+
strip.scrollLeft += t.left - s.left
|
|
111
|
+
}, [activeHref])
|
|
112
|
+
|
|
92
113
|
return (
|
|
93
114
|
<header className={`kol-shell-header sticky top-0 z-50 shrink-0 bg-surface-primary ${className}`.trim()}>
|
|
94
115
|
{/* Row 1: brand block + controls */}
|
|
@@ -139,7 +160,7 @@ export default function ShellHeader({
|
|
|
139
160
|
{hasTabRow && (
|
|
140
161
|
<div className="border-b border-fg-08">
|
|
141
162
|
<div className="w-full" style={{ paddingInline: 'var(--kol-pad-chrome-x)' }}>
|
|
142
|
-
<div className="kol-shell-header-tabs">
|
|
163
|
+
<div className="kol-shell-header-tabs" ref={tabsRef}>
|
|
143
164
|
<nav className="flex flex-1 gap-6" aria-label="Sections">
|
|
144
165
|
{nav.map((item) => {
|
|
145
166
|
const active = isActive ? isActive(item.href) : false
|
package/src/SideNav.jsx
CHANGED
|
@@ -93,7 +93,12 @@ export default function SideNav({
|
|
|
93
93
|
return (
|
|
94
94
|
<aside
|
|
95
95
|
ref={asideRef}
|
|
96
|
-
|
|
96
|
+
/* Box utilities deliberately absent (SideNavMobilePosition, 2026-08-25):
|
|
97
|
+
* the aside's position/height/stack live in .kol-sidenav
|
|
98
|
+
* (kol-framework.css). `sticky … h-dvh z-20` here outranked the 767px
|
|
99
|
+
* drawer rule's `position: fixed`, so every consumer page opened one
|
|
100
|
+
* viewport down on phones. Only colour utilities ride the element. */
|
|
101
|
+
className={`kol-sidenav${background ? ' bg-surface-primary border-r border-fg-08' : ''}${collapsed ? ' is-collapsed' : ''}${drawerOpen ? ' is-drawer-open' : ''}`}
|
|
97
102
|
>
|
|
98
103
|
{/* NO chip Button in either state (user build order 2026-08-09 — "MAKE
|
|
99
104
|
* IT"; supersedes the elder A/B): the pill-marked grab edge below is
|