@kolkrabbi/kol-component 0.147.0 → 0.148.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.147.0",
3
+ "version": "0.148.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",
@@ -40,7 +40,21 @@ import usePrefersReducedMotion from '../hooks/usePrefersReducedMotion.js'
40
40
  * a class built at runtime is never emitted.
41
41
  *
42
42
  * @param {string} form 'grid' | 'list'
43
- * @param {string} min grid track minimum, card form (default 320px)
43
+ * @param {string} min grid track minimum, card form (default 320px) — the FLUID wall's floor
44
+ * @param {string} minCol the floor a `cols` track may not go under. DEFAULTS TO `min` (320px), so
45
+ * the count path and the fluid path share one ruled minimum and raising
46
+ * `min` raises both (ContentCollectionMinColumnWidth, kol-chess
47
+ * 2026-08-31). `cols` is a CEILING now, not a command: the wall takes up
48
+ * to N columns and drops one rather than let a track go under this.
49
+ *
50
+ * WHY NOT A HIGHER DEFAULT. The filer measured a roster row clipping on
51
+ * 7 of 10 rows at 324 and 3 of 10 at 373, which argues for ~360 — but that
52
+ * is a number about a ROW two truncated lines tall, and this floor governs
53
+ * every kind. 320 is the width this DS has already ruled as the narrowest
54
+ * acceptable track and lived with; applying it to the `cols` path is
55
+ * carrying an existing ruling across, where 360 would be a new estate-wide
56
+ * law made from one page's evidence. A wall whose content needs more says
57
+ * so: `minCol="360px"`.
44
58
  * @param {number|object} cols OPT-IN column count, grid form. A number: 1 below
45
59
  * md, N from md (1–6). A map per breakpoint
46
60
  * (`{ md: 3, xl: 4 }` — sm · md · lg · xl · 2xl): 1
@@ -59,23 +73,34 @@ import usePrefersReducedMotion from '../hooks/usePrefersReducedMotion.js'
59
73
  * @param {boolean} stagger enter animation on/off (reduced motion wins)
60
74
  */
61
75
  /* literal per rung × count — a class built at runtime is never emitted */
76
+ /* THE COUNT IS A CEILING NOW, NOT A COMMAND (ContentCollectionMinColumnWidth,
77
+ * kol-chess 2026-08-31). `cols` used to emit `grid-cols-N` and take N columns
78
+ * whatever they measured, which is how a WIDER screen came to clip MORE text:
79
+ * kol-chess's roster ran 1 column at 350 on a phone and 2 columns at 324 at
80
+ * 768 — narrower tracks on a bigger viewport, 7 rows of 10 clipping at both.
81
+ *
82
+ * So the rungs publish a VARIABLE instead of a track list, and one static
83
+ * template below turns it into "at most N, and never narrower than the floor".
84
+ * Literal class strings, as before — a class built at runtime is never emitted
85
+ * by Tailwind's scanner, and arbitrary-property utilities are no exception. */
62
86
  const COLS_AT = {
63
- sm: { 1: 'sm:grid-cols-1', 2: 'sm:grid-cols-2', 3: 'sm:grid-cols-3', 4: 'sm:grid-cols-4', 5: 'sm:grid-cols-5', 6: 'sm:grid-cols-6' },
64
- md: { 1: 'md:grid-cols-1', 2: 'md:grid-cols-2', 3: 'md:grid-cols-3', 4: 'md:grid-cols-4', 5: 'md:grid-cols-5', 6: 'md:grid-cols-6' },
65
- lg: { 1: 'lg:grid-cols-1', 2: 'lg:grid-cols-2', 3: 'lg:grid-cols-3', 4: 'lg:grid-cols-4', 5: 'lg:grid-cols-5', 6: 'lg:grid-cols-6' },
66
- xl: { 1: 'xl:grid-cols-1', 2: 'xl:grid-cols-2', 3: 'xl:grid-cols-3', 4: 'xl:grid-cols-4', 5: 'xl:grid-cols-5', 6: 'xl:grid-cols-6' },
67
- '2xl': { 1: '2xl:grid-cols-1', 2: '2xl:grid-cols-2', 3: '2xl:grid-cols-3', 4: '2xl:grid-cols-4', 5: '2xl:grid-cols-5', 6: '2xl:grid-cols-6' },
87
+ sm: { 1: 'sm:[--kol-wall-cols:1]', 2: 'sm:[--kol-wall-cols:2]', 3: 'sm:[--kol-wall-cols:3]', 4: 'sm:[--kol-wall-cols:4]', 5: 'sm:[--kol-wall-cols:5]', 6: 'sm:[--kol-wall-cols:6]' },
88
+ md: { 1: 'md:[--kol-wall-cols:1]', 2: 'md:[--kol-wall-cols:2]', 3: 'md:[--kol-wall-cols:3]', 4: 'md:[--kol-wall-cols:4]', 5: 'md:[--kol-wall-cols:5]', 6: 'md:[--kol-wall-cols:6]' },
89
+ lg: { 1: 'lg:[--kol-wall-cols:1]', 2: 'lg:[--kol-wall-cols:2]', 3: 'lg:[--kol-wall-cols:3]', 4: 'lg:[--kol-wall-cols:4]', 5: 'lg:[--kol-wall-cols:5]', 6: 'lg:[--kol-wall-cols:6]' },
90
+ xl: { 1: 'xl:[--kol-wall-cols:1]', 2: 'xl:[--kol-wall-cols:2]', 3: 'xl:[--kol-wall-cols:3]', 4: 'xl:[--kol-wall-cols:4]', 5: 'xl:[--kol-wall-cols:5]', 6: 'xl:[--kol-wall-cols:6]' },
91
+ '2xl': { 1: '2xl:[--kol-wall-cols:1]', 2: '2xl:[--kol-wall-cols:2]', 3: '2xl:[--kol-wall-cols:3]', 4: '2xl:[--kol-wall-cols:4]', 5: '2xl:[--kol-wall-cols:5]', 6: '2xl:[--kol-wall-cols:6]' },
68
92
  }
