@kolkrabbi/kol-component 0.84.0 → 0.86.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-component",
3
- "version": "0.84.0",
3
+ "version": "0.86.0",
4
4
  "description": "KOL design-system components — atoms through organisms, emitting canonical kol-* classes. Pairs with @kolkrabbi/kol-theme for styling.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -120,9 +120,9 @@ function MediaLayer({ media }) {
120
120
  * @param {ReactNode|{src, kind, poster, srcSet, alt}|Array} media background (media) / the half (split) / an ARRAY of slides → carousel
121
121
  * @param {number} overlayOpacity 0–100 surface-primary scrim over the media (default 0)
122
122
  * @param {boolean} veil bottom-heavy gradient over the media
123
- * @param {'center'|'end'} justify content vertically centred, or pinned to the foot
123
+ * @param {'center'|'end'} justify content vertically centred, or pinned to the foot (with `foot`, the pb ramp plus `overlap` — the text clears the card)
124
124
  * @param {ReactNode} foot a node rendered across the hero's bottom edge
125
- * @param {number} overlap how far `foot` rises into the hero, px (default 250)
125
+ * @param {number} overlap how far `foot` rises into the hero, px (default 250); published on the section as `--kol-section-foot-overlap`
126
126
  * @param {boolean} autoPlay · {number} autoPlayInterval · {'stack'|'header'} navPosition carousel only
127
127
  * @param {Function} renderTitle · {string} ctaLabel · {Function} onNavigate · {boolean} showTitle · showDescription · showCta ·
128
128
  * {string} titleClassName · descriptionClassName · {Object} options carousel only — FeaturedCarousel's
@@ -311,10 +311,23 @@ export default function SectionHero({
311
311
  ))
312
312
  : children)
313
313
 
314
- const justifyCls = justify === 'end' ? 'items-end pb-32 sm:pb-40 lg:pb-48 xl:pb-56' : 'items-center'
314
+ /* `justify="end"` + `foot` (SectionHeroFootClearance, kol-website
315
+ * 2026-08-27 — user, dev vs live: "why can't you make it the same?"): the
316
+ * foot rises `overlap` px into the hero, so the content's bottom inset is
317
+ * the pb ramp PLUS the overlap — the text clears the card at any overlap.
318
+ * The ramp is a custom property so the sum is one literal class; `overlap`
319
+ * is published on the section as --kol-section-foot-overlap (0 without a
320
+ * foot). */
321
+ /* the bottom inset is never the `p-*` shorthand: `md:p-10` sits in a later
322
+ * media query than a bare `pb-*` and would win it back at ≥768 (measured —
323
+ * the old `pb-32 sm:pb-40` ramp lost to it between 768 and 1023 too). */
324
+ const justifyCls = justify === 'end'
325
+ ? 'items-end [--kol-section-pb:8rem] sm:[--kol-section-pb:10rem] lg:[--kol-section-pb:12rem] xl:[--kol-section-pb:14rem] pb-[calc(var(--kol-section-pb)+var(--kol-section-foot-overlap,0px))]'
326
+ : 'items-center pb-6 md:pb-10'
327
+ const footVar = foot ? { '--kol-section-foot-overlap': `${overlap}px` } : undefined
315
328
 
