@kolkrabbi/kol-component 0.67.0 → 0.68.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.68.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",
|
|
@@ -57,6 +57,10 @@ const HOVER = {
|
|
|
57
57
|
default: 'var(--kol-oq-04)',
|
|
58
58
|
catalog: 'var(--kol-surface-tertiary)',
|
|
59
59
|
print: 'var(--kol-oq-04)',
|
|
60
|
+
/* article and work take NO surface hover, and that is a decision not a gap:
|
|
61
|
+
* article has no surface of its own (its media frame steps its border
|
|
62
|
+
* instead), and work's whole hover IS the drawer rising. A second wash under
|
|
63
|
+
* either would be two answers to one question. */
|
|
60
64
|
article: null,
|
|
61
65
|
work: null,
|
|
62
66
|
typeface: 'var(--kol-surface-inverse)',
|
|
@@ -31,10 +31,14 @@ const BOX = {
|
|
|
31
31
|
catalog: { thumb: 0, ratio: '1 / 1', pad: `0 ${S3}`, gap: S3, frame: 'var(--kol-fg-04)', bg: 'var(--kol-surface-tertiary)', minH: 36, align: 'items-center', hover: 'var(--kol-oq-04)' },
|
|
32
32
|
print: { thumb: 0, ratio: '1 / 1', pad: `0 ${S3}`, gap: S3, frame: 'var(--kol-fg-04)', bg: 'var(--kol-surface-tertiary)', minH: 36, align: 'items-center', hover: 'var(--kol-oq-04)' },
|
|
33
33
|
/* ListingCard's row thumb is `bg-fg-12` bare — a tint, no border. */
|
|
34
|
-
article
|
|
34
|
+
/* article had NO hover of any kind — the only row in the family you could
|
|
35
|
+
* point at and get nothing back. It has no surface of its own, so it takes
|
|
36
|
+
* the lightest step there is. Its thumb zooms: on an article row the image
|
|
37
|
+
* IS the subject. */
|
|
38
|
+
article: { thumb: 120, ratio: '1 / 1', pad: '0', gap: S6, align: 'items-start', thumbBg: 'var(--kol-fg-12)', hover: 'var(--kol-oq-02)', thumbZoom: true },
|
|
35
39
|
/* work and typeface step UP at md — the shipped rows both do, and a work row
|
|
36
40
|
* at a fixed 96 cannot hold the display-03 line it was ruled to carry. */
|
|
37
|
-
work: { thumb: 64, thumbMd: 112, ratio: '1 / 1', pad: S4, padMd: S6, gap: S4, gapMd: S6, frame: 'transparent', frameHover: 'var(--kol-fg-16)', bg: 'var(--kol-surface-secondary)', minH: 96, minHMd: 160, align: 'items-stretch', thumbRadius: 'var(--kol-radius-xs)', thumbBorder: true },
|
|
41
|
+
work: { thumb: 64, thumbMd: 112, ratio: '1 / 1', pad: S4, padMd: S6, gap: S4, gapMd: S6, frame: 'transparent', frameHover: 'var(--kol-fg-16)', bg: 'var(--kol-surface-secondary)', minH: 96, minHMd: 160, align: 'items-stretch', thumbRadius: 'var(--kol-radius-xs)', thumbBorder: true, thumbZoom: true },
|
|
38
42
|
/* typeface's row is a COLUMN, not a line: a header (name/styles left,
|
|
39
43
|
* classification/year right) with a full-width specimen band under it. The
|
|
40
44
|
* shipped item is `flex-col gap-6`, and forcing it into the horizontal
|
|
@@ -100,17 +104,22 @@ export default function ContentRow({
|
|
|
100
104
|
) : (
|
|
101
105
|
<>
|
|
102
106
|
{thumbPx > 0 && (
|
|
103
|
-
/* THE THUMB FILLS THE ROW'S HEIGHT
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
107
|
+
/* THE THUMB FILLS THE ROW'S HEIGHT (user ruling 2026-08-15).
|
|
108
|
+
*
|
|
109
|
+
* WIDTH stays pinned to `--kol-row-thumb` — that is what stops it
|
|
110
|
+
* running away. Only the height stretches, and `ratio={null}` puts
|
|
111
|
+
* ContentMedia on `h-full` so the image object-covers into whatever
|
|
112
|
+
* height the row is. An earlier cut freed the width instead and let
|
|
113
|
+
* `aspect-ratio` fall back to the image's intrinsic size, which is how
|
|
114
|
+
* one card ate the page. */
|
|
108
115
|
<div className="kol-row-thumb shrink-0 self-stretch">
|
|
109
116
|
<ContentMedia
|
|
110
|
-
|
|
111
|
-
ratio={ratio ?? box.ratio}
|
|
117
|
+
ratio={null}
|
|
112
118
|
border={box.thumbBorder ?? false}
|
|
113
119
|
bg={box.thumbBg}
|
|
120
|
+
/* zoom where the thumb IS the subject (article · work), never on a
|
|
121
|
+
* 48px file chip or a between-header with no media at all */
|
|
122
|
+
zoom={box.thumbZoom ?? false}
|
|
114
123
|
className={box.thumbRadius ? 'rounded-[var(--kol-radius-xs)]' : ''}
|
|
115
124
|
>
|
|
116
125
|
{media}
|
|
@@ -27,6 +27,23 @@
|
|
|
27
27
|
* dateClass, sizeClass, metaClass — full class overrides
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
+
/* THE ROW INK LADDER — three rungs, and only three (2026-08-15).
|
|
31
|
+
*
|
|
32
|
+
* A work row was rendering its small title, its big description AND its type
|
|
33
|
+
* label all at full emphasis, with only the year stepped down: four fields
|
|
34
|
+
* wearing two values, so nothing led. The ladder is the same one the header
|
|
35
|
+
* strips already use, which is why it reads as one system rather than a
|
|
36
|
+
* per-variant guess:
|
|
37
|
+
*
|
|
38
|
+
* emphasis THE THING YOU CAME FOR — one per row, never two
|
|
39
|
+
* body what identifies it — the title, the type
|
|
40
|
+
* meta metadata — the year, the count, the file size
|
|
41
|
+
*
|
|
42
|
+
* The ROLES, not raw `fg-*` opacities and not raw `oq-*` rungs. A role is the
|
|
43
|
+
* only thing that survives a theme flip and a consumer's own palette; the three
|
|
44
|
+
* of them are exactly the three the family needs, which is why there are three
|
|
45
|
+
* and not fourteen. */
|
|
46
|
+
|
|
30
47
|
/* [variant][form][slot] → 'type-class ink-role', verbatim from the ruled table */
|
|
31
48
|
const RAMP = {
|
|
32
49
|
default: {
|
|
@@ -38,28 +55,37 @@ const RAMP = {
|
|
|
38
55
|
row: { title: 'kol-mono-12 text-emphasis', detail: 'kol-mono-10 text-meta' },
|
|
39
56
|
},
|
|
40
57
|
print: {
|
|
41
|
-
card: { title: 'kol-mono-14 text-
|
|
42
|
-
|
|
58
|
+
card: { title: 'kol-mono-14 text-emphasis', detail: 'kol-mono-10 text-meta' },
|
|
59
|
+
/* mono-12 + emphasis, matching catalog — print's row IS catalog's row, so
|
|
60
|
+
* the two must not disagree about their own type. */
|
|
61
|
+
row: { title: 'kol-mono-12 text-emphasis', detail: 'kol-mono-10 text-meta' },
|
|
43
62
|
},
|
|
44
63
|
article: {
|
|
45
|
-
card: { kicker: 'kol-mono-12 text-
|
|
46
|
-
row: { kicker: 'kol-mono-12 text-
|
|
64
|
+
card: { kicker: 'kol-mono-12 text-meta', title: 'kol-sans-heading-03 text-emphasis', body: 'kol-mono-14 text-body', date: 'kol-mono-12 text-meta', size: 'kol-mono-12 text-meta', tags: 'flex flex-wrap gap-2' },
|
|
65
|
+
row: { kicker: 'kol-mono-12 text-meta', title: 'kol-sans-heading-04 text-emphasis', body: 'kol-mono-12 text-body', date: 'kol-mono-12 text-meta', size: 'kol-mono-12 text-meta', tags: 'flex flex-wrap gap-2' },
|
|
47
66
|
},
|
|
48
67
|
work: {
|
|
49
68
|
/* INVERSE ink — the card's plate is the drawer, `surface-inverse`. Leaving
|
|
50
69
|
* these on `text-emphasis` painted light type on a light plate and the
|
|
51
70
|
* caption disappeared. */
|
|
52
|
-
|
|
71
|
+
/* the drawer is an INVERSE surface, so the roles do not apply — but the
|
|
72
|
+
* ladder does: one full ink, the rest stepped. */
|
|
73
|
+
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' },
|
|
53
74
|
/* verbatim from WorkListItem: title `kol-mono-14` truncated · type
|
|
54
75
|
* `kol-mono-12 md:kol-mono-14` at FULL ink, no opacity step · year
|
|
55
76
|
* `kol-mono-12 text-fg-64` · description `kol-sans-heading-03 text-auto`.
|
|
56
77
|
* Title and type are the pair — same rung, same full ink; only the year
|
|
57
78
|
* steps down. */
|
|
58
|
-
row: { title: 'kol-mono-12 text-
|
|
79
|
+
row: { title: 'kol-mono-12 text-body uppercase truncate', body: 'kol-sans-heading-03 leading-tight text-emphasis truncate', meta: 'kol-mono-12 text-body', date: 'kol-mono-12 text-meta', tags: 'flex flex-wrap items-center gap-1.5' },
|
|
59
80
|
},
|
|
60
81
|
typeface: {
|
|
61
|
-
|
|
62
|
-
|
|
82
|
+
/* same as the row: the SPECIMEN carries the emphasis on a typeface card,
|
|
83
|
+
* so the name steps down. The glyph is what you came to look at. */
|
|
84
|
+
card: { title: 'kol-mono-16 text-body', body: 'kol-mono-12 text-meta', date: 'kol-mono-12 text-meta' },
|
|
85
|
+
/* the name steps DOWN to `body`: on a typeface row the SPECIMEN is the
|
|
86
|
+
* thing you came for and it carries the one emphasis. Two full inks in one
|
|
87
|
+
* block is the ladder broken, and the measurement caught it. */
|
|
88
|
+
row: { title: 'kol-mono-14 uppercase text-body', body: 'kol-mono-12 text-meta', detail: 'kol-mono-14 text-body', date: 'kol-mono-12 text-meta' },
|
|
63
89
|
},
|
|
64
90
|
}
|
|
65
91
|
|
|
@@ -162,17 +188,18 @@ export default function ContentText({
|
|
|
162
188
|
* needs exactly that: classification over year at the right edge, which a
|
|
163
189
|
* flat slot list cannot express and which was previously collapsed into one
|
|
164
190
|
* `date` slot that lost a value. */
|
|
165
|
-
const render = (entry, i) => {
|
|
191
|
+
const render = (entry, i, trailing = false) => {
|
|
166
192
|
if (typeof entry === 'string') return line(entry)
|
|
167
193
|
const [kind, ...slots] = entry
|
|
168
|
-
const parts = slots.map((s, j) => render(s, j)).filter(Boolean)
|
|
194
|
+
const parts = slots.map((s, j) => render(s, j, kind === 'between' && j === slots.length - 1)).filter(Boolean)
|
|
169
195
|
if (!parts.length) return null
|
|
170
196
|
if (kind === 'stack') {
|
|
171
|
-
/*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
197
|
+
/* A TRAILING column right-aligns (user ruling 2026-08-15) — it sits at
|
|
198
|
+
* the row's right edge, so its own right edge is the one that lines up
|
|
199
|
+
* down the list. `items-end` only applies where the stack is trailing;
|
|
200
|
+
* a leading stack still reads from the left. */
|
|
174
201
|
return (
|
|
175
|
-
<div key={`stack-${i}`} className=
|
|
202
|
+
<div key={`stack-${i}`} className={`flex w-full min-w-0 flex-col ${trailing ? 'items-end text-right' : 'items-start text-left'}`} style={{ gap: STACK[variant] ?? 'var(--kol-spacing-1)' }}>
|
|
176
203
|
{parts}
|
|
177
204
|
</div>
|
|
178
205
|
)
|
|
@@ -193,7 +220,7 @@ export default function ContentText({
|
|
|
193
220
|
* empty space sat between the two columns. */}
|
|
194
221
|
{kind === 'between' && parts.length > 1
|
|
195
222
|
? parts.map((p, j) => (
|
|
196
|
-
<div key={j} className={j === 0 ? 'min-w-0 flex-1' : 'shrink-0 text-
|
|
223
|
+
<div key={j} className={j === 0 ? 'min-w-0 flex-1' : 'shrink-0 text-right'}>{p}</div>
|
|
197
224
|
))
|
|
198
225
|
: parts}
|
|
199
226
|
</div>
|