@kolkrabbi/kol-component 0.81.0 → 0.83.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.81.0",
3
+ "version": "0.83.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",
@@ -1,6 +1,7 @@
1
1
  import SectionCardItem from '../molecules/SectionCardItem.jsx'
2
2
  import SectionText, { HEADLINE_ROLE } from '../molecules/SectionText.jsx'
3
3
  import useSectionTheme from '../hooks/useSectionTheme.js'
4
+ import { minHeightClass } from './sectionHeights.js'
4
5
 
5
6
  /**
6
7
  * SectionCards — the "N-up feature cards" band: a `SectionText` header over a
@@ -18,6 +19,8 @@ import useSectionTheme from '../hooks/useSectionTheme.js'
18
19
  * `inverse` = the paired theme of the nearest live one, following the toggle;
19
20
  * `light` / `dark` pinned; omit to inherit. Stamps `data-theme` on the root and
20
21
  * paints its surface — every token inside resolves to the other theme's.
22
+ * @param {'full'|'80'|'60'|string} [height='60'] min-height on the family's ladder — full = 100dvh,
23
+ * 80 = 70svh / 80vh, 60 = 50svh / 60vh (default); content stays vertically centred inside it
21
24
  * @param {{title, icon, visual, description, href, backgroundColor, imageAspectRatio}[]} features
22
25
  * @param {ReactNode} label · headline · body the header (heading-03 + mono lede by default)
23
26
  * @param {ReactNode} actions centred action row under the cards
@@ -29,6 +32,7 @@ import useSectionTheme from '../hooks/useSectionTheme.js'
29
32
  */
