@kolkrabbi/kol-framework 0.59.0 → 0.61.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 CHANGED
@@ -32,8 +32,15 @@ body {
32
32
  * law, brand-app ruling): text is selectable everywhere — copyability beats
33
33
  * chrome purity. */
34
34
 
35
- /* Selection highlight uses brand-primary so it reads on-brand when it appears. */
35
+ /* SELECTED TEXT IS NEUTRAL BY DEFAULT (ArticleRendererHomeHeroSelection, kol-website 2026-10-28 — user: accents are opt-in,
36
+ * not default; brand's selections were yellow): the page's own ink pair, inverted — a white highlight with dark ink in dark
37
+ * mode, its inverse in light. An app (or any subtree) opts into the accent with `.kol-select-accent`. */
36
38
  ::selection {
39
+ background-color: var(--kol-surface-on-primary);
40
+ color: var(--kol-surface-primary);
41
+ }
42
+ .kol-select-accent::selection,
43
+ .kol-select-accent ::selection {
37
44
  background-color: var(--kol-accent-primary);
38
45
  color: var(--kol-accent-on-primary);
39
46
  }
@@ -211,6 +218,59 @@ body {
211
218
  line-height: 1;
212
219
  transition: transform 150ms ease, opacity 120ms ease, visibility 0s linear 120ms;
213
220
  }
221
+ /* THE SPLIT ROW (user 2026-10-28: "expand anywhere except the title"): a foldernote's row is two targets on one box.
222
+ * The caret button fills the row under the link (the Tooltip wrapper is the positioned box, so it covers the head and
223
+ * not the open list below), its chevron where the floated caret sat; the link above passes the pointer through except
224
+ * on its glyph and title. The row still brightens as one when either half is hovered. */
225
+ .kol-sidenav-hop-caret--row {
226
+ top: 0;
227
+ right: 0;
228
+ bottom: 0;
229
+ left: 0;
230
+ justify-content: flex-end;
231
+ align-items: flex-start;
232
+ /* the chevron's 12 from the top and 16 from the edge ride the element as utilities (`pt-3 pr-4`) — a padding here
233
+ * would lose to the utility layer, which is how the first cut put the chevron 12px high */
234
+ }
235
+ .kol-sidenav-hop-chevron {
236
+ display: inline-flex;
237
+ transition: transform 150ms ease;
238
+ }
239
+ .kol-sidenav-hop--split {
240
+ pointer-events: none;
241
+ }
242
+ .kol-sidenav-hop--split .kol-sidenav-hop-icon,
243
+ .kol-sidenav-hop--split .kol-sidenav-hop-title {
244
+ pointer-events: auto;
245
+ }
246
+ .kol-sidenav-hop-split:has(> .kol-sidenav-hop-caret--row:hover) .kol-sidenav-hop {
247
+ color: var(--kol-fg-96);
248
+ }
249
+ .kol-sidenav-hop-split:has(> .kol-sidenav-hop-caret--row:hover) .kol-sidenav-hop-icon {
250
+ color: var(--kol-surface-on-primary);
251
+ }
252
+ :root[data-sidenav="collapsed"] .kol-sidenav-hop--split { pointer-events: auto; }
253
+ /* THE TREE FADES AT ITS CLIP (user 2026-10-28: "we also are missing a fade when list clips in navrail") — the app rail's
254
+ * `.kol-shell-rail-list--fade` and the workshop rail's `.shell-rail`, the same 3rem ramp; the element's `pb-12` (a utility,
255
+ * which a components-layer padding here could not outrank) lets the last row scroll clear of it, so at the end of the list nothing stays faded. `NavRail variant="site" fade={false}` drops it. */
256
+ .kol-sidenav-scroll--fade {
257
+ -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 3rem), transparent);
258
+ mask-image: linear-gradient(to bottom, #000 calc(100% - 3rem), transparent);
259
+ }
260
+ /* THE SITE HERO'S WASH (SiteHero, 2026-10-28) — kol-website's `.landing-hero-overlay` and `.stack-hero-overlay`, one rule:
261
+ * the inverse ink mixed over the image, 32 % light · 80 % dark, the theme law's selectors (explicit > system > light).
262
+ * SiteHero's `overlay` prop sets the two variables. */
263
+ .kol-site-hero-overlay {
264
+ background-color: color-mix(in srgb, var(--kol-surface-on-inverse, currentColor) var(--kol-site-hero-mix, 32%), transparent);
265
+ }
266
+ :is([data-theme="dark"], .dark) .kol-site-hero-overlay {
267
+ background-color: color-mix(in srgb, var(--kol-surface-on-inverse, currentColor) var(--kol-site-hero-mix-dark, 80%), transparent);
268
+ }
269
+ @media (prefers-color-scheme: dark) {
270
+ :root:not([data-theme="light"]) .kol-site-hero-overlay {
271
+ background-color: color-mix(in srgb, var(--kol-surface-on-inverse, currentColor) var(--kol-site-hero-mix-dark, 80%), transparent);
272
+ }
273
+ }
214
274
  .kol-sidenav-list {
215
275
  display: flex;
216
276
  flex-direction: column;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-framework",
3
- "version": "0.59.0",
3
+ "version": "0.61.0",
4
4
  "description": "Site shell and page layout",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,7 +14,7 @@
14
14
  "./kol-brand-color.css": "./kol-brand-color.css"
15
15
  },
