@kolkrabbi/kol-framework 0.38.0 → 0.40.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/README.md +5 -5
- package/package.json +4 -4
- package/src/PageHero.jsx +59 -0
- package/src/{AppShell.jsx → PageLayout.jsx} +63 -8
- package/src/PageSection.jsx +16 -4
- package/src/ThemeToggle.jsx +3 -1
- package/src/index.js +4 -5
- package/src/BrandHero.jsx +0 -14
- package/src/SubPageHero.jsx +0 -27
package/README.md
CHANGED
|
@@ -26,17 +26,17 @@ chrome above the theme's type layer and the same package renders differently
|
|
|
26
26
|
per consumer.
|
|
27
27
|
|
|
28
28
|
```jsx
|
|
29
|
-
import {
|
|
30
|
-
import { NAV_TREE
|
|
29
|
+
import { PageLayout } from '@kolkrabbi/kol-framework'
|
|
30
|
+
import { NAV_TREE } from './sidebars.config'
|
|
31
31
|
|
|
32
|
-
<
|
|
32
|
+
<PageLayout navTree={NAV_TREE} pageWash="var(--kol-fg-02)" />
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
Exports: `AppShell
|
|
35
|
+
Exports: `PageLayout` (alias `AppShell`), `SideNav`, `ThemeToggle`, `Layout`, `PageHero` (aliases `BrandHero`, `SubPageHero`), `PageSection`, `PortalFooter`, `ScrollToTop`.
|
|
36
36
|
|
|
37
37
|
## Tailwind v4 consumers
|
|
38
38
|
|
|
39
|
-
Tailwind skips `node_modules` when scanning, so add this next to the imports above — otherwise the shell's layout utilities never generate and the
|
|
39
|
+
Tailwind skips `node_modules` when scanning, so add this next to the imports above — otherwise the shell's layout utilities never generate and the PageLayout grid collapses:
|
|
40
40
|
|
|
41
41
|
```css
|
|
42
42
|
@source "../node_modules/@kolkrabbi/kol-framework/src";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.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",
|
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
"./kol-brand-color.css": "./kol-brand-color.css"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"@kolkrabbi/kol-component": ">=0.
|
|
16
|
+
"@kolkrabbi/kol-component": ">=0.176.0",
|
|
17
17
|
"@kolkrabbi/kol-icons": ">=0.18.0",
|
|
18
|
-
"@kolkrabbi/kol-theme": ">=0.
|
|
18
|
+
"@kolkrabbi/kol-theme": ">=0.134.0",
|
|
19
19
|
"react": "^18.3.0 || ^19.0.0",
|
|
20
20
|
"react-dom": "^18.3.0 || ^19.0.0",
|
|
21
21
|
"react-router-dom": "^6.0.0 || ^7.0.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@kolkrabbi/kol-component": "^0.
|
|
24
|
+
"@kolkrabbi/kol-component": "^0.176.0",
|
|
25
25
|
"@kolkrabbi/kol-icons": "^0.25.0"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
package/src/PageHero.jsx
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Link } from 'react-router-dom'
|
|
2
|
+
import { SectionText } from '@kolkrabbi/kol-component'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* PageHero — the page's opening band: label · title · lede, with an optional
|
|
6
|
+
* mark beside the text and an optional back link above it.
|
|
7
|
+
*
|
|
8
|
+
* ONE HERO (page-family-is-not-a-set, kol-client-olina 2026-09-03; user, on
|
|
9
|
+
* the brand kit: *"why do 3 of 4 prefix PAGE if they are a set?"*). `BrandHero`
|
|
10
|
+
* and `SubPageHero` were the same core with one optional slot each — a `mark`
|
|
11
|
+
* on one, a back link on the other — under two prefixes, and each hand-stacked
|
|
12
|
+
* its own label / title / lede. Both names alias this; the retirements ledger
|
|
13
|
+
* carries them.
|
|
14
|
+
*
|
|
15
|
+
* COMPOSES `SectionText`, the page tier's base, the way `PageHeader` does. The
|
|
16
|
+
* voices are `BrandHero`'s verbatim — `.kol-prose-label` · `.kol-prose-display`
|
|
17
|
+
* · `.kol-prose-lede` (kol-theme's section-head trio, each carrying its own
|
|
18
|
+
* bottom margin, so the block's `gap` is 0 and the rhythm is the classes').
|
|
19
|
+
* `SubPageHero` had no package consumer, so its private voices did not survive
|
|
20
|
+
* the merge; its back link did, class-for-class.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} id section id (default `hero`) — the sidenav's scroll-spy anchor
|
|
23
|
+
* @param {string} backTo route for the back link; unset renders no link
|
|
24
|
+
* @param {ReactNode} backLabel the back link's text
|
|
25
|
+
* @param {ReactNode} label eyebrow above the title
|
|
26
|
+
* @param {ReactNode} title the h1
|
|
27
|
+
* @param {ReactNode} lede standfirst under the title, measured at 60ch
|
|
28
|
+
* @param {ReactNode} mark a node beside the text column (a logo); the text sits right of it
|
|
29
|
+
* @param {string} className extra classes on the section
|
|
30
|
+
*/
|
|
31
|
+
export default function PageHero({ id = 'hero', backTo, backLabel, label, title, lede, mark, className = '' }) {
|
|
32
|
+
return (
|
|
33
|
+
<section id={id} className={`kol-page-hero ${className}`.trim()}>
|
|
34
|
+
{backTo && (
|
|
35
|
+
<Link
|
|
36
|
+
to={backTo}
|
|
37
|
+
className="kol-back-link kol-helper-12 tracking-widest text-body hover:text-emphasis no-underline"
|
|
38
|
+
>
|
|
39
|
+
{backLabel}
|
|
40
|
+
</Link>
|
|
41
|
+
)}
|
|
42
|
+
<div className="flex items-center gap-12 flex-wrap">
|
|
43
|
+
{mark}
|
|
44
|
+
<div className="flex-1 min-w-[280px]">
|
|
45
|
+
<SectionText
|
|
46
|
+
eyebrow={label}
|
|
47
|
+
eyebrowClass="kol-prose-label"
|
|
48
|
+
headline={title}
|
|
49
|
+
headlineAs="h1"
|
|
50
|
+
headlineClass="kol-prose-display"
|
|
51
|
+
body={lede}
|
|
52
|
+
bodyClass="kol-prose-lede max-w-[60ch]"
|
|
53
|
+
gap="gap-0"
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</section>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
@@ -21,7 +21,43 @@ export const ShellTocCollapsedContext = createContext(null)
|
|
|
21
21
|
/* `sideNav` — every SideNav prop AppShell does not own, spread onto the rail
|
|
22
22
|
* (sidenav-rail-hairline, 2026-08-26): `background`, `hairline`, `isActive`,
|
|
23
23
|
* `onNavigate` … the next rail option needs no AppShell release. */
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* PageLayout — the brand-book page layout: the `.kol-brand-layout` grid with
|
|
26
|
+
* the sidenav in its first track, the page plane in the second, the mobile
|
|
27
|
+
* drawer + hamburger, the modal provider and the optional TOC rail.
|
|
28
|
+
*
|
|
29
|
+
* WAS `AppShell` (page-family-is-not-a-set, kol-client-olina 2026-09-03). The
|
|
30
|
+
* ticket asked for a `PageLayout` to ship beside the fifteen `.kol-brand-layout`
|
|
31
|
+
* rules kol-framework already owned — every consumer `BrandLayout.jsx` in the
|
|
32
|
+
* estate is a copy of this file's markup. The component existed; the name did
|
|
33
|
+
* not say so, and it collided with kol-shell's `AppShell` (the 48px app rail).
|
|
34
|
+
* Renamed, `AppShell` aliased on the retirements ledger, and the two things the
|
|
35
|
+
* forks carried that it lacked folded in:
|
|
36
|
+
*
|
|
37
|
+
* pageWash the plane's wash over the primary back — the SAME prop and
|
|
38
|
+
* variable as kol-shell's `AppShell` (`--kol-shell-page-wash`; there
|
|
39
|
+
* `PageShell` paints it, here the plane does), so a brand page and a
|
|
40
|
+
* monitor / mirror / fxr page step their lightness with one name
|
|
41
|
+
* (user 2026-08-27). A translucent `fg-*` over `surface-primary`,
|
|
42
|
+
* never an opaque swap. Unset = the plane IS the back.
|
|
43
|
+
* bare the plane and the outlet only — no grid, no rail, no drawer. The
|
|
44
|
+
* forks' `?embed=1` branch, for iframing a page into another site.
|
|
45
|
+
*
|
|
46
|
+
* THE BACK IS `surface-primary` (BrandLayout's line, user rulings 2026-08-24 →
|
|
47
|
+
* 08-27): the rail sits on it, the plane wears the wash. It was
|
|
48
|
+
* `surface-tertiary` here (appshell-content-surface, kol-studio 2026-08-26) —
|
|
49
|
+
* one component takes one model, and kol-studio passes its wash.
|
|
50
|
+
*
|
|
51
|
+
* @param {Array} navTree the sidenav's tree (see SideNav)
|
|
52
|
+
* @param {Function} getActivePage accepted, inert since 2026-08-09
|
|
53
|
+
* @param {ReactNode} header above the outlet on the plane
|
|
54
|
+
* @param {ReactNode} footer below the outlet on the plane
|
|
55
|
+
* @param {ReactNode} defaultTocContent the TOC rail's content when no page registers one
|
|
56
|
+
* @param {object} sideNav every SideNav prop this layout does not own, spread onto the rail
|
|
57
|
+
* @param {string} pageWash the plane's wash, a CSS colour (e.g. `'var(--kol-fg-02)'`)
|
|
58
|
+
* @param {boolean} bare plane + outlet only (default false)
|
|
59
|
+
*/
|
|
60
|
+
export default function PageLayout({ navTree = [], getActivePage: _getActivePage, header, footer, defaultTocContent, sideNav, pageWash, bare = false }) {
|
|
25
61
|
const [drawerOpen, setDrawerOpen] = useState(false)
|
|
26
62
|
const [tocContent, setTocContent] = useState(null)
|
|
27
63
|
const [tocCollapsed, setTocCollapsed] = useState(false)
|
|
@@ -39,17 +75,36 @@ export default function AppShell({ navTree = [], getActivePage: _getActivePage,
|
|
|
39
75
|
const effectiveTocContent = tocContent ?? defaultTocContent
|
|
40
76
|
const showToc = Boolean(effectiveTocContent) && !tocCollapsed
|
|
41
77
|
|
|
78
|
+
/* the plane paints the wash; the var lives on the plane so any child that
|
|
79
|
+
reads it resolves the same value (BrandLayout, verbatim) */
|
|
80
|
+
const plane = {
|
|
81
|
+
'--kol-shell-page-wash': pageWash,
|
|
82
|
+
background: 'var(--kol-shell-page-wash, var(--kol-surface-primary))',
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (bare) {
|
|
86
|
+
return (
|
|
87
|
+
<ModalProvider>
|
|
88
|
+
<ShellTocContext.Provider value={setTocContent}>
|
|
89
|
+
<ShellTocCollapsedContext.Provider value={setTocCollapsed}>
|
|
90
|
+
<div className="min-h-dvh min-w-0" style={plane}>
|
|
91
|
+
<Outlet />
|
|
92
|
+
</div>
|
|
93
|
+
</ShellTocCollapsedContext.Provider>
|
|
94
|
+
</ShellTocContext.Provider>
|
|
95
|
+
</ModalProvider>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
42
99
|
return (
|
|
43
100
|
<ModalProvider>
|
|
44
101
|
<ShellTocContext.Provider value={setTocContent}>
|
|
45
102
|
<ShellTocCollapsedContext.Provider value={setTocCollapsed}>
|
|
46
103
|
<div
|
|
47
|
-
/* the
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
|
|
51
|
-
* AppShell app rendered one flat colour. */
|
|
52
|
-
className="kol-brand-layout bg-surface-tertiary min-h-dvh"
|
|
104
|
+
/* TWO surfaces: the BACK is surface-primary, the rail sits on it,
|
|
105
|
+
* and the PLANE wears `pageWash` over it (BrandLayout's line,
|
|
106
|
+
* 2026-08-24 → 08-27). */
|
|
107
|
+
className="kol-brand-layout bg-surface-primary min-h-dvh"
|
|
53
108
|
data-drawer-open={drawerOpen ? 'true' : undefined}
|
|
54
109
|
data-toc={showToc ? 'true' : undefined}
|
|
55
110
|
>
|
|
@@ -70,7 +125,7 @@ export default function AppShell({ navTree = [], getActivePage: _getActivePage,
|
|
|
70
125
|
/>
|
|
71
126
|
|
|
72
127
|
<SideNav navTree={navTree} drawerOpen={drawerOpen} onCloseDrawer={() => setDrawerOpen(false)} {...sideNav} />
|
|
73
|
-
<div className="min-w-0">
|
|
128
|
+
<div className="min-w-0" style={plane}>
|
|
74
129
|
{header}
|
|
75
130
|
<Outlet />
|
|
76
131
|
{footer}
|
package/src/PageSection.jsx
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import { Divider } from '@kolkrabbi/kol-component'
|
|
1
|
+
import { Divider, SectionText } from '@kolkrabbi/kol-component'
|
|
2
2
|
|
|
3
|
+
/* The chapter section of the brand-book page kit — `PageHero` · `PageSection`
|
|
4
|
+
* · `PageLayout`. Its head COMPOSES `SectionText` (page-family-is-not-a-set,
|
|
5
|
+
* 2026-09-03) instead of hand-stacking label / title / lede: the voices are the
|
|
6
|
+
* same `.kol-prose-*` trio as before, passed as the base's seams, and each class
|
|
7
|
+
* carries its own bottom margin, so the block's gap is 0 and nothing moved. */
|
|
3
8
|
export default function PageSection({ id, label, title, body, children, className = '', fullbleed = false, divider = false }) {
|
|
4
9
|
const hasHead = label || title || body
|
|
5
10
|
const cls = [
|
|
@@ -16,9 +21,16 @@ export default function PageSection({ id, label, title, body, children, classNam
|
|
|
16
21
|
* exactly the panel token; `720px` was an improvised near-miss and
|
|
17
22
|
* becomes the column token — 768px, so section heads widen by 48px. */
|
|
18
23
|
<header className={fullbleed ? 'max-w-[var(--kol-content-panel)]' : 'max-w-[var(--kol-content-column)]'}>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
<SectionText
|
|
25
|
+
eyebrow={label}
|
|
26
|
+
eyebrowClass="kol-prose-label"
|
|
27
|
+
headline={title}
|
|
28
|
+
headlineAs="h2"
|
|
29
|
+
headlineClass="kol-prose-title"
|
|
30
|
+
body={body}
|
|
31
|
+
bodyClass="kol-prose-lede"
|
|
32
|
+
gap="gap-0"
|
|
33
|
+
/>
|
|
22
34
|
</header>
|
|
23
35
|
)}
|
|
24
36
|
{children}
|
package/src/ThemeToggle.jsx
CHANGED
|
@@ -90,7 +90,9 @@ export default function ThemeToggle({
|
|
|
90
90
|
className = '',
|
|
91
91
|
}) {
|
|
92
92
|
const { theme, mode, cycle, clear } = useTheme()
|
|
93
|
-
|
|
93
|
+
/* six tones (tone-is-the-ground-axis, 2026-09-03); unset inherits the wrapper's */
|
|
94
|
+
const TONE_CLASS = { primary: 'kol-tone-primary', secondary: 'kol-tone-secondary', outline: 'kol-tone-outline', ghost: 'kol-tone-ghost', grey: 'kol-tone-grey', sunken: 'kol-tone-sunken', inverse: 'kol-tone-sunken' }
|
|
95
|
+
const extra = `${TONE_CLASS[tone] ?? ''} ${className}`.trim()
|
|
94
96
|
|
|
95
97
|
/* Label and slot follow the RESOLVED theme, not the stored choice: when the
|
|
96
98
|
* page is unset, what the user is looking at is the OS's answer, and the
|
package/src/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @kol/framework — shared KOL app shell.
|
|
3
3
|
*
|
|
4
|
-
* Site chrome (sidenav, layout, theme toggle, footer,
|
|
4
|
+
* Site chrome (sidenav, page layout, theme toggle, footer, the page kit) consumed across
|
|
5
5
|
* apps. SideNav takes its nav data as props (navTree) so the tree stays
|
|
6
6
|
* app-local. CSS lives in src/styles: kol-framework.css
|
|
7
7
|
* and kol-brand-color.css.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
export { default as AppShell } from './
|
|
11
|
-
export { ShellTocContext, ShellTocCollapsedContext } from './
|
|
10
|
+
export { default as PageLayout, default as AppShell } from './PageLayout.jsx'
|
|
11
|
+
export { ShellTocContext, ShellTocCollapsedContext } from './PageLayout.jsx'
|
|
12
12
|
export { default as SideNav } from './SideNav.jsx'
|
|
13
13
|
export { default as useDragResize } from './useDragResize.js'
|
|
14
14
|
export { default as ShellHeader, HEADER_ICON } from './ShellHeader.jsx'
|
|
@@ -18,5 +18,4 @@ export { default as Layout } from './Layout.jsx'
|
|
|
18
18
|
export { default as PageSection } from './PageSection.jsx'
|
|
19
19
|
export { default as PortalFooter } from './PortalFooter.jsx'
|
|
20
20
|
export { default as ScrollToTop } from './ScrollToTop.jsx'
|
|
21
|
-
export { default as BrandHero } from './
|
|
22
|
-
export { default as SubPageHero } from './SubPageHero.jsx'
|
|
21
|
+
export { default as PageHero, default as BrandHero, default as SubPageHero } from './PageHero.jsx'
|
package/src/BrandHero.jsx
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export default function BrandHero({ id = 'hero', label, title, lede, mark }) {
|
|
2
|
-
return (
|
|
3
|
-
<section id={id} className="kol-page-hero">
|
|
4
|
-
{label && <p className="kol-prose-label">{label}</p>}
|
|
5
|
-
<div className="flex items-center gap-12 flex-wrap">
|
|
6
|
-
{mark}
|
|
7
|
-
<div className="flex-1 min-w-[280px]">
|
|
8
|
-
<h1 className="kol-prose-display">{title}</h1>
|
|
9
|
-
{lede && <p className="kol-prose-lede max-w-[60ch]">{lede}</p>}
|
|
10
|
-
</div>
|
|
11
|
-
</div>
|
|
12
|
-
</section>
|
|
13
|
-
)
|
|
14
|
-
}
|
package/src/SubPageHero.jsx
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Link } from 'react-router-dom'
|
|
2
|
-
|
|
3
|
-
export default function SubPageHero({ backTo, backLabel, label, title, lede }) {
|
|
4
|
-
return (
|
|
5
|
-
<section className="kol-page-hero" id="hero">
|
|
6
|
-
{backTo && (
|
|
7
|
-
<Link
|
|
8
|
-
to={backTo}
|
|
9
|
-
className="kol-back-link kol-helper-12 tracking-widest text-body hover:text-emphasis no-underline"
|
|
10
|
-
>
|
|
11
|
-
{backLabel}
|
|
12
|
-
</Link>
|
|
13
|
-
)}
|
|
14
|
-
{label && (
|
|
15
|
-
<p className="kol-helper-12 tracking-widest text-meta m-0 mb-4">
|
|
16
|
-
{label}
|
|
17
|
-
</p>
|
|
18
|
-
)}
|
|
19
|
-
<div className="flex-1 min-w-[280px]">
|
|
20
|
-
<h1 className="kol-sans-display-01 text-auto m-0 mb-6">{title}</h1>
|
|
21
|
-
{lede && (
|
|
22
|
-
<p className="kol-sans-body-01 text-body m-0 max-w-[60ch]">{lede}</p>
|
|
23
|
-
)}
|
|
24
|
-
</div>
|
|
25
|
-
</section>
|
|
26
|
-
)
|
|
27
|
-
}
|