@kolkrabbi/kol-component 0.73.0 → 0.74.1

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.73.0",
3
+ "version": "0.74.1",
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",
@@ -227,7 +227,11 @@ export default function ContentText({
227
227
  )
228
228
  }
229
229
 
230
- const nodes = order.map(render)
230
+ /* NOT `order.map(render)`: map passes (entry, index, ARRAY) and the array
231
+ * landed in `trailing`, so every top-level stack right-aligned — the article
232
+ * card's title and body sat flush right under left-aligned tags for eleven
233
+ * days (found on the comparison page, 2026-08-26). */
234
+ const nodes = order.map((entry, i) => render(entry, i))
231
235
 
232
236
  return (
233
237
  <div
@@ -2,7 +2,9 @@ import { isValidElement } from 'react'
2
2
  import HlsVideo from '../atoms/HlsVideo.jsx'
3
3
  import Image from '../atoms/Image.jsx'
4
4
  import OverlayGlassPanel from '../utilities/OverlayGlassPanel.jsx'
5
- import SectionText from '../molecules/SectionText.jsx'
5
+ import AssetPlaceholder from '../utilities/AssetPlaceholder.jsx'
6
+ import ContentMedia from '../molecules/ContentMedia.jsx'
7
+ import SectionText, { HEADLINE_ROLE } from '../molecules/SectionText.jsx'
6
8
 
7
9
  /** Height presets; anything else is passed through as a class string. */
8
10
  const HEIGHTS = {
@@ -66,7 +68,16 @@ function MediaLayer({ media }) {
66
68
  * the hero renders your node exactly as before — the escape hatch, and what
67
69
  * every existing call site does; nothing there moves.
68
70
  *
69
- * @param {ReactNode|{src, kind, poster, srcSet, alt}} media background
71
+ * TWO VARIANTS (user ask 2026-08-26). `media` (default): the cover-fit hero
72
+ * above. `split`: the whole viewport in two halves — media fills one edge to
73
+ * edge (no media → AssetPlaceholder), the text sits centred in the other, no
74
+ * glass panel, headline UPPERCASE by role (`.kol-section-text-caps`). It is
75
+ * SectionSplit blown up to 100vh/100vw with no padding, no ratio, no cap.
76
+ * `align` picks the media side there ('left' default · 'right'); below `md`
77
+ * the halves stack, media first.
78
+ *
79
+ * @param {'media'|'split'} variant
80
+ * @param {ReactNode|{src, kind, poster, srcSet, alt}} media background (media) / the half (split)
70
81
  * @param {number} overlayOpacity 0–100 surface-primary scrim over the media (default 0)
71
82
  * @param {string} height 'screen' | 'lg' | 'md' preset, or a height class string
72
83
  * @param {ReactNode} label · headline · body · actions the composed text (SectionText)
@@ -74,10 +85,14 @@ function MediaLayer({ media }) {
74
85
  * @param {string} [panelMaxWidth='max-w-[440px]'] the glass panel's cap on the composed route
75
86
  * @param {ReactNode} panel a caller-authored content node; wins over the text props and children
76
87
  * @param {ReactNode} children fallback content slot
77
- * @param {string} align 'center' | 'start' | 'end' — horizontal placement of the content
88
+ * @param {string} align media: 'center' | 'start' | 'end' — placement of the content ·
89
+ * split: 'left' | 'right' — the media side
90
+ * @param {boolean} fullBleed span the full viewport width (100vw breakout) — split
78
91
  * @param {string} className extra classes on the section
79
92
  */
80
93
  export default function SectionHero({
94
+ variant = 'media',
95
+ fullBleed = false,
81
96
  media,
82
97
  overlayOpacity = 0,
83
98
  height = 'lg',
@@ -92,6 +107,37 @@ export default function SectionHero({
92
107
  align = 'center',
93
108
  className = '',
94
109
  }) {
110
+ if (variant === 'split') {
111
+ const mediaFirst = align !== 'right'
112
+ const bleed = fullBleed ? 'w-screen ml-[calc(50%-50vw)]' : 'w-full'
113
+ const mediaNode = media == null
114
+ ? <AssetPlaceholder radius={false} className="h-full w-full" />
115
+ : isValidElement(media) || typeof media !== 'object'
116
+ ? <ContentMedia ratio={null} radius={false} fit="cover" className="h-full">{media}</ContentMedia>
117
+ : <MediaLayer media={media} />
118
+ return (
119
+ <section className={`kol-section-hero-split grid min-h-screen grid-cols-1 md:grid-cols-2 ${bleed} ${className}`.replace(/\s+/g, ' ').trim()}>
120
+ <div className={`relative min-h-[50vh] overflow-hidden md:min-h-0 ${mediaFirst ? '' : 'md:order-2'}`.trim()}>{mediaNode}</div>
121
+ <div className={`flex items-center justify-center p-10 ${mediaFirst ? '' : 'md:order-1'}`.trim()}>
122
+ <SectionText
123
+ label={label}
124
+ headline={headline}
125
+ headlineSize={headlineSize === 'display-04' ? 'heading-02' : headlineSize}
126
+ headlineClass={`${HEADLINE_ROLE[headlineSize === 'display-04' ? 'heading-02' : headlineSize] ?? HEADLINE_ROLE['heading-02']} kol-section-text-caps`}
127
+ body={body}
128
+ actions={actions}
129
+ align="center"
130
+ gap="gap-6"
131
+ labelClass="kol-helper-12 text-meta"
132
+ bodyClass="kol-sans-body-02 text-body"
133
+ actionsClass="flex flex-wrap gap-4 justify-center"
134
+ className="max-w-[var(--kol-content-column)]"
135
+ />
136
+ </div>
137
+ </section>
138
+ )
139
+ }
140
+
95
141
  const heightCls = HEIGHTS[height] || height
96
142
  const alignCls =
97
143
  align === 'start' ? 'justify-start'