@kolkrabbi/kol-component 0.76.4 → 0.77.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.
|
|
3
|
+
"version": "0.77.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",
|
package/src/index.js
CHANGED
|
@@ -70,7 +70,7 @@ export { default as AlignmentGrid } from './molecules/AlignmentGrid.jsx'
|
|
|
70
70
|
export { default as ImageBlock } from './molecules/ImageBlock.jsx'
|
|
71
71
|
export { default as SelectionOverlay } from './atoms/SelectionOverlay.jsx'
|
|
72
72
|
export { default as VideoBlock, getEmbedUrl } from './molecules/VideoBlock.jsx'
|
|
73
|
-
export { default as CardFeatureItem } from './molecules/
|
|
73
|
+
export { default as SectionCardItem, default as CardFeatureItem } from './molecules/SectionCardItem.jsx'
|
|
74
74
|
export { default as CodeBlock } from './molecules/CodeBlock.jsx'
|
|
75
75
|
export { default as ColorInputRow } from './molecules/ColorInputRow.jsx'
|
|
76
76
|
export { default as ColorRamp } from './molecules/ColorRamp.jsx'
|
|
@@ -1,133 +1,6 @@
|
|
|
1
|
-
import { Icon } from '@kolkrabbi/kol-icons'
|
|
2
|
-
|
|
3
|
-
/* taxonomy-ok: nests only kol-icons's Icon (a package import the
|
|
4
|
-
* relative-import check can't see). */
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* `mask-image` block tinted with `currentColor` (theme-correct line-art),
|
|
11
|
-
* any other string renders as a cover-fit `<img>`, a ReactNode renders
|
|
12
|
-
* as-is, and no visual falls back to a large 96px `Icon` (the header's
|
|
13
|
-
* `icon` name). The grid child of FeaturesCardSection.
|
|
14
|
-
*
|
|
15
|
-
* Optionally the whole card is a link. `http*` / `mailto` hrefs open in a
|
|
16
|
-
* new tab; any other href renders a plain same-tab anchor with an
|
|
17
|
-
* `onNavigate(event)` seam — an SPA consumer intercepts there
|
|
18
|
-
* (preventDefault + its router) instead of this component importing one.
|
|
19
|
-
*
|
|
20
|
-
* Title and description render exactly as authored — no casing transforms;
|
|
21
|
-
* author strings in their final case at the call site.
|
|
22
|
-
*
|
|
23
|
-
* @param {ReactNode} title header heading (kol-helper-16)
|
|
24
|
-
* @param {string} icon Icon name for the header (16px); also the 96px no-visual fallback
|
|
25
|
-
* @param {string|ReactNode} visual image URL, `.svg` mask URL, or inline node
|
|
26
|
-
* @param {ReactNode} description footer line (kol-mono-12, muted)
|
|
27
|
-
* @param {string} backgroundColor card background utility class
|
|
28
|
-
* @param {string} href link target; `http*`/`mailto` → new tab, else plain same-tab anchor
|
|
29
|
-
* @param {Function} onNavigate (event) => void — click seam on the same-tab anchor (SPA intercept)
|
|
30
|
-
* @param {'auto'|'9/6'|'10/6'|'16/9'|'1/1'} imageAspectRatio aspect class on the visual middle
|
|
31
|
-
* @param {string} imagePosition `<img>` object-position
|
|
2
|
+
* @deprecated 2026-08-26 — `CardFeatureItem` is `SectionCardItem` under its old
|
|
3
|
+
* name (the section family: the composition is the card, its parts are card
|
|
4
|
+
* items). Alias kept; removed at the next major.
|
|
32
5
|
*/
|
|
33
|
-
export default
|
|
34
|
-
title,
|
|
35
|
-
icon,
|
|
36
|
-
visual,
|
|
37
|
-
description,
|
|
38
|
-
backgroundColor = 'bg-surface-primary',
|
|
39
|
-
href,
|
|
40
|
-
onNavigate,
|
|
41
|
-
imageAspectRatio = 'auto',
|
|
42
|
-
imagePosition = 'center',
|
|
43
|
-
}) {
|
|
44
|
-
const isSvgUrl = typeof visual === 'string' && visual.endsWith('.svg')
|
|
45
|
-
|
|
46
|
-
const aspectClasses = {
|
|
47
|
-
'auto': '',
|
|
48
|
-
'9/6': 'aspect-[9/6]',
|
|
49
|
-
'10/6': 'aspect-[10/6]',
|
|
50
|
-
'16/9': 'aspect-video',
|
|
51
|
-
'1/1': 'aspect-square',
|
|
52
|
-
}
|
|
53
|
-
const aspectClass = aspectClasses[imageAspectRatio] || ''
|
|
54
|
-
|
|
55
|
-
const content = (
|
|
56
|
-
<>
|
|
57
|
-
<div className="w-full flex items-center justify-between gap-2">
|
|
58
|
-
<h3 className="kol-helper-16">{title}</h3>
|
|
59
|
-
{icon && <Icon name={icon} size={16} className="shrink-0" />}
|
|
60
|
-
</div>
|
|
61
|
-
|
|
62
|
-
{/* kol-card-feature-visual: zooms 1.03 on card hover (chrome in
|
|
63
|
-
* kol-theme — CardFeatureHoverZoom 2026-08-12); all three visual
|
|
64
|
-
* forms ride the same wrapper, reduced-motion opts out. */}
|
|
65
|
-
<div className={`kol-card-feature-visual w-full flex-1 flex items-center justify-center overflow-hidden ${aspectClass}`.trim()}>
|
|
66
|
-
{visual ? (
|
|
67
|
-
typeof visual === 'string' ? (
|
|
68
|
-
isSvgUrl ? (
|
|
69
|
-
/* currentColor line-art: the SVG paints as a mask over bg-current */
|
|
70
|
-
<div
|
|
71
|
-
className="w-full h-full bg-current rounded"
|
|
72
|
-
style={{
|
|
73
|
-
maskImage: `url(${visual})`,
|
|
74
|
-
maskSize: 'contain',
|
|
75
|
-
maskRepeat: 'no-repeat',
|
|
76
|
-
maskPosition: 'center',
|
|
77
|
-
WebkitMaskImage: `url(${visual})`,
|
|
78
|
-
WebkitMaskSize: 'contain',
|
|
79
|
-
WebkitMaskRepeat: 'no-repeat',
|
|
80
|
-
WebkitMaskPosition: 'center',
|
|
81
|
-
}}
|
|
82
|
-
/>
|
|
83
|
-
) : (
|
|
84
|
-
<img
|
|
85
|
-
src={visual}
|
|
86
|
-
alt={typeof title === 'string' ? title : ''}
|
|
87
|
-
className="w-full h-full object-cover rounded"
|
|
88
|
-
style={{ objectPosition: imagePosition }}
|
|
89
|
-
/>
|
|
90
|
-
)
|
|
91
|
-
) : (
|
|
92
|
-
visual
|
|
93
|
-
)
|
|
94
|
-
) : (
|
|
95
|
-
<Icon name={icon} size={96} />
|
|
96
|
-
)}
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
<p className="kol-mono-12 text-fg-48">{description}</p>
|
|
100
|
-
</>
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
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`
|
|
104
|
-
|
|
105
|
-
if (href) {
|
|
106
|
-
const isExternal = href.startsWith('http') || href.startsWith('mailto')
|
|
107
|
-
|
|
108
|
-
if (isExternal) {
|
|
109
|
-
return (
|
|
110
|
-
<a
|
|
111
|
-
href={href}
|
|
112
|
-
className={`${baseClasses} hover:border-fg-32 transition-colors duration-300`}
|
|
113
|
-
target="_blank"
|
|
114
|
-
rel="noreferrer noopener"
|
|
115
|
-
>
|
|
116
|
-
{content}
|
|
117
|
-
</a>
|
|
118
|
-
)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return (
|
|
122
|
-
<a
|
|
123
|
-
href={href}
|
|
124
|
-
onClick={onNavigate}
|
|
125
|
-
className={`${baseClasses} hover:border-fg-24 transition-colors duration-300`}
|
|
126
|
-
>
|
|
127
|
-
{content}
|
|
128
|
-
</a>
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return <div className={baseClasses}>{content}</div>
|
|
133
|
-
}
|
|
6
|
+
export { default } from './SectionCardItem.jsx'
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Icon } from '@kolkrabbi/kol-icons'
|
|
2
|
+
|
|
3
|
+
/* taxonomy-ok: nests only kol-icons's Icon (a package import the
|
|
4
|
+
* relative-import check can't see). */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* SectionCardItem — the card SectionCards is made of. Renamed from
|
|
8
|
+
* CardFeatureItem 2026-08-26 (user: the composition is the card, so its parts
|
|
9
|
+
* are card ITEMS); `CardFeatureItem` stays as a deprecated alias.
|
|
10
|
+
*
|
|
11
|
+
* SectionCardItem — fixed-height feature card: a title + optional icon
|
|
12
|
+
* header, a flexible visual middle, and a mono description footer. The
|
|
13
|
+
* card's visual is polymorphic: an `.svg` URL string renders as a
|
|
14
|
+
* `mask-image` block tinted with `currentColor` (theme-correct line-art),
|
|
15
|
+
* any other string renders as a cover-fit `<img>`, a ReactNode renders
|
|
16
|
+
* as-is, and no visual falls back to a large 96px `Icon` (the header's
|
|
17
|
+
* `icon` name). The grid child of FeaturesCardSection.
|
|
18
|
+
*
|
|
19
|
+
* Optionally the whole card is a link. `http*` / `mailto` hrefs open in a
|
|
20
|
+
* new tab; any other href renders a plain same-tab anchor with an
|
|
21
|
+
* `onNavigate(event)` seam — an SPA consumer intercepts there
|
|
22
|
+
* (preventDefault + its router) instead of this component importing one.
|
|
23
|
+
*
|
|
24
|
+
* Title and description render exactly as authored — no casing transforms;
|
|
25
|
+
* author strings in their final case at the call site.
|
|
26
|
+
*
|
|
27
|
+
* @param {ReactNode} title header heading (kol-helper-16)
|
|
28
|
+
* @param {string} icon Icon name for the header (16px); also the 96px no-visual fallback
|
|
29
|
+
* @param {string|ReactNode} visual image URL, `.svg` mask URL, or inline node
|
|
30
|
+
* @param {ReactNode} description footer line (kol-mono-12, muted)
|
|
31
|
+
* @param {string} backgroundColor card background utility class
|
|
32
|
+
* @param {string} href link target; `http*`/`mailto` → new tab, else plain same-tab anchor
|
|
33
|
+
* @param {Function} onNavigate (event) => void — click seam on the same-tab anchor (SPA intercept)
|
|
34
|
+
* @param {'auto'|'9/6'|'10/6'|'16/9'|'1/1'} imageAspectRatio aspect class on the visual middle
|
|
35
|
+
* @param {string} imagePosition `<img>` object-position
|
|
36
|
+
*/
|
|
37
|
+
export default function SectionCardItem({
|
|
38
|
+
title,
|
|
39
|
+
icon,
|
|
40
|
+
visual,
|
|
41
|
+
description,
|
|
42
|
+
backgroundColor = 'bg-surface-primary',
|
|
43
|
+
href,
|
|
44
|
+
onNavigate,
|
|
45
|
+
imageAspectRatio = 'auto',
|
|
46
|
+
imagePosition = 'center',
|
|
47
|
+
}) {
|
|
48
|
+
const isSvgUrl = typeof visual === 'string' && visual.endsWith('.svg')
|
|
49
|
+
|
|
50
|
+
const aspectClasses = {
|
|
51
|
+
'auto': '',
|
|
52
|
+
'9/6': 'aspect-[9/6]',
|
|
53
|
+
'10/6': 'aspect-[10/6]',
|
|
54
|
+
'16/9': 'aspect-video',
|
|
55
|
+
'1/1': 'aspect-square',
|
|
56
|
+
}
|
|
57
|
+
const aspectClass = aspectClasses[imageAspectRatio] || ''
|
|
58
|
+
|
|
59
|
+
const content = (
|
|
60
|
+
<>
|
|
61
|
+
<div className="w-full flex items-center justify-between gap-2">
|
|
62
|
+
<h3 className="kol-helper-16">{title}</h3>
|
|
63
|
+
{icon && <Icon name={icon} size={16} className="shrink-0" />}
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
{/* kol-card-feature-visual: zooms 1.03 on card hover (chrome in
|
|
67
|
+
* kol-theme — CardFeatureHoverZoom 2026-08-12); all three visual
|
|
68
|
+
* forms ride the same wrapper, reduced-motion opts out. */}
|
|
69
|
+
<div className={`kol-card-feature-visual w-full flex-1 flex items-center justify-center overflow-hidden ${aspectClass}`.trim()}>
|
|
70
|
+
{visual ? (
|
|
71
|
+
typeof visual === 'string' ? (
|
|
72
|
+
isSvgUrl ? (
|
|
73
|
+
/* currentColor line-art: the SVG paints as a mask over bg-current */
|
|
74
|
+
<div
|
|
75
|
+
className="w-full h-full bg-current rounded"
|
|
76
|
+
style={{
|
|
77
|
+
maskImage: `url(${visual})`,
|
|
78
|
+
maskSize: 'contain',
|
|
79
|
+
maskRepeat: 'no-repeat',
|
|
80
|
+
maskPosition: 'center',
|
|
81
|
+
WebkitMaskImage: `url(${visual})`,
|
|
82
|
+
WebkitMaskSize: 'contain',
|
|
83
|
+
WebkitMaskRepeat: 'no-repeat',
|
|
84
|
+
WebkitMaskPosition: 'center',
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
) : (
|
|
88
|
+
<img
|
|
89
|
+
src={visual}
|
|
90
|
+
alt={typeof title === 'string' ? title : ''}
|
|
91
|
+
className="w-full h-full object-cover rounded"
|
|
92
|
+
style={{ objectPosition: imagePosition }}
|
|
93
|
+
/>
|
|
94
|
+
)
|
|
95
|
+
) : (
|
|
96
|
+
visual
|
|
97
|
+
)
|
|
98
|
+
) : (
|
|
99
|
+
<Icon name={icon} size={96} />
|
|
100
|
+
)}
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<p className="kol-mono-12 text-fg-48">{description}</p>
|
|
104
|
+
</>
|
|
105
|
+
)
|
|
106
|
+
|
|
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`
|
|
108
|
+
|
|
109
|
+
if (href) {
|
|
110
|
+
const isExternal = href.startsWith('http') || href.startsWith('mailto')
|
|
111
|
+
|
|
112
|
+
if (isExternal) {
|
|
113
|
+
return (
|
|
114
|
+
<a
|
|
115
|
+
href={href}
|
|
116
|
+
className={`${baseClasses} hover:border-fg-32 transition-colors duration-300`}
|
|
117
|
+
target="_blank"
|
|
118
|
+
rel="noreferrer noopener"
|
|
119
|
+
>
|
|
120
|
+
{content}
|
|
121
|
+
</a>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<a
|
|
127
|
+
href={href}
|
|
128
|
+
onClick={onNavigate}
|
|
129
|
+
className={`${baseClasses} hover:border-fg-24 transition-colors duration-300`}
|
|
130
|
+
>
|
|
131
|
+
{content}
|
|
132
|
+
</a>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return <div className={baseClasses}>{content}</div>
|
|
137
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import SectionCardItem from '../molecules/SectionCardItem.jsx'
|
|
2
2
|
import SectionText, { HEADLINE_ROLE } from '../molecules/SectionText.jsx'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* SectionCards — the "N-up feature cards" band: a `SectionText` header over a
|
|
6
|
-
* responsive row of `
|
|
6
|
+
* responsive row of `SectionCardItem` cards, capped by an optional centred
|
|
7
7
|
* action row. `FeaturesCardSection` is this component under its old name and
|
|
8
8
|
* prop names (`headerLabel` → `headline`, `headerDescription` → `body`,
|
|
9
9
|
* `ctas` → `actions`; alias kept).
|
|
@@ -57,7 +57,7 @@ export default function SectionCards({
|
|
|
57
57
|
{features.length > 0 && (
|
|
58
58
|
<div className={cardsWrapperClassName}>
|
|
59
59
|
{features.map((feature, index) => (
|
|
60
|
-
<
|
|
60
|
+
<SectionCardItem
|
|
61
61
|
key={index}
|
|
62
62
|
title={feature.title}
|
|
63
63
|
icon={feature.icon}
|