@kolkrabbi/kol-shell 0.12.0 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-shell",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "private": false,
5
5
  "description": "KOL application shell — fixed 48px NavRail + AppShell layout root, PageShell/PageHeader scaffolds, ContentFilters catalog organism, GridCard, SettingsScaffold, WalkthroughPanel, ShortcutsOverlay. App chrome (kol-framework owns site chrome). Nav items, content, shortcuts and settings are consumer-injected. Sits above @kolkrabbi/kol-{theme,component,framework}.",
6
6
  "license": "MIT",
@@ -13,17 +13,17 @@
13
13
  },
14
14
  "peerDependencies": {
15
15
  "@kolkrabbi/kol-component": ">=0.108.0",
16
- "@kolkrabbi/kol-framework": ">=0.20.0",
16
+ "@kolkrabbi/kol-framework": ">=0.31.0",
17
17
  "@kolkrabbi/kol-icons": ">=0.16.0",
18
- "@kolkrabbi/kol-theme": ">=0.68.0",
18
+ "@kolkrabbi/kol-theme": ">=0.80.0",
19
19
  "react": "^18.3.0 || ^19.0.0",
20
20
  "react-dom": "^18.3.0 || ^19.0.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@kolkrabbi/kol-component": "^0.118.2",
24
- "@kolkrabbi/kol-framework": "^0.30.0",
23
+ "@kolkrabbi/kol-component": "^0.118.3",
24
+ "@kolkrabbi/kol-framework": "^0.31.0",
25
25
  "@kolkrabbi/kol-icons": "^0.24.0",
26
- "@kolkrabbi/kol-theme": "^0.78.1"
26
+ "@kolkrabbi/kol-theme": "^0.80.0"
27
27
  },
28
28
  "files": [
29
29
  "src",
package/src/AppShell.jsx CHANGED
@@ -4,17 +4,25 @@ import { NavHiddenContext } from './navHidden.js'
4
4
  import TouchDeviceOverlay, { useTouchPrimary } from './TouchDeviceOverlay.jsx'
5
5
 
6
6
  /**
7
- * AppShell — layout root: fixed NavRail + content offset by the rail width.
8
- * No header, no footer. Router-agnostic: render your router's element (e.g.
9
- * `<Outlet/>`) as children and wire `currentPath`/`onNavigate` from your
10
- * router at the call site.
7
+ * AppShell — layout root: the rail + the content column. No header, no
8
+ * footer. Router-agnostic: render your router's element (e.g. `<Outlet/>`) as
9
+ * children and wire `currentPath`/`onNavigate` from your router at the call
10
+ * site.
11
11
  *
12
- * Rail width is `--kol-shell-rail-width` (kol-theme) read by both the rail
13
- * and the offset so they can never disagree.
12
+ * THE RAIL IS A SIDENAV (RailSideNavPixelParity, kol-fxr 2026-08-28user:
13
+ * "same component both states super nice"): since 0.13.0 the root is the
14
+ * brand grid, `.kol-brand-layout` (kol-framework.css — `--kol-sidenav-w` ×
15
+ * `minmax(0, 1fr)`, the collapsed column on the `data-sidenav` stamp, the
16
+ * ≤1024 forced rail and the ≤767 drawer), and `NavRail` mounts kol-framework's
17
+ * `SideNav` collapsed in it. The grab edge opens it to the brand sidebar and
18
+ * the drag is the transition; the content column follows the grid, so the old
19
+ * fixed rail + `margin-left` offset is gone. `--kol-shell-rail-width`
20
+ * (kol-theme ≥0.80.0) is LIVE — the sidenav's width in its current state —
21
+ * for consumers that offset a fixed element by it; hidden rail = `0px`.
14
22
  *
15
23
  * @param {Array} props.items nav items `{ icon, path, label }`
16
24
  * @param {Array} props.bottomItems items pinned below the theme toggle
17
- * @param {Object} props.logomark `{ svgUrl, title }` — top mark, navigates to '/'
25
+ * @param {Object} props.logomark `{ svgUrl, title }` — the rail's footer mark, navigates to '/'
18
26
  * @param {string} props.currentPath the router's current pathname
19
27
  * @param {Function} props.onNavigate `(path) => void`
20
28
  * @param {ElementType} props.iconComponent icon renderer seam (see Button)
@@ -101,8 +109,13 @@ export default function AppShell({
101
109
 
102
110
  return (
103
111
  <NavHiddenContext.Provider value={{ navHidden, setNavHidden }}>
104
- {/* `kol-app-shell` = the app tier: neutral ::selection (kol-theme) */}
105
- <div className="kol-app-shell contents">
112
+ {/* `kol-app-shell` = the app tier: neutral ::selection (kol-theme).
113
+ * `kol-brand-layout` = the sidenav grid; a hidden rail collapses the
114
+ * grid to the content column and zeroes the live width token. */}
115
+ <div
116
+ className="kol-app-shell kol-brand-layout min-h-dvh bg-surface-primary"
117
+ style={navHidden ? { gridTemplateColumns: 'minmax(0, 1fr)', '--kol-shell-rail-width': '0px' } : undefined}
118
+ >
106
119
  {touch === 'overlay' && <TouchDeviceOverlay appName={appName} />}