30
33
  export default function SectionCards({
31
34
  theme,
35
+ height = '60',
32
36
  features = [],
33
37
  label,
34
38
  headline,
@@ -53,7 +57,7 @@ export default function SectionCards({
53
57
  }) {
54
58
  const [themeRef, themeStamp] = useSectionTheme(theme)
55
59
  return (
56
- <section ref={themeRef} data-theme={themeStamp} className={`w-full ${theme ? 'bg-surface-primary' : ''} ${sectionClassName}`.replace(/\s+/g, ' ').trim()}>
60
+ <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()}>
57
61
  <div className={wrapperClassName}>
58
62
  {(label || headline || body) && (
59
63
  <SectionText
@@ -1,5 +1,6 @@
1
1
  import Button from '../atoms/Button.jsx'
2
2
  import SectionText from '../molecules/SectionText.jsx'
3
+ import { minHeightClass } from './sectionHeights.js'
3
4
 
4
5
  /**
5
6
  * SectionCta — the editorial two-column contact/CTA band: a large display
@@ -14,6 +15,8 @@ import SectionText from '../molecules/SectionText.jsx'
14
15
  * heading, a mono line and Buttons, centred — which was `FoundryCTA`, a second
15
16
  * way to render SectionText. FoundryCTA is its deprecated alias.
16
17
  *
18
+ * @param {'full'|'80'|'60'|string} [height='60'] min-height on the family's ladder — full = 100dvh,
19
+ * 80 = 70svh / 80vh, 60 = 50svh / 60vh (default); content stays vertically centred inside it
17
20
  * @param {'editorial'|'centered'} variant
18
21
  * @param {ReactNode} headline · body · actions centered: the SectionText slots
19
22
  * @param {ReactNode} eyebrow left-column display wordmark (kol-sans-display-01)
@@ -26,6 +29,7 @@ import SectionText from '../molecules/SectionText.jsx'
26
29
  */
27
30
  export default function SectionCta({
28
31
  variant = 'editorial',
32
+ height = '60',
29
33
  headline,
30
34
  body,
31
35
  actions,
@@ -41,8 +45,8 @@ export default function SectionCta({
41
45
  }) {
42
46
  if (variant === 'centered') {
43
47
  return (
44
- <section className={`w-full py-24 ${className}`.trim()}>
45
- <div className="max-w-[var(--kol-container-max,var(--kol-content-shell,1800px))] mx-auto">
48
+ <section className={`w-full flex flex-col justify-center py-24 ${minHeightClass(height)} ${className}`.replace(/\s+/g, ' ').trim()}>
49
+ <div className="w-full max-w-[var(--kol-container-max,var(--kol-content-shell,1800px))] mx-auto">
46
50
  <div className="w-32 h-px bg-fg-24 mx-auto mb-8" />
47
51
  <SectionText
48
52
  align="center"
@@ -68,7 +72,7 @@ export default function SectionCta({
68
72
  ...(email ? [{ label: contactLabel, value: email, href: `mailto:${email}` }] : []),
69
73
  ]
70
74
  return (
71
- <section className={`w-full bg-auto ${className}`.trim()}>
75
+ <section className={`w-full bg-auto flex flex-col justify-center ${minHeightClass(height)} ${className}`.replace(/\s+/g, ' ').trim()}>
72
76
  {/* the family's ONE cap — the shell's --kol-container-max ladder (user
73
77
  * ruling 2026-08-26; the 1600 SectionCtaEditorial asked for was a third
74
78
  * number beside split's 1200 and cards' 1400) — the surface stays full
@@ -1,6 +1,7 @@
1
1
  import { useState } from 'react'
2
2
  import { Accordion, AccordionPanel } from '../molecules/Accordion.jsx'
3
3
  import SectionText from '../molecules/SectionText.jsx'
4
+ import { minHeightClass } from './sectionHeights.js'
4
5
 
5
6
  /**
6
7
  * SectionFaq — a `SectionText` header over an `Accordion` of question /
@@ -10,6 +11,8 @@ import SectionText from '../molecules/SectionText.jsx'
10
11
  *
11
12
  * @param {ReactNode} label · headline · body · actions the header (SectionText)
12
13
  * @param {string} [headlineSize='heading-02']
14
+ * @param {'full'|'80'|'60'|string} [height='60'] min-height on the family's ladder — full = 100dvh,
15
+ * 80 = 70svh / 80vh, 60 = 50svh / 60vh (default); content stays vertically centred inside it
13
16
  * @param {{ q: ReactNode, a: ReactNode, meta?: ReactNode }[]} items
14
17
  * @param {boolean} [singleOpen=false] opening one panel closes the others
15
18
  * @param {number} [defaultOpen] index open on mount (singleOpen) — omit for all closed
@@ -24,6 +27,7 @@ export default function SectionFaq({
24
27
  items = [],
25
28
  singleOpen = false,
26
29
  defaultOpen,
30
+ height = '60',
27
31
  slotClass,
28
32
  slotStyle,
29
33
  className = '',
@@ -33,8 +37,8 @@ export default function SectionFaq({
33
37
  }) {
34
38
  const [open, setOpen] = useState(defaultOpen ?? null)
35
39
  return (
36
- <section className={`kol-section-faq w-full px-5 py-16 md:px-8 md:py-24 lg:px-14 ${className}`.trim()}>
37
- <div className="max-w-[var(--kol-container-max,var(--kol-content-shell,1800px))] mx-auto">
40
+ <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()}>
41
+ <div className="w-full max-w-[var(--kol-container-max,var(--kol-content-shell,1800px))] mx-auto">
38
42
  <div className={innerClassName}>
39
43
  {(label || headline || body || actions) && (
40
44
  <SectionText label={label} headline={headline} headlineSize={headlineSize} body={body} actions={actions} slotClass={slotClass} slotStyle={slotStyle} />
@@ -1,5 +1,6 @@
1
1
  import SectionText from '../molecules/SectionText.jsx'
2
2
  import useSectionTheme from '../hooks/useSectionTheme.js'
3
+ import { minHeightClass } from './sectionHeights.js'
3
4
 
4
5
  /**
5
6
  * SectionSplit — the media-and-text split section, on `SectionText` (the
@@ -38,12 +39,15 @@ import useSectionTheme from '../hooks/useSectionTheme.js'
38
39
  * lifted corners were being cut flat). `mediaHover` and a self-animating node are
39
40
  * mutually exclusive in practice — pick one.
40
41
  * @param {'right'|'left'|'center'} [align='right'] media side, or centred single column
42
+ * @param {'full'|'80'|'60'|string} [height='60'] min-height on the family's ladder — full = 100dvh,
43
+ * 80 = 70svh / 80vh, 60 = 50svh / 60vh (default); the columns stay vertically centred inside it
41
44
  * @param {ReactNode} caption mono caption + gradient veil over the media
42
45
  * @param {string} bgImage inline cover background on the section
43
46
  * @param {boolean} fullBleed span the full viewport width
44
47
  * @param {object} slotClass · slotStyle per-slot class / style on the text block (reveal seam)
45
48
  * @param {string} className · innerClassName · columnClassName layout seams
46
49
  */
50
+
47
51
  export default function SectionSplit({
48
52
  theme,
49
53
  label,
@@ -58,6 +62,7 @@ export default function SectionSplit({
58
62
  mediaHover = false,
59
63
  mediaClip = true,
60
64
  align = 'right',
65
+ height = '60',
61
66
  caption,
62
67
  bgImage,
63
68
  fullBleed = false,
@@ -81,13 +86,13 @@ export default function SectionSplit({
81
86
  <section
82
87
  ref={themeRef}
83
88
  data-theme={themeStamp}
84
- className={`kol-section-split px-5 py-16 md:px-8 md:py-24 lg:px-14 lg:py-32 ${bleed} ${className}`.replace(/\s+/g, ' ').trim()}
89
+ 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 ${minHeightClass(height)} ${bleed} ${className}`.replace(/\s+/g, ' ').trim()}
85
90
  style={sectionStyle}
86
91
  >
87
92
  {/* ONE cap for the whole section family (user ruling 2026-08-26): the shell's
88
93
  * --kol-container-max ladder — 100% → 1400 → 1600 → 1800. Split ran 1200,
89
94
  * cards 1400, the CTA 1600: three numbers for one job, unflagged. */}
90
- <div className={`max-w-[var(--kol-container-max,var(--kol-content-shell,1800px))] mx-auto ${grid} ${innerClassName}`.replace(/\s+/g, ' ').trim()}>
95
+ <div className={`w-full max-w-[var(--kol-container-max,var(--kol-content-shell,1800px))] mx-auto ${grid} ${innerClassName}`.replace(/\s+/g, ' ').trim()}>
91
96
  {/* `order` rather than `flex-row-reverse`: the grid is one column below
92
97
  * 901px, and DOM order is what decides the stack there. */}
93
98
  <SectionText
@@ -0,0 +1,12 @@
1
+ /* The section family's height ladder, as MIN-height — content may still grow,
2
+ * the content stays vertically centred inside it (SectionSplitHeight →
3
+ * SectionHeightLadder, kol-website 2026-08-27: "we need to set some sizes").
4
+ * Same three names as SectionHero's presets; literal strings — a class built
5
+ * at runtime is not a class (the SPLIT_HEIGHTS lesson). Anything else passes
6
+ * through as a class string. */
7
+ export const MIN_HEIGHTS = {
8
+ full: 'min-h-dvh',
9
+ 80: 'min-h-[70svh] md:min-h-[80vh]',
10
+ 60: 'min-h-[50svh] md:min-h-[60vh]',
11
+ }
12
+ export const minHeightClass = (height) => MIN_HEIGHTS[height] || height