@kolkrabbi/kol-framework 0.48.0 → 0.50.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 +6 -2
- package/package.json +5 -4
- package/src/PageHero.jsx +1 -1
- package/src/PageLayout.jsx +1 -1
- package/src/ThemeToggle.jsx +1 -1
- package/src/index.js +2 -0
- package/src/loaderApp.js +14 -0
- package/src/vite.js +71 -0
package/kol-framework.css
CHANGED
|
@@ -101,9 +101,12 @@ body {
|
|
|
101
101
|
deliberately tighter: chrome is not page content. The shell header and the
|
|
102
102
|
shell content frame both read this, so they cannot disagree; before it,
|
|
103
103
|
the header carried bare Tailwind steps and the frame carried the page ramp.
|
|
104
|
-
|
|
104
|
+
ONE STEP (user ruling 2026-10-02, the phone pass): on a phone it is the
|
|
105
|
+
page ladder's 20, so the header, the frame and a page all stand on one
|
|
106
|
+
edge — it was 24 at every width, which put a phone page 4px off the
|
|
107
|
+
ladder. 24 returns at 768, where the 2026-07-31 ruling was made.
|
|
105
108
|
Do NOT reach for this on page containers — that is --kol-pad-section-x. */
|
|
106
|
-
--kol-pad-chrome-x: var(--kol-spacing-
|
|
109
|
+
--kol-pad-chrome-x: var(--kol-spacing-5);
|
|
107
110
|
|
|
108
111
|
/* Stepped page / section / band padding tokens.
|
|
109
112
|
Used by chrome (topnav, sidenav) and landing bands. Scaled at 768 / 1024
|
|
@@ -117,6 +120,7 @@ body {
|
|
|
117
120
|
|
|
118
121
|
@media (min-width: 768px) {
|
|
119
122
|
:root {
|
|
123
|
+
--kol-pad-chrome-x: var(--kol-spacing-6);
|
|
120
124
|
--kol-pad-page-x: 32px;
|
|
121
125
|
--kol-pad-page-y: 96px;
|
|
122
126
|
--kol-pad-section-x: 32px;
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-framework",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.50.0",
|
|
4
|
+
"description": "Site shell and page layout",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./src/index.js",
|
|
8
8
|
"module": "./src/index.js",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": "./src/index.js",
|
|
11
|
+
"./vite": "./src/vite.js",
|
|
11
12
|
"./src/*": "./src/*",
|
|
12
13
|
"./kol-framework.css": "./kol-framework.css",
|
|
13
14
|
"./kol-brand-color.css": "./kol-brand-color.css"
|
|
@@ -21,8 +22,8 @@
|
|
|
21
22
|
"react-router-dom": "^6.0.0 || ^7.0.0"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
|
-
"@kolkrabbi/kol-component": "^0.
|
|
25
|
-
"@kolkrabbi/kol-icons": "^0.
|
|
25
|
+
"@kolkrabbi/kol-component": "^0.266.0",
|
|
26
|
+
"@kolkrabbi/kol-icons": "^0.39.0"
|
|
26
27
|
},
|
|
27
28
|
"files": [
|
|
28
29
|
"src",
|
package/src/PageHero.jsx
CHANGED
|
@@ -2,7 +2,7 @@ import { Link } from 'react-router-dom'
|
|
|
2
2
|
import { SectionText } from '@kolkrabbi/kol-component'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* PageHero — the page's opening band: label · title · lede, with an optional
|
|
5
|
+
* PageHero — A page's opening band. the page's opening band: label · title · lede, with an optional
|
|
6
6
|
* mark beside the text and an optional back link above it.
|
|
7
7
|
*
|
|
8
8
|
* ONE HERO (page-family-is-not-a-set, kol-client-olina 2026-09-03; user, on
|
package/src/PageLayout.jsx
CHANGED
|
@@ -24,7 +24,7 @@ export const ShellTocCollapsedContext = createContext(null)
|
|
|
24
24
|
* (sidenav-rail-hairline, 2026-08-26): `background`, `hairline`, `isActive`,
|
|
25
25
|
* `onNavigate` … the next rail option needs no AppShell release. */
|
|
26
26
|
/**
|
|
27
|
-
* PageLayout — the brand-book page layout: the `.kol-brand-layout` grid with
|
|
27
|
+
* PageLayout — The brand-book page layout. the brand-book page layout: the `.kol-brand-layout` grid with
|
|
28
28
|
* the sidenav in its first track, the page plane in the second, the mobile
|
|
29
29
|
* drawer + hamburger, the modal provider and the optional TOC rail.
|
|
30
30
|
*
|
package/src/ThemeToggle.jsx
CHANGED
|
@@ -3,7 +3,7 @@ import { Tooltip } from '@kolkrabbi/kol-component'
|
|
|
3
3
|
import { useTheme } from './theme.js'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Theme toggle —
|
|
6
|
+
* Theme toggle — Switch between light and dark. Two-position glyph-roll button (the approved 2-variant spec).
|
|
7
7
|
*
|
|
8
8
|
* Clicking cycles **light ↔ dark**. `system` is not a position — see the
|
|
9
9
|
* ruling above MODE_LABEL. Alt- or shift-click clears the choice and hands the
|
package/src/index.js
CHANGED
|
@@ -17,6 +17,8 @@ export { useDragResize } from '@kolkrabbi/kol-component'
|
|
|
17
17
|
export { default as ShellHeader, HEADER_ICON } from './ShellHeader.jsx'
|
|
18
18
|
export { default as ThemeToggle } from './ThemeToggle.jsx'
|
|
19
19
|
export { useTheme, applyTheme, getInitialTheme, THEME_STORAGE_KEY, THEME_BOOT_SCRIPT } from './theme.js'
|
|
20
|
+
/* the app loader's runtime half — the build half is `loaderApp()` in `@kolkrabbi/kol-framework/vite` */
|
|
21
|
+
export { hideLoaderApp } from './loaderApp.js'
|
|
20
22
|
export { default as Layout } from './Layout.jsx'
|
|
21
23
|
export { default as PageSection } from './PageSection.jsx'
|
|
22
24
|
export { default as PortalFooter } from './PortalFooter.jsx'
|
package/src/loaderApp.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hideLoaderApp — the app loader's other half (`loaderApp()` in `@kolkrabbi/kol-framework/vite` writes it into
|
|
3
|
+
* index.html). Call it once React has committed: two frames later the loader fades (240 ms, none under reduced
|
|
4
|
+
* motion — the CSS drops the transition) and leaves the DOM. Safe to call when there is no loader.
|
|
5
|
+
*/
|
|
6
|
+
export function hideLoaderApp() {
|
|
7
|
+
if (typeof document === 'undefined') return
|
|
8
|
+
const el = document.getElementById('kol-loader-app')
|
|
9
|
+
if (!el) return
|
|
10
|
+
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
11
|
+
el.classList.add('is-done')
|
|
12
|
+
setTimeout(() => { el.remove(); document.getElementById('kol-loader-app-css')?.remove() }, 300)
|
|
13
|
+
}))
|
|
14
|
+
}
|
package/src/vite.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugins for KOL apps — Node-side, no React (import from `@kolkrabbi/kol-framework/vite` in vite.config).
|
|
3
|
+
*
|
|
4
|
+
* devFavicon — THE DEV TAB IS TELLABLE AT A GLANCE (2026-10-17 — user: "I need to be able to tell which tabs
|
|
5
|
+
* are localhost dev and which are production"). On the dev server the favicon becomes a solid tile with the
|
|
6
|
+
* app's letters; a build never sees it (`apply: 'serve'`). It REPLACES the page's icon link: fxr appended a
|
|
7
|
+
* second one and browsers pick their own (Firefox kept prod's), mirror swapped it at runtime after load.
|
|
8
|
+
* Each app picks its letters and ground once — the table lives in 16-app-anatomy.md § Dev favicon.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} [opts]
|
|
11
|
+
* @param {string} [opts.label='DEV'] 1–3 letters, fitted to the tile
|
|
12
|
+
* @param {string} [opts.ground='#7C5CFF'] the tile's colour — unique per app
|
|
13
|
+
* @param {string} [opts.ink='#FFFFFF']
|
|
14
|
+
*/
|
|
15
|
+
export function devFavicon({ label = 'DEV', ground = '#7C5CFF', ink = '#FFFFFF' } = {}) {
|
|
16
|
+
const text = String(label).slice(0, 3).toUpperCase()
|
|
17
|
+
const size = text.length === 1 ? 28 : text.length === 2 ? 20 : 15
|
|
18
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="6" fill="${ground}"/>`
|
|
19
|
+
+ `<text x="16" y="${(16 + size * 0.36).toFixed(1)}" text-anchor="middle" font-family="Arial Black, Arial, sans-serif" font-weight="900" font-size="${size}"`
|
|
20
|
+
+ `${text.length > 1 ? ' textLength="28" lengthAdjust="spacingAndGlyphs"' : ''} fill="${ink}">${text}</text></svg>`
|
|
21
|
+
const link = `<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,${encodeURIComponent(svg)}">`
|
|
22
|
+
return {
|
|
23
|
+
name: 'kol-dev-favicon',
|
|
24
|
+
apply: 'serve',
|
|
25
|
+
transformIndexHtml(html) {
|
|
26
|
+
return /<link rel="icon"[^>]*>/.test(html) ? html.replace(/<link rel="icon"[^>]*>/, link) : html.replace('</head>', `${link}</head>`)
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* loaderApp — THE APP LOADER, OWNED ONCE (AppLoader, kol-monitor 2026-10-17 — user: *"shouldnt that loader just be
|
|
33
|
+
* owned in DS and consumed by the fxr monitor and mirror? with custom graphic input as prop?"*). The app's mark
|
|
34
|
+
* centred on the theme's ground, pulsing while the bundle downloads. It has to be static HTML in `index.html` —
|
|
35
|
+
* a React component would only appear after the wait it covers — so this plugin writes it there: fxr's loader,
|
|
36
|
+
* once, with the graphic as an option. Call `hideLoaderApp()` (kol-framework) after the first commit.
|
|
37
|
+
*
|
|
38
|
+
* Ground and ink follow the stored `data-theme`, else the system (run `THEME_BOOT_SCRIPT` before it, as every
|
|
39
|
+
* KOL app does). Reduced motion: no pulse, no fade.
|
|
40
|
+
*
|
|
41
|
+
* @param {Object} opts
|
|
42
|
+
* @param {string} opts.graphic an SVG — the markup itself, or a path (relative to the app root) to an .svg file;
|
|
43
|
+
* draw it in `currentColor` so it takes the ground's ink
|
|
44
|
+
* @param {number} [opts.size=64] the mark's edge in px
|
|
45
|
+
*/
|
|
46
|
+
export function loaderApp({ graphic, size = 64 } = {}) {
|
|
47
|
+
let root = process.cwd()
|
|
48
|
+
const css = `#kol-loader-app{position:fixed;inset:0;z-index:2147483647;display:grid;place-items:center;background:#FAFAFA;color:#0E0E11;transition:opacity 240ms ease}`
|
|
49
|
+
+ `@media (prefers-color-scheme: dark){:root:not([data-theme="light"]) #kol-loader-app{background:#121215;color:#FFFFFF}}`
|
|
50
|
+
+ `:root[data-theme="dark"] #kol-loader-app{background:#121215;color:#FFFFFF}`
|
|
51
|
+
+ `#kol-loader-app svg{width:${size}px;height:${size}px;animation:kol-loader-app-pulse 1.6s ease-in-out infinite}`
|
|
52
|
+
+ `#kol-loader-app.is-done{opacity:0;pointer-events:none}`
|
|
53
|
+
+ `@keyframes kol-loader-app-pulse{50%{opacity:.35}}`
|
|
54
|
+
+ `@media (prefers-reduced-motion: reduce){#kol-loader-app svg{animation:none}#kol-loader-app{transition:none}}`
|
|
55
|
+
return {
|
|
56
|
+
name: 'kol-loader-app',
|
|
57
|
+
configResolved(config) { root = config.root },
|
|
58
|
+
async transformIndexHtml(html) {
|
|
59
|
+
let svg = String(graphic ?? '').trim()
|
|
60
|
+
if (svg && !svg.startsWith('<')) {
|
|
61
|
+
const { readFile } = await import('node:fs/promises')
|
|
62
|
+
const { resolve } = await import('node:path')
|
|
63
|
+
svg = (await readFile(resolve(root, svg), 'utf8')).trim()
|
|
64
|
+
}
|
|
65
|
+
svg = svg.replace(/<\?xml[^>]*>/, '').replace(/<!--[\s\S]*?-->/g, '')
|
|
66
|
+
return html
|
|
67
|
+
.replace('</head>', `<style id="kol-loader-app-css">${css}</style></head>`)
|
|
68
|
+
.replace(/<body([^>]*)>/, `<body$1><div id="kol-loader-app" aria-hidden="true">${svg}</div>`)
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
}
|