107
120
  {!navHidden && (
108
121
  <NavRail
@@ -117,7 +130,7 @@ export default function AppShell({
117
130
  )}
118
131
  {/* THE BACK OF THE BACK — surface-primary, always, in every app; the
119
132
  * page paints its wash over it (ShellPageWash). */}
120
- <div className="bg-surface-primary" style={{ marginLeft: navHidden ? 0 : 'var(--kol-shell-rail-width)', '--kol-shell-page-wash': pageWash }}>
133
+ <div className="bg-surface-primary min-w-0" style={{ '--kol-shell-page-wash': pageWash }}>
121
134
  {children}
122
135
  </div>
123
136
  </div>
package/src/NavRail.jsx CHANGED
@@ -1,35 +1,23 @@
1
- import { Button } from '@kolkrabbi/kol-component'
2
- import { ThemeToggle } from '@kolkrabbi/kol-framework'
1
+ import { SideNav } from '@kolkrabbi/kol-framework'
3
2
  import Logomark from './Logomark.jsx'
4
3
 
5
4
  /**
6
- * NavRail — the fixed 48px icon rail. Geometry/stacking live in kol-theme's
7
- * `.kol-shell-rail`; surface/border ride the theme utility classes.
5
+ * NavRail — AppShell's rail, which IS kol-framework's `SideNav`
6
+ * (RailSideNavPixelParity, kol-fxr 2026-08-28 user, on the brand sidebar's
7
+ * drag: "same component both states super nice"). Until 0.13.0 this was its
8
+ * own fixed 48px icon rail, and the first app to run it beside a collapsed
9
+ * SideNav on adjacent routes measured every icon moving on the route change
10
+ * (48 vs 56 wide, 20 vs 16px glyphs, 40 vs 38 pitch, first glyph at y 76 vs
11
+ * 26). There is no second component now: this file maps the shell's flat
12
+ * `{ icon, path, label }` items onto the sidenav's tree and mounts it
13
+ * collapsed — the pill-marked grab edge opens it to the brand sidebar, the
14
+ * drag is the transition, and it stays a rail while it navigates
15
+ * (`expandOnSelect={false}`).
8
16
  *
9
- * Items are flat `{ icon, path, label }` deliberately NOT kol-framework
10
- * SideNav's two-level navTree; different component, not a variant.
11
- *
12
- * Active state (user ruling 2026-08-12, carried natively in kol-theme):
13
- * ink `--kol-oq-96` in every state, hover = the `--kol-oq-04` wash, active
14
- * route = that wash held on via `aria-current="page"` — never
15
- * `selected`/`pressed`; navigation is location, not a toggled tool.
17
+ * The bottom, specified once: theme slot `bottomItems` (pinned rows)
18
+ * footer. The `logomark` moved from the top of the rail to the footer — the
19
+ * brand sidebar's mark sits there, and the first glyph's y is the sidenav's.
16
20
  */
17
-
18
- function RailItem({ icon, path, label, active, onNavigate, iconComponent }) {
19
- return (
20
- <Button
21
- iconOnly={icon}
22
- iconSize={20}
23
- variant="nav"
24
- size="md"
25
- aria-current={active ? 'page' : undefined}
26
- onClick={() => onNavigate?.(path)}
27
- title={label}
28
- iconComponent={iconComponent}
29
- />
30
- )
31
- }
32
-
33
21
  export default function NavRail({
34
22
  items = [],
35
23
  bottomItems = [],
@@ -41,42 +29,32 @@ export default function NavRail({
41
29
  hidden = false,
42
30
  }) {
43
31
  if (hidden) return null
44
- const isActive = (path) =>
45
- path === '/' ? currentPath === '/' : currentPath.startsWith(path)
46
-
32
+ const node = (item) => ({ id: item.path, label: item.label, icon: item.icon, to: item.path })
33
+ /* the shell's route match: '/' exact, anything else by prefix */
34
+ const isActive = (to) => (to === '/' ? currentPath === '/' : currentPath.startsWith(to))
47
35
  return (
48
- <div className="kol-shell-rail bg-surface-tertiary border-r border-fg-04">
49
- {logomark && (
50
- <div
36
+ <SideNav
37
+ navTree={items.map(node)}
38
+ bottomItems={bottomItems.map(node)}
39
+ currentPath={currentPath}
40
+ isActive={isActive}
41
+ onNavigate={(e, to) => onNavigate?.(to)}
42
+ iconComponent={iconComponent}
43
+ themeToggle={themeToggle}
44
+ expandOnSelect={false}
45
+ defaultCollapsed
46
+ hairline
47
+ footer={logomark ? (
48
+ <button
49
+ type="button"
50
+ className="inline-flex items-center bg-transparent border-0 p-0 cursor-pointer text-body hover:text-emphasis"
51
51
  onClick={() => onNavigate?.('/')}
52
- className="text-oq-96"
53
- style={{ cursor: 'pointer', marginBottom: 16, paddingTop: 4 }}
54
52
  title={logomark.title}
53
+ aria-label={logomark.title}
55
54
  >
56
55
  <Logomark svgUrl={logomark.svgUrl} size={20} />
57
- </div>
58
- )}
59
- {items.map((item) => (
60
- <RailItem
61
- key={item.path}
62
- {...item}
63
- active={isActive(item.path)}
64
- onNavigate={onNavigate}
65
- iconComponent={iconComponent}
66
- />
67
- ))}
68
- <div style={{ flex: 1 }} />
69
- {themeToggle && <ThemeToggle label={false} style={{ color: 'var(--kol-oq-96)' }} />}
70
- {bottomItems.map((item) => (
71
- <RailItem
72
- key={item.path}
73
- {...item}
74
- active={isActive(item.path)}
75
- onNavigate={onNavigate}
76
- iconComponent={iconComponent}
77
- />
78
- ))}
79
- <div style={{ height: 8 }} />
80
- </div>
56
+ </button>
57
+ ) : false}
58
+ />
81
59
  )
82
60
  }
package/src/index.js CHANGED
@@ -4,7 +4,8 @@
4
4
  * Lifted 2026-08-14 from the hand-copied twins in kol-monitor ("Monitor") and
5
5
  * kol-mirror ("Hall of Mirrors") — AppShellSet lobby brief. App chrome, not
6
6
  * site chrome: kol-framework's SideNav/footer/heroes are the site register;
7
- * this is the fixed 48px rail + page scaffolds an application is built from.
7
+ * this is the rail (a collapsed kol-framework SideNav since 0.13.0
8
+ * RailSideNavPixelParity) + the page scaffolds an application is built from.
8
9
  *
9
10
  * Router-agnostic: no react-router dependency — pass `currentPath` +
10
11
  * `onNavigate` and render your router's element as children.