@kolkrabbi/kol-component 0.171.0 → 0.173.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.171.0",
3
+ "version": "0.173.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",
@@ -44,6 +44,21 @@ import { minHeightClass } from './sectionHeights.js'
44
44
  * the node owns its own clipping (SectionSplitMediaClip, kol-website 2026-08-26: TiltCard's
45
45
  * lifted corners were being cut flat). `mediaHover` and a self-animating node are
46
46
  * mutually exclusive in practice — pick one.
47
+ * @param {boolean} [fill=false] THE HALF-FILLING FORM (section-split-fill-variant,
48
+ * kol-client-olina 2026-09-03). The bounded frame is the right rule for a card with media
49
+ * and the wrong one for a half: `fill` releases it — the media column loses the ratio, the
50
+ * rung-minus-padding height, the radius and the section padding, and covers its half edge to
51
+ * edge, with the text centred in the other. `align` still picks the side; below 901px the
52
+ * media stacks on top at `min-h-[50vh]`. This is the layout `SectionHero variant="split"`
53
+ * draws, without the hero's overlay / glass panel / carousel machinery or its name — the
54
+ * user's reason for not putting a hero under an About section.
55
+ * @param {'center'|'start'} [textAlign] how the TYPE rags inside the text block —
56
+ * `align` places the block, this aligns what is in it (section-split-fill-text-align,
57
+ * kol-client-olina 2026-09-03; user: *"same placement but just aligned to left … can the
58
+ * text align left and everything else kinda stays as is?"*). Works in BOTH forms rather
59
+ * than only in `fill`, because a prop that silently does nothing in the default form is a
60
+ * seam wired to nothing. Unset, each form keeps exactly what it did: `fill` centres,
61
+ * bounded follows `align` (centred when `align="center"`, else start).
47
62
  * @param {'right'|'left'|'center'} [align='right'] media side, or centred single column
48
63
  * @param {'full'|'80'|'60'|string} [height='60'] min-height on the family's ladder — full = 100dvh,
49
64
  * 80 = 70svh / 80vh, 60 = 50svh / 60vh (default), 40 = 35svh / 40vh; the columns stay vertically centred inside it
@@ -72,6 +87,8 @@ export default function SectionSplit({
72
87
  mediaHover = false,
73
88
  mediaClip = true,
74
89
  align = 'right',
90
+ textAlign,
91
+ fill = false,
75
92
  height = '60',
76
93
  caption,
77
94
  bgImage,
@@ -91,9 +108,67 @@ export default function SectionSplit({
91
108
  const bleed = fullBleed ? FULL_BLEED : ''
92
109
  const centred = align === 'center'
93
110
  const mediaFirst = align === 'left'
111
+ /* unset = each form's own prior behaviour, so nothing existing moves */
112
+ const fillText = textAlign ?? 'center'
113
+ const boundedText = textAlign ?? (centred ? 'center' : 'start')
114
+ const justify = (a) => (a === 'center' ? 'justify-center' : 'justify-start')
94
115
  const grid = centred
95
116
  ? 'grid grid-cols-1 justify-items-center gap-[clamp(48px,6vw,96px)]'
96
117
  : 'grid grid-cols-1 min-[901px]:grid-cols-2 items-center gap-[clamp(48px,6vw,96px)]'
118
+
119
+ /* THE FILL FORM — two halves, no frame. Deliberately its own return rather
120
+ * than conditionals threaded through the bounded one: every bounded rule
121
+ * (the container cap, the section padding, the ratio box, the rung-minus-
122
+ * padding height) is a rule this form does not have, so sharing the JSX would
123
+ * mean negating each of them at its own site. `.kol-section-split-visual`
124
+ * stays on the media half because its `img { object-fit: cover }` rule is
125
+ * exactly what a filling half wants — the class earns its keep here without a
126
+ * line of new CSS. `centred` has no meaning with two halves and is ignored. */
127
+ if (fill && media) {
128
+ return (
129
+ <section
130
+ ref={themeRef}
131
+ data-theme={themeStamp}
132
+ className={`kol-section-split kol-section-split--fill grid grid-cols-1 min-[901px]:grid-cols-2 ${minHeightClass(height)} ${surfaceClass(background, 'none')} ${bleed} ${className}`.replace(/\s+/g, ' ').trim()}
133
+ style={sectionStyle}
134
+ >
135
+ <div
136
+ className={`kol-section-split-visual relative h-full min-h-[50vh] overflow-hidden min-[901px]:min-h-0 ${mediaHover ? 'is-hoverable' : ''} ${mediaFirst ? '' : 'min-[901px]:order-2'} ${columnClassName}`.replace(/\s+/g, ' ').trim()}
137
+ >
138
+ {media}
139
+ {caption && <div className="kol-section-split-visual-veil" aria-hidden="true" />}
140
+ {caption && <span className="kol-section-split-visual-caption">{caption}</span>}
141
+ </div>
142
+ <div className={`flex items-center justify-center px-5 py-16 md:px-8 md:py-24 lg:px-14 ${mediaFirst ? '' : 'min-[901px]:order-1'} ${innerClassName}`.replace(/\s+/g, ' ').trim()}>
143
+ <SectionText
144
+ eyebrow={eb}
145
+ headline={headline}
146
+ headlineSize={headlineSize}
147
+ headlineAs={headlineAs}
148
+ body={body}
149
+ actions={actions}
150
+ actionsClass={`flex flex-wrap gap-4 pt-2 ${justify(fillText)}`}
151
+ align={fillText}
152
+ slotClass={slotClass}
153
+ slotStyle={slotStyle}
154
+ className="max-w-[var(--kol-content-column)]"
155
+ >
156
+ {meta && meta.length > 0 && (
157
+ <div className={`kol-section-split-meta flex flex-wrap gap-y-7 gap-x-12 pt-4 ${justify(fillText)}`}>
158
+ {meta.map((m) => (
159
+ <div key={m.label} className="flex flex-col gap-0.5">
160
+ <span className="kol-section-split-meta-num">{m.num}</span>
161
+ <span className="kol-section-split-meta-label">{m.label}</span>
162
+ </div>
163
+ ))}
164
+ </div>
165
+ )}
166
+ </SectionText>
167
+ </div>
168
+ </section>
169
+ )
170
+ }
171
+
97
172
  return (
98
173
  <section
99
174
  ref={themeRef}
@@ -114,14 +189,14 @@ export default function SectionSplit({
114
189
  headlineAs={headlineAs}
115
190
  body={body}
116
191
  actions={actions}
117
- actionsClass={`flex flex-wrap gap-4 pt-2${centred ? ' justify-center' : ''}`}
118
- align={centred ? 'center' : 'start'}
192
+ actionsClass={`flex flex-wrap gap-4 pt-2 ${justify(boundedText)}`}
193
+ align={boundedText}
119
194
  slotClass={slotClass}
120
195
  slotStyle={slotStyle}
121
196
  className={`max-w-[640px] ${mediaFirst ? 'order-2' : ''} ${columnClassName}`}
122
197
  >
123
198
  {meta && meta.length > 0 && (
124
- <div className={`kol-section-split-meta flex flex-wrap gap-y-7 gap-x-12 pt-4${centred ? ' justify-center' : ''}`}>
199
+ <div className={`kol-section-split-meta flex flex-wrap gap-y-7 gap-x-12 pt-4 ${justify(boundedText)}`}>
125
200
  {meta.map((m) => (
126
201
  <div key={m.label} className="flex flex-col gap-0.5">
127
202
  <span className="kol-section-split-meta-num">{m.num}</span>