@kolkrabbi/kol-component 0.71.0 → 0.73.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.71.0",
3
+ "version": "0.73.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",
@@ -12,6 +12,13 @@ import ContentText from './ContentText.jsx'
12
12
  * canvas the card is the ratio frame; media fills it absolutely and the
13
13
  * text plate floats on top — typeface (details over specimen)
14
14
  *
15
+ * EXPANDED (2026-08-26, the GridCard retirement): a fill-card variant with
16
+ * `expanded` grows to a 2×2 cell — `grid-column / grid-row: span 2`, no
17
+ * ratio — and flips to a ROW: media on the right at 50%, `expandedContent`
18
+ * (the info field + button the consumer authors) on the left in a lg-padded,
19
+ * space-between column. Verbatim the 2×2 GridCard shipped; without it the
20
+ * three GridCard repos could not swap. Neighbour-hiding stays consumer-side.
21
+ *
15
22
  * Padding spells the TOKENS (--kol-pad-card-{sm,md,lg} = 12/16/24), never a
16
23
  * literal. Passing `pad` overrides the plate padding with one token step
17
24
  * (named `pad`, not `size` — `size` is the ruled TEXT slot). Image-only cards
@@ -91,6 +98,8 @@ export default function ContentCard({
91
98
  zoom,
92
99
  control,
93
100
  actions,
101
+ expanded = false,
102
+ expandedContent,
94
103
  selected = false,
95
104
  onClick,
96
105
  href,
@@ -178,11 +187,18 @@ export default function ContentCard({
178
187
  </>
179
188
  ) : box.layout === 'fill-card' ? (
180
189
  <>
181
- <div className="flex-1 min-w-0 relative overflow-hidden">
190
+ <div className="flex-1 min-w-0 min-h-0 relative overflow-hidden" style={expanded ? { flex: '0 0 50%' } : undefined}>
182
191
  <ContentMedia ratio={null} {...mediaProps}>{media}</ContentMedia>
183
192
  {controlNode}
184
193
  </div>
185
- {textNode}
194
+ {expanded ? (
195
+ <div
196
+ className="flex flex-1 flex-col justify-between overflow-auto"
197
+ style={{ padding: 'var(--kol-pad-card-lg)' }}
198
+ >
199
+ {expandedContent}
200
+ </div>
201
+ ) : textNode}
186
202
  </>
187
203
  ) : box.layout === 'drawer' ? (
188
204
  <>
@@ -223,7 +239,7 @@ export default function ContentCard({
223
239
  const hoverBg = HOVER[variant]
224
240
 
225
241
  const common = {
226
- className: `kol-card group flex flex-col ${box.layout === 'drawer' ? 'relative overflow-hidden rounded-[var(--kol-radius-sm)]' : ''} ${framed ? 'overflow-hidden rounded-[var(--kol-radius-sm)]' : ''} ${box.border ? 'border' : ''} ${box.layout === 'canvas' ? 'relative' : ''} ${interactive ? 'cursor-pointer select-none' : ''} ${hoverBg && interactive ? 'kol-content-hover' : ''} ${className}`.trim(),
242
+ className: `kol-card group flex ${expanded ? 'flex-row-reverse' : 'flex-col'} ${box.layout === 'drawer' ? 'relative overflow-hidden rounded-[var(--kol-radius-sm)]' : ''} ${framed ? 'overflow-hidden rounded-[var(--kol-radius-sm)]' : ''} ${box.border ? 'border' : ''} ${box.layout === 'canvas' ? 'relative' : ''} ${interactive ? 'cursor-pointer select-none' : ''} ${hoverBg && interactive ? 'kol-content-hover' : ''} ${className}`.trim(),
227
243
  style: {
228
244
  /* same reason as ContentRow: rest colours are PROPERTIES, because an
229
245
  * inline background/borderColor outranks the hover class and the step
@@ -231,8 +247,13 @@ export default function ContentCard({
231
247
  '--kol-card-bg': box.bg ?? undefined,
232
248
  '--kol-card-border': box.border ? (selected ? 'var(--kol-fg-64)' : box.border) : undefined,
233
249
  '--kol-content-hover-bg': hoverBg ?? undefined,
234
- aspectRatio: box.height ? undefined : (box.layout !== 'stack' ? r : undefined),
250
+ aspectRatio: box.height || expanded ? undefined : (box.layout !== 'stack' ? r : undefined),
235
251
  height: box.height,
252
+ gridColumn: expanded ? 'span 2' : undefined,
253
+ gridRow: expanded ? 'span 2' : undefined,
254
+ /* the grow/shrink between the two states rides the house curve; only a
255
+ * card that CAN expand carries the transition */
256
+ transition: expandedContent != null ? 'all 300ms var(--kol-ease-house)' : undefined,
236
257
  },
237
258
  }
238
259
 
@@ -13,7 +13,9 @@
13
13
  * Every default class is a SEAM (the ContentText pattern): pass `labelClass`
14
14
  * / `bodyClass` / `actionsClass` to replace the string whole — that is how
15
15
  * SectionHero's eyebrow and SectionCta's rows wear their own voices on the
16
- * same anatomy. No casing transforms author strings in their final case.
16
+ * same anatomy. THE LABEL IS UPPERCASE BY ROLE (user ruling 2026-08-26):
17
+ * an eyebrow is caps by design language, so the span carries
18
+ * `.kol-section-text-eyebrow`; headline, body and actions render as authored.
17
19
  *
18
20
  * @param {ReactNode} label section header / kicker
19
21
  * @param {ReactNode} headline the heading
@@ -60,7 +62,9 @@ export default function SectionText({
60
62
  const alignCls = align === 'center' ? 'items-center text-center' : 'items-start text-left'
61
63
  return (
62
64
  <div className={`kol-section-text flex flex-col ${gap} ${alignCls} ${className}`.replace(/\s+/g, ' ').trim()}>
63
- {label && <span className={labelClass}>{label}</span>}
65
+ {/* `kol-section-text-eyebrow` = uppercase by ROLE (kol-theme ≥0.55.0);
66
+ * `labelClass` is the voice riding beside it */}
67
+ {label && <span className={`kol-section-text-eyebrow ${labelClass}`}>{label}</span>}
64
68
  {headline && <Headline className={headlineClass ?? (HEADLINE_ROLE[headlineSize] ?? HEADLINE_ROLE['heading-02'])}>{headline}</Headline>}
65
69
  {body && (typeof body === 'string' ? <p className={bodyClass}>{body}</p> : <div className={bodyClass}>{body}</div>)}
66
70
  {children}
@@ -11,7 +11,7 @@ import SectionText, { HEADLINE_ROLE } from '../molecules/SectionText.jsx'
11
11
  * Every block is presence-gated (header on any text prop, cards on
12
12
  * `features.length`, actions on `actions`), so omitted slots emit no empty
13
13
  * nodes. Card links are plain anchors; pass `onNavigate` to intercept same-tab
14
- * navigations in an SPA. Casing is authored at the call site.
14
+ * navigations in an SPA. The label is uppercase by role; the rest as authored.
15
15
  *
16
16
  * @param {{title, icon, visual, description, href, backgroundColor, imageAspectRatio}[]} features
17
17
  * @param {ReactNode} label · headline · body the header (heading-03 + mono lede by default)
@@ -6,7 +6,7 @@ import SectionText from '../molecules/SectionText.jsx'
6
6
  * label-over-value rows, each a `SectionText` (label in the helper voice,
7
7
  * value as a heading-01; a row with `href` renders its value as a link).
8
8
  * `CtaGlobal` is this component under its old name (alias kept). Columns sit
9
- * side by side from `md` up and stack below. Casing is authored at the call site.
9
+ * side by side from `md` up and stack below. Row labels are uppercase by role.
10
10
  *
11
11
  * @param {ReactNode} eyebrow left-column display wordmark (kol-sans-display-01)
12
12
  * @param {ReactNode} promptLabel prompt-row label
@@ -15,7 +15,7 @@ import SectionText from '../molecules/SectionText.jsx'
15
15
  * `meta` and `actions` are mutually exclusive by intent (pick one); `actions`
16
16
  * is conventionally a row of KOL Buttons. The media column renders only when
17
17
  * `media` is passed, and `caption` gates both the gradient veil and the
18
- * caption element. Casing is authored at the call site.
18
+ * caption element. The label is uppercase by role; the rest renders as authored.
19
19
  *
20
20
  * @param {ReactNode} label mono eyebrow above the headline (accent)
21
21
  * @param {ReactNode} headline display pull; `<em>` renders as the italic accent