@kolkrabbi/kol-component 0.87.0 → 0.89.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.87.0",
3
+ "version": "0.89.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",
@@ -65,8 +65,8 @@ const HOVER = {
65
65
  catalog: 'var(--kol-surface-tertiary)',
66
66
  print: 'var(--kol-oq-04)',
67
67
  /* article and work take NO surface hover, and that is a decision not a gap:
68
- * article has no surface of its own (its media frame steps its border
69
- * instead), and work's whole hover IS the drawer rising. A second wash under
68
+ * article has no surface of its own (its media frame, when on, steps its
69
+ * border instead), and work's whole hover IS the drawer rising. A second wash under
70
70
  * either would be two answers to one question. */
71
71
  article: null,
72
72
  work: null,
@@ -75,16 +75,18 @@ const HOVER = {
75
75
 
76
76
  /* per-variant media treatment. `ring` sits OVER the artwork, `frame` UNDER it —
77
77
  * see ContentMedia. print rings because an A4 print on a light page has no edge
78
- * of its own; article frames because its media is a 16/9 thumbnail that rarely
79
- * fills its box. */
78
+ * of its own; article CAN frame (opt-in since 0.88.0) because its media is a
79
+ * 16/9 thumbnail that rarely fills its box. */
80
80
  const MEDIA = {
81
81
  /* zoom is for IMAGE-LED cards — where the artwork is the content and the
82
82
  * card is a frame around it. A catalog tile whose preview is a diagram, or
83
83
  * a default file card whose thumb is a 48px chip, gets nothing from it. */
84
84
  print: { ring: true, zoom: true },
85
85
  work: { zoom: true },
86
- /* ListingCard's card media steps its border on hover — fg-08 fg-16 */
87
- article: { frame: true, borderHover: true, zoom: true },
86
+ /* frame OFF by default (ListingCardThumbBorder, user 2026-08-27: "I hate
87
+ * border remove border"): the article thumb's hairline is opt-in —
88
+ * `frame` turns it on, and with it the fg-08 → fg-16 hover step */
89
+ article: { frame: false, borderHover: true, zoom: true },
88
90
  }
89
91
 
90
92
  export default function ContentCard({
@@ -41,8 +41,13 @@ import usePrefersReducedMotion from '../hooks/usePrefersReducedMotion.js'
41
41
  *
42
42
  * @param {string} form 'grid' | 'list'
43
43
  * @param {string} min grid track minimum, card form (default 320px)
44
- * @param {number} cols OPT-IN column count, grid form: 1 below md, N from
45
- * md (2–6). Wins over `min`; the list form ignores it
44
+ * @param {number|object} cols OPT-IN column count, grid form. A number: 1 below
45
+ * md, N from md (1–6). A map per breakpoint
46
+ * (`{ md: 3, xl: 4 }` — sm · md · lg · xl · 2xl): 1
47
+ * below the first rung, each rung's count from there
48
+ * (ContentCollectionColsResponsive, 2026-08-27 —
49
+ * user: "can we make it 4 in the biggest breakpoint?").
50
+ * Wins over `min`; the list form ignores it
46
51
  * @param {string} listMin OPT-IN: makes the list multi-column too, for a dense
47
52
  * file-browser cut. Unset = one full-width column
48
53
  * @param {number} gap px between items. UNSET reads the token for the
@@ -53,13 +58,19 @@ import usePrefersReducedMotion from '../hooks/usePrefersReducedMotion.js'
53
58
  * nine call sites. Pass a number only to differ
54
59
  * @param {boolean} stagger enter animation on/off (reduced motion wins)
55
60
  */
56
- const COLS = {
57
- 1: 'grid-cols-1',
58
- 2: 'grid-cols-1 md:grid-cols-2',
59
- 3: 'grid-cols-1 md:grid-cols-3',
60
- 4: 'grid-cols-1 md:grid-cols-4',
61
- 5: 'grid-cols-1 md:grid-cols-5',
62
- 6: 'grid-cols-1 md:grid-cols-6',
61
+ /* literal per rung × count — a class built at runtime is never emitted */
62
+ 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' },
68
+ }
69
+ const colsClasses = (cols) => {
70
+ const map = typeof cols === 'number' ? { md: cols } : cols
71
+ if (!map || typeof map !== 'object') return ''
72
+ const rungs = Object.keys(COLS_AT).map((bp) => COLS_AT[bp][map[bp]]).filter(Boolean)
73
+ return rungs.length ? ['grid-cols-1', ...rungs].join(' ') : ''
63
74
  }
64
75
 
65
76
  export default function ContentCollection({
@@ -81,7 +92,7 @@ export default function ContentCollection({
81
92
  ? `${gap}px`
82
93
  : `var(--kol-gap-wall-${form === 'list' ? 'list' : 'grid'})`
83
94
 
84
- const colsCls = form !== 'list' && COLS[cols] ? COLS[cols] : ''
95
+ const colsCls = form !== 'list' ? colsClasses(cols) : ''
85
96
  return (
86
97
  <ul
87
98
  key={form}