@kolkrabbi/kol-component 0.79.0 → 0.80.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.79.0",
3
+ "version": "0.80.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",
@@ -21,6 +21,9 @@
21
21
  * @param {ReactNode} headline the heading
22
22
  * @param {'pull'|'display-01'|'display-02'|'display-03'|'display-04'|'heading-01'|'heading-02'|'heading-03'|'heading-04'|'heading-05'} [headlineSize='heading-02']
23
23
  * @param {string} [headlineAs='h2'] element for the headline
24
+ * @param {'auto'|'upper'} [headlineCase='auto'] `upper` = the caps role (`.kol-section-text-caps`) —
25
+ * a title card; the split hero sets it (SectionHeroNoOverrides, 2026-08-26:
26
+ * a ROLE on the molecule, never a hero-side class)
24
27
  * @param {ReactNode} body lede / paragraph(s)
25
28
  * @param {ReactNode} actions row of Buttons / links / Tags
26
29
  * @param {'start'|'center'} [align='start'] text-align + item alignment
@@ -52,6 +55,7 @@ export default function SectionText({
52
55
  headline,
53
56
  headlineSize = 'heading-02',
54
57
  headlineAs: Headline = 'h2',
58
+ headlineCase = 'auto',
55
59
  headlineClass,
56
60
  body,
57
61
  actions,
@@ -76,7 +80,7 @@ export default function SectionText({
76
80
  {/* `kol-section-text-eyebrow` = uppercase by ROLE (kol-theme ≥0.55.0);
77
81
  * `labelClass` is the voice riding beside it */}
78
82
  {label && <span className={cls(`kol-section-text-eyebrow ${labelClass}`, 'label')} style={slotStyle.label}>{label}</span>}
79
- {headline && <Headline className={cls(headlineClass ?? (HEADLINE_ROLE[headlineSize] ?? HEADLINE_ROLE['heading-02']), 'headline')} style={slotStyle.headline}>{headline}</Headline>}
83
+ {headline && <Headline className={cls(`${headlineClass ?? (HEADLINE_ROLE[headlineSize] ?? HEADLINE_ROLE['heading-02'])}${headlineCase === 'upper' ? ' kol-section-text-caps' : ''}`, 'headline')} style={slotStyle.headline}>{headline}</Headline>}
80
84
  {body && (typeof body === 'string' ? <p className={cls(bodyClass, 'body')} style={slotStyle.body}>{body}</p> : <div className={cls(bodyClass, 'body')} style={slotStyle.body}>{body}</div>)}
81
85
  {children}
82
86
  {actions && <div className={cls(actionsClass, 'actions')} style={slotStyle.actions}>{actions}</div>}
@@ -4,7 +4,7 @@ import Image from '../atoms/Image.jsx'
4
4
  import OverlayGlassPanel from '../utilities/OverlayGlassPanel.jsx'
5
5
  import AssetPlaceholder from '../utilities/AssetPlaceholder.jsx'
6
6
  import ContentMedia from '../molecules/ContentMedia.jsx'
7
- import SectionText, { HEADLINE_ROLE } from '../molecules/SectionText.jsx'
7
+ import SectionText from '../molecules/SectionText.jsx'
8
8
  import FeaturedCarousel from './FeaturedCarousel.jsx'
9
9
 
10
10
  /* Height presets (SectionHeroRound2, user ruling 2026-08-26): three tiers in
@@ -22,6 +22,20 @@ const HEIGHTS = {
22
22
  md: 'h-[320px] md:h-[440px]',
23
23
  }
24
24
 
25
+ /* The SPLIT variant's presets as LITERALS (SectionHeroSplitHeight, kol-website
26
+ * 2026-08-26). 0.76.0 rewrote HEIGHTS to min-h-* at runtime — a string no
27
+ * source file carries, so Tailwind's scanner never emitted it and every
28
+ * split preset was dead: the Studio hero rendered its text column's 175px.
29
+ * A class Tailwind cannot see is not a class. */
30
+ const SPLIT_HEIGHTS = {
31
+ full: 'min-h-dvh',
32
+ 80: 'min-h-[70svh] md:min-h-[80vh]',
33
+ 60: 'min-h-[50svh] md:min-h-[60vh]',
34
+ screen: 'min-h-dvh',
35
+ lg: 'min-h-dvh',
36
+ md: 'min-h-[50svh] md:min-h-[60vh]',
37
+ }
38
+
25
39
  /**
26
40
  * Background layer: a media descriptor becomes a cover-fit Image / HlsVideo /
27
41
  * inert <video> (`.kol-full-bleed-hero-media` pins it absolute + object-cover
@@ -110,7 +124,8 @@ function MediaLayer({ media }) {
110
124
  * @param {string} height 'full' | '80' | '60' (viewport tiers) · 'lg' | 'md' | 'screen' (aliases) · or a height class string
111
125
  * @param {ReactNode} label · headline · body · actions the composed text (SectionText)
112
126
  * @param {object} slotClass · slotStyle per-slot class / style, forwarded to SectionText (a consumer's reveal seam)
113
- * @param {string} [headlineSize='display-04']
127
+ * @param {string} headlineSize role; defaults per variant — media 'display-04', split 'heading-02'
128
+ * @param {string} gap SectionText's inner rhythm, forwarded as given (no hero default)
114
129
  * @param {string} [panelMaxWidth='max-w-[440px]'] the glass panel's cap on the composed route
115
130
  * @param {object} panelProps everything else on the glass panel (user ask 2026-08-26):
116
131
  * `padding` ('px-6 py-8') · `maxWidth` · `surfaceOpacity`
@@ -146,7 +161,11 @@ export default function SectionHero({
146
161
  height = 'lg',
147
162
  label,
148
163
  headline,
149
- headlineSize = 'display-04',
164
+ /* per-variant DEFAULT only — the hero has no say in the text's voice
165
+ * (SectionHeroNoOverrides, 2026-08-26): media 'display-04', split
166
+ * 'heading-02'; the consumer's value wins */
167
+ headlineSize,
168
+ gap,
150
169
  body,
151
170
  actions,
152
171
  panelMaxWidth = 'max-w-[440px]',
@@ -167,20 +186,22 @@ export default function SectionHero({
167
186
  ? <ContentMedia ratio={null} radius={false} fit="cover" className="h-full">{media}</ContentMedia>
168
187
  : <MediaLayer media={media} />
169
188
  return (
170
- <section className={`kol-section-hero-split grid grid-cols-1 md:grid-cols-2 ${(HEIGHTS[height === 'lg' ? 'full' : height] || height).replace(/\bh-/g, 'min-h-')} ${bleed} ${className}`.replace(/\s+/g, ' ').trim()}>
171
- <div className={`relative min-h-[50vh] overflow-hidden md:min-h-0 ${mediaFirst ? '' : 'md:order-2'}`.trim()}>{mediaNode}</div>
189
+ <section className={`kol-section-hero-split grid grid-cols-1 md:grid-cols-2 ${SPLIT_HEIGHTS[height] || height} ${bleed} ${className}`.replace(/\s+/g, ' ').trim()}>
190
+ {/* the half sizes itself off the grid row (`h-full min-h-0`) so an
191
+ * absolute media node — the descriptor's cover-fit img — fills it */}
192
+ <div className={`relative h-full min-h-[50vh] overflow-hidden md:min-h-0 ${mediaFirst ? '' : 'md:order-2'}`.trim()}>{mediaNode}</div>
172
193
  <div className={`flex items-center justify-center p-10 ${mediaFirst ? '' : 'md:order-1'}`.trim()}>
194
+ {/* BARE — the molecule's own voice; `headlineCase="upper"` is the
195
+ * split hero's one trait, a role on SectionText (SectionHeroNoOverrides) */}
173
196
  <SectionText
174
197
  label={label}
175
198
  headline={headline}
176
- headlineSize={headlineSize === 'display-04' ? 'heading-02' : headlineSize}
177
- headlineClass={`${HEADLINE_ROLE[headlineSize === 'display-04' ? 'heading-02' : headlineSize] ?? HEADLINE_ROLE['heading-02']} kol-section-text-caps`}
199
+ headlineSize={headlineSize ?? 'heading-02'}
200
+ headlineCase="upper"
178
201
  body={body}
179
202
  actions={actions}
180
203
  align="center"
181
- gap="gap-6"
182
- bodyClass="kol-sans-body-02 text-body"
183
- actionsClass="flex flex-wrap gap-4 justify-center"
204
+ gap={gap}
184
205
  className="max-w-[var(--kol-content-column)]"
185
206
  slotClass={slotClass}
186
207
  slotStyle={slotStyle}
@@ -231,18 +252,20 @@ export default function SectionHero({
231
252
  }
232
253
 
233
254
  const text = composed ? (
255
+ /* BARE (SectionHeroNoOverrides, user 2026-08-26: "you think the purpose
256
+ * of unifying components is to make OVERWRITES?") — the hero passes the
257
+ * slots and nothing about their voice. The old glass hero's gap-6 /
258
+ * body-02 / caps were pixel parity with a retired component, and parity
259
+ * with it is not the bar; one voice across the set is. `gap` is the
260
+ * consumer's if they want it. */
234
261
  <SectionText
235
262
  label={label}
236
263
  headline={headline}
237
- headlineSize={headlineSize}
264
+ headlineSize={headlineSize ?? 'display-04'}
238
265
  body={body}
239
266
  actions={actions}
240
267
  align={align === 'center' ? 'center' : 'start'}
241
- /* gap-6 — the rhythm every hand-built OverlayGlassPanel hero ran on,
242
- * so the composed route lands on the same pixels */
243
- gap="gap-6"
244
- bodyClass="kol-sans-body-02 text-body"
245
- actionsClass={`flex flex-wrap gap-4${align === 'center' ? ' justify-center' : ''}`}
268
+ gap={gap}
246
269
  className={media == null ? `${panelMaxWidth} mx-auto` : ''}
247
270
  slotClass={slotClass}
248
271
  slotStyle={slotStyle}