@kolkrabbi/kol-component 0.78.0 → 0.79.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.78.0",
3
+ "version": "0.79.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,8 @@ export default function SectionCardItem({
44
44
  onNavigate,
45
45
  imageAspectRatio = 'auto',
46
46
  imagePosition = 'center',
47
+ className = '',
48
+ style,
47
49
  }) {
48
50
  const isSvgUrl = typeof visual === 'string' && visual.endsWith('.svg')
49
51
 
@@ -104,7 +106,7 @@ export default function SectionCardItem({
104
106
  </>
105
107
  )
106
108
 
107
- const baseClasses = `kol-card-feature w-full flex-1 h-[304px] md:h-72 p-4 md:p-6 gap-4 ${backgroundColor} rounded border border-fg-08 flex flex-col justify-between items-start overflow-hidden`
109
+ const baseClasses = `kol-card-feature w-full flex-1 h-[304px] md:h-72 p-4 md:p-6 gap-4 ${backgroundColor} rounded border border-fg-08 flex flex-col justify-between items-start overflow-hidden ${className}`.trim()
108
110
 
109
111
  if (href) {
110
112
  const isExternal = href.startsWith('http') || href.startsWith('mailto')
@@ -114,6 +116,7 @@ export default function SectionCardItem({
114
116
  <a
115
117
  href={href}
116
118
  className={`${baseClasses} hover:border-fg-32 transition-colors duration-300`}
119
+ style={style}
117
120
  target="_blank"
118
121
  rel="noreferrer noopener"
119
122
  >
@@ -127,11 +130,12 @@ export default function SectionCardItem({
127
130
  href={href}
128
131
  onClick={onNavigate}
129
132
  className={`${baseClasses} hover:border-fg-24 transition-colors duration-300`}
133
+ style={style}
130
134
  >
131
135
  {content}
132
136
  </a>
133
137
  )
134
138
  }
135
139
 
136
- return <div className={baseClasses}>{content}</div>
140
+ return <div className={baseClasses} style={style}>{content}</div>
137
141
  }
@@ -28,6 +28,10 @@
28
28
  * @param {ReactNode} children extra slots rendered between body and actions
29
29
  * (the split's stats strip)
30
30
  * @param {string} labelClass · bodyClass · actionsClass full class overrides (label default: the hero's `kol-helper-12 text-meta`)
31
+ * @param {object} slotClass extra classes per slot — `{ label, headline, body, actions }`
32
+ * (SectionRevealSeams, 2026-08-26: a consumer's reveal system
33
+ * stamps `reveal` on each part; nothing animates in the DS)
34
+ * @param {object} slotStyle inline style per slot — `{ headline: { '--reveal-delay': '0.1s' } }`
31
35
  * @param {string} className extra classes on the block
32
36
  */
33
37
  export const HEADLINE_ROLE = {
@@ -61,18 +65,21 @@ export default function SectionText({
61
65
  labelClass = 'kol-helper-12 text-meta',
62
66
  bodyClass = 'kol-section-text-body',
63
67
  actionsClass = 'flex flex-wrap gap-4',
68
+ slotClass = {},
69
+ slotStyle = {},
64
70
  className = '',
65
71
  }) {
72
+ const cls = (base, slot) => `${base} ${slotClass[slot] ?? ''}`.trim()
66
73
  const alignCls = align === 'center' ? 'items-center text-center' : 'items-start text-left'
67
74
  return (
68
75
  <div className={`kol-section-text flex flex-col ${gap} ${alignCls} ${className}`.replace(/\s+/g, ' ').trim()}>
69
76
  {/* `kol-section-text-eyebrow` = uppercase by ROLE (kol-theme ≥0.55.0);
70
77
  * `labelClass` is the voice riding beside it */}
71
- {label && <span className={`kol-section-text-eyebrow ${labelClass}`}>{label}</span>}
72
- {headline && <Headline className={headlineClass ?? (HEADLINE_ROLE[headlineSize] ?? HEADLINE_ROLE['heading-02'])}>{headline}</Headline>}
73
- {body && (typeof body === 'string' ? <p className={bodyClass}>{body}</p> : <div className={bodyClass}>{body}</div>)}
78
+ {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>}
80
+ {body && (typeof body === 'string' ? <p className={cls(bodyClass, 'body')} style={slotStyle.body}>{body}</p> : <div className={cls(bodyClass, 'body')} style={slotStyle.body}>{body}</div>)}
74
81
  {children}
75
- {actions && <div className={actionsClass}>{actions}</div>}
82
+ {actions && <div className={cls(actionsClass, 'actions')} style={slotStyle.actions}>{actions}</div>}
76
83
  </div>
77
84
  )
78
85
  }
@@ -17,6 +17,9 @@ import SectionText, { HEADLINE_ROLE } from '../molecules/SectionText.jsx'
17
17
  * @param {ReactNode} label · headline · body the header (heading-03 + mono lede by default)
18
18
  * @param {ReactNode} actions centred action row under the cards
19
19
  * @param {Function} onNavigate (event, feature) => void
20
+ * @param {string} itemClassName extra classes on every card (reveal seam)
21
+ * @param {object|Function} itemStyle inline style per card, or `(index) => style`
22
+ * @param {object} slotClass · slotStyle per-slot class / style on the header text
20
23
  * @param {string} sectionClassName · wrapperClassName · cardsWrapperClassName · actionsClassName · headerClassName · headerTextWidthClass layout seams
21
24
  */
22
25
  export default function SectionCards({
@@ -27,6 +30,13 @@ export default function SectionCards({
27
30
  body,
28
31
  actions,
29
32
  onNavigate,
33
+ /* the reveal seams (SectionRevealSeams, kol-website 2026-08-26): a
34
+ * consumer's entrance system stamps a class + `--reveal-delay` per card;
35
+ * `itemStyle` is an object or `(index) => object`. Nothing animates here. */
36
+ itemClassName = '',
37
+ itemStyle,
38
+ slotClass,
39
+ slotStyle,
30
40
  sectionClassName = '',
31
41
  /* the family's one cap — the shell's --kol-container-max ladder (2026-08-26) */
32
42
  wrapperClassName = 'w-full flex flex-col gap-8 md:gap-10 max-w-[var(--kol-container-max,var(--kol-content-shell,1800px))] mx-auto',
@@ -51,6 +61,8 @@ export default function SectionCards({
51
61
  bodyClass={`kol-mono-14 text-fg-64 ${headerTextWidthClass}`}
52
62
  labelClass="kol-helper-12 text-meta"
53
63
  gap="gap-3"
64
+ slotClass={slotClass}
65
+ slotStyle={slotStyle}
54
66
  className={headerClassName}
55
67
  />
56
68
  )}
@@ -68,6 +80,8 @@ export default function SectionCards({
68
80
  backgroundColor={feature.backgroundColor}
69
81
  imageAspectRatio={feature.imageAspectRatio}
70
82
  onNavigate={onNavigate ? (event) => onNavigate(event, feature) : undefined}
83
+ className={itemClassName}
84
+ style={typeof itemStyle === 'function' ? itemStyle(index) : itemStyle}
71
85
  />
72
86
  ))}
73
87
  </div>
@@ -35,6 +35,8 @@ export default function SectionCta({
35
35
  contactLabel,
36
36
  email,
37
37
  secondaryRows = [],
38
+ slotClass,
39
+ slotStyle,
38
40
  className = '',
39
41
  }) {
40
42
  if (variant === 'centered') {
@@ -52,6 +54,8 @@ export default function SectionCta({
52
54
  actions={actions}
53
55
  actionsClass="pt-4 flex flex-col sm:flex-row gap-4 justify-center"
54
56
  gap="gap-8"
57
+ slotClass={slotClass}
58
+ slotStyle={slotStyle}
55
59
  />
56
60
  </div>
57
61
  </section>
@@ -82,6 +86,8 @@ export default function SectionCta({
82
86
  headlineAs="div"
83
87
  headlineClass="text-auto kol-sans-heading-01"
84
88
  gap="gap-2"
89
+ slotClass={slotClass}
90
+ slotStyle={slotStyle}
85
91
  className="self-stretch"
86
92
  />
87
93
  ))}
@@ -24,6 +24,8 @@ export default function SectionFaq({
24
24
  items = [],
25
25
  singleOpen = false,
26
26
  defaultOpen,
27
+ slotClass,
28
+ slotStyle,
27
29
  className = '',
28
30
  /* the section sits on the family's cap; the list itself is a READING
29
31
  * column inside it — a measure is a content decision, not the frame */
@@ -35,7 +37,7 @@ export default function SectionFaq({
35
37
  <div className="max-w-[var(--kol-container-max,var(--kol-content-shell,1800px))] mx-auto">
36
38
  <div className={innerClassName}>
37
39
  {(label || headline || body || actions) && (
38
- <SectionText label={label} headline={headline} headlineSize={headlineSize} body={body} actions={actions} />
40
+ <SectionText label={label} headline={headline} headlineSize={headlineSize} body={body} actions={actions} slotClass={slotClass} slotStyle={slotStyle} />
39
41
  )}
40
42
  {items.length > 0 && (
41
43
  <Accordion className="mt-0 mb-0">
@@ -109,6 +109,7 @@ function MediaLayer({ media }) {
109
109
  * slide's title in the typeface's own font and routes the CTA through the SPA)
110
110
  * @param {string} height 'full' | '80' | '60' (viewport tiers) · 'lg' | 'md' | 'screen' (aliases) · or a height class string
111
111
  * @param {ReactNode} label · headline · body · actions the composed text (SectionText)
112
+ * @param {object} slotClass · slotStyle per-slot class / style, forwarded to SectionText (a consumer's reveal seam)
112
113
  * @param {string} [headlineSize='display-04']
113
114
  * @param {string} [panelMaxWidth='max-w-[440px]'] the glass panel's cap on the composed route
114
115
  * @param {object} panelProps everything else on the glass panel (user ask 2026-08-26):
@@ -150,6 +151,8 @@ export default function SectionHero({
150
151
  actions,
151
152
  panelMaxWidth = 'max-w-[440px]',
152
153
  panelProps,
154
+ slotClass,
155
+ slotStyle,
153
156
  panel,
154
157
  children,
155
158
  align = 'center',
@@ -179,6 +182,8 @@ export default function SectionHero({
179
182
  bodyClass="kol-sans-body-02 text-body"
180
183
  actionsClass="flex flex-wrap gap-4 justify-center"
181
184
  className="max-w-[var(--kol-content-column)]"
185
+ slotClass={slotClass}
186
+ slotStyle={slotStyle}
182
187
  />
183
188
  </div>
184
189
  </section>
@@ -239,6 +244,8 @@ export default function SectionHero({
239
244
  bodyClass="kol-sans-body-02 text-body"
240
245
  actionsClass={`flex flex-wrap gap-4${align === 'center' ? ' justify-center' : ''}`}
241
246
  className={media == null ? `${panelMaxWidth} mx-auto` : ''}
247
+ slotClass={slotClass}
248
+ slotStyle={slotStyle}
242
249
  />
243
250
  ) : null
244
251
  /* no media → the TEXT-ONLY hero: the composed text on the surface, no glass
@@ -36,6 +36,7 @@ import SectionText from '../molecules/SectionText.jsx'
36
36
  * @param {ReactNode} caption mono caption + gradient veil over the media
37
37
  * @param {string} bgImage inline cover background on the section
38
38
  * @param {boolean} fullBleed span the full viewport width
39
+ * @param {object} slotClass · slotStyle per-slot class / style on the text block (reveal seam)
39
40
  * @param {string} className · innerClassName · columnClassName layout seams
40
41
  */
41
42
  export default function SectionSplit({
@@ -54,6 +55,8 @@ export default function SectionSplit({
54
55
  caption,
55
56
  bgImage,
56
57
  fullBleed = false,
58
+ slotClass,
59
+ slotStyle,
57
60
  className = '',
58
61
  innerClassName = '',
59
62
  columnClassName = '',
@@ -87,6 +90,8 @@ export default function SectionSplit({
87
90
  actions={actions}
88
91
  actionsClass={`flex flex-wrap gap-4 pt-2${centred ? ' justify-center' : ''}`}
89
92
  align={centred ? 'center' : 'start'}
93
+ slotClass={slotClass}
94
+ slotStyle={slotStyle}
90
95
  className={`max-w-[640px] ${mediaFirst ? 'order-2' : ''} ${columnClassName}`}
91
96
  >
92
97
  {meta && meta.length > 0 && (