69
93
  const colsClasses = (cols) => {
70
94
  const map = typeof cols === 'number' ? { md: cols } : cols
71
95
  if (!map || typeof map !== 'object') return ''
72
96
  const rungs = Object.keys(COLS_AT).map((bp) => COLS_AT[bp][map[bp]]).filter(Boolean)
73
- return rungs.length ? ['grid-cols-1', ...rungs].join(' ') : ''
97
+ return rungs.length ? ['[--kol-wall-cols:1]', ...rungs].join(' ') : ''
74
98
  }
75
99
 
76
100
  export default function ContentCollection({
77
101
  form = 'grid',
78
102
  min = '320px',
103
+ minCol,
79
104
  cols,
80
105
  listMin,
81
106
  gap,
@@ -103,9 +128,18 @@ export default function ContentCollection({
103
128
  * wall is one, so a row inside it steps on the wall's own width */
104
129
  containerType: 'inline-size',
105
130
  display: 'grid',
106
- /* with `cols` the classes carry the tracks — an inline template would outrank them */
131
+ /* the classes no longer carry tracks — they set `--kol-wall-cols` and this
132
+ * ONE static template reads it, so the count and the floor cannot disagree */
107
133
  gridTemplateColumns: colsCls
108
- ? undefined
134
+ /* AT MOST N, AND NEVER NARROWER THAN THE FLOOR. `auto-fill` counts the
135
+ * tracks; the track size is the LARGER of the floor and an even 1/N
136
+ * share, so the wall takes N columns while they fit and drops one the
137
+ * moment a share would go under the floor. `min(100%, …)` is the guard
138
+ * that keeps a single narrow container from overflowing — without it a
139
+ * 350px phone gets one 360px track and a horizontal scrollbar.
140
+ * All of it is CSS: no measurement, no observer, and it works inside
141
+ * the container query this wall already establishes. */
142
+ ? `repeat(auto-fill, minmax(min(100%, max(${minCol ?? min}, calc((100% - (var(--kol-wall-cols, 1) - 1) * ${g}) / var(--kol-wall-cols, 1)))), 1fr))`
109
143
  : form === 'list'
110
144
  /* minmax(0, 1fr), never a bare 1fr (= minmax(auto, 1fr)): a truncated
111
145
  * nowrap line handed its min-content width to the track and a /work row