16
16
  "peerDependencies": {
17
- "@kolkrabbi/kol-component": ">=0.298.0",
17
+ "@kolkrabbi/kol-component": ">=0.308.0",
18
18
  "@kolkrabbi/kol-icons": ">=0.18.0",
19
19
  "@kolkrabbi/kol-markdown": ">=0.2.0",
20
20
  "@kolkrabbi/kol-theme": ">=0.186.0",
@@ -23,9 +23,9 @@
23
23
  "react-router-dom": "^6.0.0 || ^7.0.0"
24
24
  },
25
25
  "devDependencies": {
26
- "@kolkrabbi/kol-markdown": "0.2.0",
27
- "@kolkrabbi/kol-component": "^0.307.0",
28
- "@kolkrabbi/kol-icons": "^0.49.0"
26
+ "@kolkrabbi/kol-component": "^0.310.0",
27
+ "@kolkrabbi/kol-icons": "^0.52.0",
28
+ "@kolkrabbi/kol-markdown": "0.2.0"
29
29
  },
30
30
  "files": [
31
31
  "src",
@@ -0,0 +1,64 @@
1
+ import { VectorLoader } from '@kolkrabbi/kol-component'
2
+
3
+ /**
4
+ * SiteHero — A site's landing: a full-screen image, a wash, the mark, the name. kol-website apps/brand's `Landing`, class
5
+ * for class (ArticleRendererHomeHeroSelection, kol-website 2026-10-28 — three hand-built copies: brand's Landing, record's
6
+ * Home, site-hub's Home; web's /stack hero wears the same wash). Every part is optional, so the next site needs no copy.
7
+ *
8
+ * Not `PageHero`: that is a page's opening band (label · title · lede beside a mark). This is the home page itself.
9
+ *
10
+ * @param {Object} image `{ src, srcSet?, sizes?, position?, alt? }` — the ground, `object-cover`, `sizes` 100vw by default
11
+ * @param {ReactNode} media any node in place of `image` (a video, a canvas), filling the ground
12
+ * @param {ReactNode|string} mark a node, or an SVG url inlined in currentColor (`VectorLoader`); the hero sizes it, the
13
+ * landing's clamp(140px, 16vw, 220px)
14
+ * @param {string} markTitle the mark's accessible name when it is a url
15
+ * @param {ReactNode} title the h1, `kol-prose-display uppercase`
16
+ * @param {ReactNode} tagline `kol-prose-tagline`
17
+ * @param {ReactNode} actions buttons under the tagline, wrapped and centred
18
+ * @param {number|{light,dark}|false} overlay the wash: the inverse ink mixed over the image, 32 % light · 80 % dark by
19
+ * default (the sites' line); a number sets both, `false` drops it
20
+ * @param {'center'|'start'} align the text column — centred (default) or from the left
21
+ * @param {string} minHeight default `100dvh`
22
+ */
23
+ export default function SiteHero({ image, media, mark, markTitle, title, tagline, actions, overlay, align = 'center', minHeight = '100dvh', className = '' }) {
24
+ const start = align === 'start'
25
+ const wash = overlay === false ? null
26
+ : typeof overlay === 'number' ? { '--kol-site-hero-mix': `${overlay}%`, '--kol-site-hero-mix-dark': `${overlay}%` }
27
+ : overlay && typeof overlay === 'object' ? { ...(overlay.light != null && { '--kol-site-hero-mix': `${overlay.light}%` }), ...(overlay.dark != null && { '--kol-site-hero-mix-dark': `${overlay.dark}%` }) }
28
+ : {}
29
+ return (
30
+ <section className={`relative flex items-center ${start ? 'justify-start' : 'justify-center'} overflow-hidden ${className}`.trim()} style={{ minHeight }}>
31
+ {(image || media) && (
32
+ <div className="absolute inset-0 w-full h-full">
33
+ {media ?? (
34
+ <img
35
+ src={image.src}
36
+ srcSet={image.srcSet}
37
+ sizes={image.srcSet ? (image.sizes ?? '100vw') : undefined}
38
+ alt={image.alt ?? ''}
39
+ className="w-full h-full object-cover"
40
+ style={{ objectPosition: image.position ?? 'center' }}
41
+ />
42
+ )}
43
+ </div>
44
+ )}
45
+ {wash && <div className="kol-site-hero-overlay absolute inset-0 w-full h-full pointer-events-none" style={wash} aria-hidden="true" />}
46
+
47
+ <div className={`relative z-[1] flex flex-col gap-10 px-8 ${start ? 'items-start text-left' : 'items-center text-center'}`}>
48
+ {mark && (
49
+ <div className="w-[clamp(140px,16vw,220px)] text-emphasis">
50
+ {typeof mark === 'string'
51
+ ? <VectorLoader src={mark} size="100%" className="block" role={markTitle ? 'img' : undefined} aria-label={markTitle} />
52
+ : mark}
53
+ </div>
54
+ )}
55
+
56
+ {title && <h1 className="kol-prose-display uppercase">{title}</h1>}
57
+
58
+ {tagline && <p className="kol-prose-tagline">{tagline}</p>}
59
+
60
+ {actions && <div className={`flex flex-wrap items-center gap-3 mt-4 ${start ? 'justify-start' : 'justify-center'}`}>{actions}</div>}
61
+ </div>
62
+ </section>
63
+ )
64
+ }
package/src/index.js CHANGED
@@ -26,6 +26,8 @@ export { default as PageSection } from './PageSection.jsx'
26
26
  export { default as PortalFooter } from './PortalFooter.jsx'
27
27
  export { default as ScrollToTop } from './ScrollToTop.jsx'
28
28
  export { default as PageHero, default as BrandHero, default as SubPageHero } from './PageHero.jsx'
29
+ /* a site's landing — the full-screen image, wash, mark and name (ArticleRendererHomeHeroSelection, 2026-10-28) */
30
+ export { default as SiteHero } from './SiteHero.jsx'
29
31
  export { default as CategoryPage } from './CategoryPage.jsx'
30
32
  /* the document page (plan-2026-10-28-document W5.2): a markdown file as `.kol-sheet`, the hover card for its links,
31
33
  * a folder of files as page rows */