316
329
  return withFoot(
317
- <section ref={themeRef} data-theme={themeStamp} className={`kol-full-bleed-hero relative isolate w-full overflow-hidden ${themed} ${heightCls} ${fullBleed ? 'w-screen ml-[calc(50%-50vw)]' : ''} ${className}`.replace(/\s+/g, ' ').trim()}>
330
+ <section ref={themeRef} data-theme={themeStamp} className={`kol-full-bleed-hero relative isolate w-full overflow-hidden ${themed} ${heightCls} ${fullBleed ? 'w-screen ml-[calc(50%-50vw)]' : ''} ${className}`.replace(/\s+/g, ' ').trim()} style={footVar}>
318
331
  <MediaLayer media={media} />
319
332
  {overlayOpacity > 0 && (
320
333
  <div
@@ -324,7 +337,7 @@ export default function SectionHero({
324
337
  />
325
338
  )}
326
339
  {veil && <div aria-hidden="true" className="kol-section-hero-veil absolute inset-0" />}
327
- <div className={`relative z-10 flex h-full w-full ${justifyCls} ${alignCls} p-6 md:p-10`}>
340
+ <div className={`relative z-10 flex h-full w-full ${justifyCls} ${alignCls} px-6 pt-6 md:px-10 md:pt-10`}>
328
341
  {content}
329
342
  </div>
330
343
  </section>,
@@ -31,7 +31,11 @@ import { minHeightClass } from './sectionHeights.js'
31
31
  * @param {{num: ReactNode, label: string}[]} meta stats strip (mutually exclusive with `actions`)
32
32
  * @param {ReactNode} actions button row
33
33
  * @param {ReactNode} media image / video / interactive node for the visual column
34
- * @param {string} [ratio='4/5'] aspect ratio of the media frame
34
+ * @param {string} [ratio='4/5'] aspect ratio of the media frame. THE FRAME IS BOUNDED BY THE
35
+ * RUNG (SectionSplitMediaBounded, 2026-08-27): its height is the section's `height` rung minus
36
+ * the vertical padding, its width follows the ratio (capped at the column). Before, the frame
37
+ * was as wide as its column and set the section's height itself, so `40` · `60` · `80`
38
+ * rendered identically — a ladder that only floors is decorative for a card with media.
35
39
  * @param {boolean} [mediaHover=false] zoom the media on frame hover (the CardFeatureItem numbers)
36
40
  * @param {boolean} [mediaClip=true] the frame clips its media (overflow hidden + radius). `false`
37
41
  * for a node that transforms in 3D (a tilt card) — the frame keeps its ratio and radius,
@@ -86,7 +90,7 @@ export default function SectionSplit({
86
90
  <section
87
91
  ref={themeRef}
88
92
  data-theme={themeStamp}
89
- className={`kol-section-split flex flex-col justify-center px-5 py-16 md:px-8 md:py-24 lg:px-14 lg:py-32 ${minHeightClass(height)} ${bleed} ${className}`.replace(/\s+/g, ' ').trim()}
93
+ className={`kol-section-split flex flex-col justify-center px-5 py-16 md:px-8 md:py-24 lg:px-14 lg:py-32 [--kol-section-py:4rem] md:[--kol-section-py:6rem] lg:[--kol-section-py:8rem] ${minHeightClass(height)} ${bleed} ${className}`.replace(/\s+/g, ' ').trim()}
90
94
  style={sectionStyle}
91
95
  >
92
96
  {/* ONE cap for the whole section family (user ruling 2026-08-26): the shell's
@@ -120,9 +124,12 @@ export default function SectionSplit({
120
124
  )}
121
125
  </SectionText>
122
126
  {media && (
127
+ /* height = the rung minus the padding; width follows the ratio and
128
+ * caps at the column — the media takes its size from the section,
129
+ * never gives it */
123
130
  <div
124
- className={`kol-section-split-visual relative rounded-[var(--kol-radius-sm)] ${mediaClip ? 'overflow-hidden' : ''} ${mediaHover ? 'is-hoverable' : ''} ${mediaFirst ? 'order-1' : ''} ${centred ? 'w-full max-w-[640px]' : ''}`.replace(/\s+/g, ' ').trim()}
125
- style={{ aspectRatio: ratio }}
131
+ className={`kol-section-split-visual relative w-auto max-w-full justify-self-center rounded-[var(--kol-radius-sm)] ${mediaClip ? 'overflow-hidden' : ''} ${mediaHover ? 'is-hoverable' : ''} ${mediaFirst ? 'order-1' : ''} ${centred ? 'max-w-[640px]' : ''}`.replace(/\s+/g, ' ').trim()}
132
+ style={{ aspectRatio: ratio, height: 'calc(var(--kol-section-h, 60vh) - 2 * var(--kol-section-py, 4rem))' }}
126
133
  >
127
134
  {media}
128
135
  {caption && <div className="kol-section-split-visual-veil" aria-hidden="true" />}
@@ -4,12 +4,15 @@
4
4
  * Same three names as SectionHero's presets; literal strings — a class built
5
5
  * at runtime is not a class (the SPLIT_HEIGHTS lesson). Anything else passes
6
6
  * through as a class string. */
7
+ /* Each rung also publishes itself as `--kol-section-h`, so a child that must
8
+ * be BOUNDED by the rung — the split's media frame (SectionSplitMediaBounded,
9
+ * 2026-08-27) — can read it. Literal strings, every one. */
7
10
  export const MIN_HEIGHTS = {
8
- full: 'min-h-dvh',
9
- 80: 'min-h-[70svh] md:min-h-[80vh]',
10
- 60: 'min-h-[50svh] md:min-h-[60vh]',
11
+ full: 'min-h-dvh [--kol-section-h:100dvh]',
12
+ 80: 'min-h-[70svh] md:min-h-[80vh] [--kol-section-h:70svh] md:[--kol-section-h:80vh]',
13
+ 60: 'min-h-[50svh] md:min-h-[60vh] [--kol-section-h:50svh] md:[--kol-section-h:60vh]',
11
14
  /* 40 (SectionHeightForty, 2026-08-27): the rung below 60 for a page-foot
12
15
  * CTA or a cards band that wants less air; default stays 60 everywhere */
13
- 40: 'min-h-[35svh] md:min-h-[40vh]',
16
+ 40: 'min-h-[35svh] md:min-h-[40vh] [--kol-section-h:35svh] md:[--kol-section-h:40vh]',
14
17
  }
15
18
  export const minHeightClass = (height) => MIN_HEIGHTS[height] || height