@kolkrabbi/kol-component 0.102.2 → 0.103.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.102.2",
3
+ "version": "0.103.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",
@@ -81,7 +81,10 @@ const RAMP = {
81
81
  * caption disappeared. */
82
82
  /* the drawer is an INVERSE surface, so the roles do not apply — but the
83
83
  * ladder does: one full ink, the rest stepped. */
84
- card: { title: 'kol-sans-display-03 text-fg-inverse', meta: 'kol-mono-12 text-fg-inverse-64', body: 'kol-mono-14 text-fg-inverse-64', date: 'kol-mono-12 text-fg-inverse-48', tags: 'flex flex-wrap gap-2' },
84
+ /* RULED ON /work GRID + shelf (WorkCardAndShelf, 2026-08-27): the title is one
85
+ * line (the site passes its face as titleClass; the truncation is the card's),
86
+ * the meta is the helper voice, uppercase, at inverse 80 */
87
+ card: { title: 'kol-sans-display-03 text-fg-inverse truncate', meta: 'kol-helper-12 uppercase text-fg-inverse-80', body: 'kol-mono-14 text-fg-inverse-64', date: 'kol-mono-12 text-fg-inverse-48', tags: 'flex flex-wrap gap-2' },
85
88
  /* verbatim from WorkListItem: title `kol-mono-14` truncated · type
86
89
  * `kol-mono-12 md:kol-mono-14` at FULL ink, no opacity step · year
87
90
  * `kol-mono-12 text-fg-64` · description `kol-sans-heading-03 text-auto`.
@@ -1,4 +1,5 @@
1
1
  import SectionCardItem from '../molecules/SectionCardItem.jsx'
2
+ import { surfaceClass } from './sectionSurface.js'
2
3
  import SectionText, { HEADLINE_ROLE } from '../molecules/SectionText.jsx'
3
4
  import useSectionTheme from '../hooks/useSectionTheme.js'
4
5
  import { minHeightClass } from './sectionHeights.js'
@@ -29,9 +30,12 @@ import { minHeightClass } from './sectionHeights.js'
29
30
  * @param {object|Function} itemStyle inline style per card, or `(index) => style`
30
31
  * @param {object} slotClass · slotStyle per-slot class / style on the header text
31
32
  * @param {string} sectionClassName · wrapperClassName · cardsWrapperClassName · actionsClassName · headerClassName · headerTextWidthClass layout seams
33
+ * @param {'primary'|'secondary'|'tertiary'|'inverse'|'auto'|'none'|string} background the section's surface
34
+ * (SectionBackgroundProp, 2026-08-27) — a named surface, `none`, or a raw utility / token string; default = what it painted before
32
35
  */
33
36
  export default function SectionCards({
34
37
  theme,
38
+ background,
35
39
  height = '60',
36
40
  features = [],
37
41
  eyebrow,
@@ -60,7 +64,7 @@ export default function SectionCards({
60
64
  const eb = eyebrow ?? label
61
65
  const [themeRef, themeStamp] = useSectionTheme(theme)
62
66
  return (
63
- <section ref={themeRef} data-theme={themeStamp} className={`w-full flex flex-col justify-center ${minHeightClass(height)} ${theme ? 'bg-surface-primary' : ''} ${sectionClassName}`.replace(/\s+/g, ' ').trim()}>
67
+ <section ref={themeRef} data-theme={themeStamp} className={`w-full flex flex-col justify-center ${minHeightClass(height)} ${surfaceClass(background, theme ? 'primary' : 'none')} ${sectionClassName}`.replace(/\s+/g, ' ').trim()}>
64
68
  <div className={wrapperClassName}>
65
69
  {(eb || headline || body) && (
66
70
  <SectionText
@@ -1,4 +1,5 @@
1
1
  import Button from '../atoms/Button.jsx'
2
+ import { surfaceClass } from './sectionSurface.js'
2
3
  import SectionText from '../molecules/SectionText.jsx'
3
4
  import { minHeightClass } from './sectionHeights.js'
4
5
 
@@ -26,9 +27,12 @@ import { minHeightClass } from './sectionHeights.js'
26
27
  * @param {string} email contact-row value + `mailto:` target; omit to drop the row
27
28
  * @param {{label: ReactNode, value: ReactNode, href?: string}[]} secondaryRows extra rows between prompt and contact
28
29
  * @param {string} className extra classes on the section
30
+ * @param {'primary'|'secondary'|'tertiary'|'inverse'|'auto'|'none'|string} background the section's surface
31
+ * (SectionBackgroundProp, 2026-08-27) — a named surface, `none`, or a raw utility / token string; default = what it painted before
29
32
  */
30
33
  export default function SectionCta({
31
34
  variant = 'editorial',
35
+ background,
32
36
  height = '60',
33
37
  headline,
34
38
  body,
@@ -45,7 +49,7 @@ export default function SectionCta({
45
49
  }) {
46
50
  if (variant === 'centered') {
47
51
  return (
48
- <section className={`w-full flex flex-col justify-center py-24 ${minHeightClass(height)} ${className}`.replace(/\s+/g, ' ').trim()}>
52
+ <section className={`w-full flex flex-col justify-center py-24 ${surfaceClass(background, 'auto')} ${minHeightClass(height)} ${className}`.replace(/\s+/g, ' ').trim()}>
49
53
  <div className="w-full max-w-[var(--kol-container-max,var(--kol-content-shell,1800px))] mx-auto">
50
54
  <div className="w-32 h-px bg-fg-24 mx-auto mb-8" />
51
55
  <SectionText
@@ -72,7 +76,7 @@ export default function SectionCta({
72
76
  ...(email ? [{ label: contactLabel, value: email, href: `mailto:${email}` }] : []),
73
77
  ]
74
78
  return (
75
- <section className={`w-full bg-auto flex flex-col justify-center ${minHeightClass(height)} ${className}`.replace(/\s+/g, ' ').trim()}>
79
+ <section className={`w-full ${surfaceClass(background, 'auto')} flex flex-col justify-center ${minHeightClass(height)} ${className}`.replace(/\s+/g, ' ').trim()}>
76
80
  {/* the family's ONE cap — the shell's --kol-container-max ladder (user
77
81
  * ruling 2026-08-26; the 1600 SectionCtaEditorial asked for was a third
78
82
  * number beside split's 1200 and cards' 1400) — the surface stays full
@@ -1,4 +1,5 @@
1
1
  import { useState } from 'react'
2
+ import { surfaceClass } from './sectionSurface.js'
2
3
  import { Accordion, AccordionPanel } from '../molecules/Accordion.jsx'
3
4
  import SectionText from '../molecules/SectionText.jsx'
4
5
  import { minHeightClass } from './sectionHeights.js'
@@ -17,6 +18,8 @@ import { minHeightClass } from './sectionHeights.js'
17
18
  * @param {boolean} [singleOpen=false] opening one panel closes the others
18
19
  * @param {number} [defaultOpen] index open on mount (singleOpen) — omit for all closed
19
20
  * @param {string} className · innerClassName layout seams
21
+ * @param {'primary'|'secondary'|'tertiary'|'inverse'|'auto'|'none'|string} background the section's surface
22
+ * (SectionBackgroundProp, 2026-08-27) — a named surface, `none`, or a raw utility / token string; default = what it painted before
20
23
  */
21
24
  export default function SectionFaq({
22
25
  eyebrow,
@@ -29,6 +32,7 @@ export default function SectionFaq({
29
32
  singleOpen = false,
30
33
  defaultOpen,
31
34
  height = '60',
35
+ background,
32
36
  slotClass,
33
37
  slotStyle,
34
38
  className = '',
@@ -40,7 +44,7 @@ export default function SectionFaq({
40
44
  const eb = eyebrow ?? label
41
45
  const [open, setOpen] = useState(defaultOpen ?? null)
42
46
  return (
43
- <section className={`kol-section-faq w-full flex flex-col justify-center px-5 py-16 md:px-8 md:py-24 lg:px-14 ${minHeightClass(height)} ${className}`.replace(/\s+/g, ' ').trim()}>
47
+ <section className={`kol-section-faq w-full flex flex-col justify-center px-5 py-16 md:px-8 md:py-24 lg:px-14 ${minHeightClass(height)} ${surfaceClass(background, 'none')} ${className}`.replace(/\s+/g, ' ').trim()}>
44
48
  <div className="w-full max-w-[var(--kol-container-max,var(--kol-content-shell,1800px))] mx-auto">
45
49
  <div className={innerClassName}>
46
50
  {(eb || headline || body || actions) && (
@@ -1,4 +1,5 @@
1
1
  import { isValidElement } from 'react'
2
+ import { surfaceClass } from './sectionSurface.js'
2
3
  import HlsVideo from '../atoms/HlsVideo.jsx'
3
4
  import Image from '../atoms/Image.jsx'
4
5
  import OverlayGlassPanel from '../utilities/OverlayGlassPanel.jsx'
@@ -143,10 +144,13 @@ function MediaLayer({ media }) {
143
144
  * split: 'left' | 'right' — the media side
144
145
  * @param {boolean} fullBleed span the full viewport width (100vw breakout) — split
145
146
  * @param {string} className extra classes on the section
147
+ * @param {'primary'|'secondary'|'tertiary'|'inverse'|'auto'|'none'|string} background the section's surface
148
+ * (SectionBackgroundProp, 2026-08-27) — a named surface, `none`, or a raw utility / token string; default = what it painted before
146
149
  */
147
150
  export default function SectionHero({
148
151
  variant = 'media',
149
152
  theme,
153
+ background,
150
154
  fullBleed = false,
151
155
  media,
152
156
  overlayOpacity = 0,
@@ -189,7 +193,7 @@ export default function SectionHero({
189
193
  /* `label` = alias of `eyebrow` (2026-08-27) */
190
194
  const eb = eyebrow ?? label
191
195
  const [themeRef, themeStamp] = useSectionTheme(theme)
192
- const themed = theme ? 'bg-surface-primary' : ''
196
+ const themed = surfaceClass(background, theme ? 'primary' : 'none')
193
197
 
194
198
  if (variant === 'split') {
195
199
  const mediaFirst = align !== 'right'
@@ -1,4 +1,5 @@
1
1
  import { useId, useState } from 'react'
2
+ import { surfaceClass } from './sectionSurface.js'
2
3
  import Input from '../atoms/Input.jsx'
3
4
  import Button from '../atoms/Button.jsx'
4
5
  import SectionText from '../molecules/SectionText.jsx'
@@ -44,10 +45,13 @@ import { minHeightClass } from './sectionHeights.js'
44
45
  * @param {string} inputId id override for the email input (default useId-generated)
45
46
  * @param {object} slotClass · slotStyle per-slot class / style on the SectionText (reveal seam)
46
47
  * @param {string} className extra classes on the section
48
+ * @param {'primary'|'secondary'|'tertiary'|'inverse'|'auto'|'none'|string} background the section's surface
49
+ * (SectionBackgroundProp, 2026-08-27) — a named surface, `none`, or a raw utility / token string; default = what it painted before
47
50
  */
48
51
  export default function SectionNewsletter({
49
52
  height = '60',
50
53
  theme,
54
+ background,
51
55
  eyebrow,
52
56
  label,
53
57
  headline,
@@ -99,7 +103,7 @@ export default function SectionNewsletter({
99
103
  id={id}
100
104
  ref={themeRef}
101
105
  data-theme={themeStamp}
102
- className={`kol-section-newsletter w-full flex flex-col justify-center py-24 ${theme ? 'bg-surface-primary text-auto' : ''} ${minHeightClass(height)} ${className}`.replace(/\s+/g, ' ').trim()}
106
+ className={`kol-section-newsletter w-full flex flex-col justify-center py-24 ${surfaceClass(background, theme ? 'primary' : 'none')} ${theme ? 'text-auto' : ''} ${minHeightClass(height)} ${className}`.replace(/\s+/g, ' ').trim()}
103
107
  >
104
108
  {/* the family's ONE cap — the shell's --kol-container-max ladder — and
105
109
  * inside it the lede's MEASURE on a wrapper (SectionNewsletterForm,
@@ -1,4 +1,5 @@
1
1
  import SectionText from '../molecules/SectionText.jsx'
2
+ import { surfaceClass } from './sectionSurface.js'
2
3
  import useSectionTheme from '../hooks/useSectionTheme.js'
3
4
  import { minHeightClass } from './sectionHeights.js'
4
5
 
@@ -50,10 +51,13 @@ import { minHeightClass } from './sectionHeights.js'
50
51
  * @param {boolean} fullBleed span the full viewport width
51
52
  * @param {object} slotClass · slotStyle per-slot class / style on the text block (reveal seam)
52
53
  * @param {string} className · innerClassName · columnClassName layout seams
54
+ * @param {'primary'|'secondary'|'tertiary'|'inverse'|'auto'|'none'|string} background the section's surface
55
+ * (SectionBackgroundProp, 2026-08-27) — a named surface, `none`, or a raw utility / token string; default = what it painted before
53
56
  */
54
57
 
55
58
  export default function SectionSplit({
56
59
  theme,
60
+ background,
57
61
  eyebrow,
58
62
  label,
59
63
  headline,
@@ -93,7 +97,7 @@ export default function SectionSplit({
93
97
  <section
94
98
  ref={themeRef}
95
99
  data-theme={themeStamp}
96
- className={`kol-section-split flex flex-col justify-center px-5 py-16 md:px-8 md:py-24 lg:px-14 lg:py-32 [--kol-section-py:4rem] md:[--kol-section-py:6rem] lg:[--kol-section-py:8rem] ${minHeightClass(height)} ${bleed} ${className}`.replace(/\s+/g, ' ').trim()}
100
+ className={`kol-section-split flex flex-col justify-center px-5 py-16 md:px-8 md:py-24 lg:px-14 lg:py-32 [--kol-section-py:4rem] md:[--kol-section-py:6rem] lg:[--kol-section-py:8rem] ${minHeightClass(height)} ${surfaceClass(background, 'none')} ${bleed} ${className}`.replace(/\s+/g, ' ').trim()}
97
101
  style={sectionStyle}
98
102
  >
99
103
  {/* ONE cap for the whole section family (user ruling 2026-08-26): the shell's
@@ -0,0 +1,17 @@
1
+ /* ONE surface prop across the section family (SectionBackgroundProp, kol-website
2
+ * 2026-08-27 — user: "ALL section family should pass bg as props"). A named
3
+ * surface, `none`, or a raw utility / token string for the odd case
4
+ * (`'bg-fg-absolute-16'`). Each section's default is what it painted before,
5
+ * so nothing moves on the bump. `theme` keeps scoping ink; this is the paint. */
6
+ export const SURFACES = {
7
+ primary: 'bg-surface-primary',
8
+ secondary: 'bg-surface-secondary',
9
+ tertiary: 'bg-surface-tertiary',
10
+ inverse: 'bg-surface-inverse',
11
+ auto: 'bg-auto',
12
+ none: '',
13
+ }
14
+ export const surfaceClass = (background, fallback = 'none') => {
15
+ const key = background ?? fallback
16
+ return key in SURFACES ? SURFACES[key] : key
17
+ }