@kolkrabbi/kol-component 0.110.0 → 0.112.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.110.0",
3
+ "version": "0.112.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
@@ -137,7 +137,7 @@ export { default as LoaderOverlay } from './utilities/LoaderOverlay.jsx'
137
137
  export { default as MediaLibrary, MediaLibraryProvider, useMediaLibrary, MediaPicker, MediaBrowser } from './organisms/MediaLibrary.jsx'
138
138
  export { default as MediaTileGallery } from './organisms/MediaTileGallery.jsx'
139
139
  export { default as MediaViewer } from './organisms/MediaViewer.jsx'
140
- export { default as SettingsPanel, SettingsSection, SettingsRow, SettingsSwitch, SettingsChoice, SettingsMulti, SettingsChipRow, SettingsFooter } from './organisms/SettingsPanel.jsx'
140
+ export { default as SettingsPanel, LabeledControlSection, SettingsRow, SettingsSwitch, SettingsChoice, SettingsMulti, SettingsChipRow, SettingsFooter } from './organisms/SettingsPanel.jsx'
141
141
  export { default as SectionNewsletter } from './organisms/SectionNewsletter.jsx'
142
142
  export { default as NewsletterBand } from './organisms/NewsletterBand.jsx'
143
143
  export { default as ColumnBrowser } from './organisms/ColumnBrowser.jsx'
@@ -78,8 +78,12 @@ const FIT = {
78
78
  /* a consumer WRAPPER div fills the frame as an img/video does (ColumnBrowser round, 2026-08-27 — kol-r2b2's wrapped thumb fell back to the image's intrinsic size) */
79
79
  cover: '[&>img]:h-full [&>img]:w-full [&>img]:object-cover [&>video]:h-full [&>video]:w-full [&>video]:object-cover [&>div]:h-full [&>div]:w-full',
80
80
  /* GridCard's previewFit, verbatim: `img { max-width: none; transform: scale(.5 | .3); transform-origin: top left }` */
81
- natural: '[&>img]:max-w-none [&>img]:scale-50 [&>img]:origin-top-left',
82
- compact: '[&>img]:max-w-none [&>img]:scale-[.3] [&>img]:origin-top-left',
81
+ /* the ANCHOR is the consumer's (ContentMediaFocusBinding, kol-monitor 2026-08-27 — user: "make it
82
+ * so that the focus can be set per repo"): `--kol-media-focus`, bound once on a consumer's :root,
83
+ * pins the fit AND the hover zoom (kol-theme `.kol-media-zoom`, same token) — the two share
84
+ * `transform-origin`. Unset = today's values: top left here, center on the zoom. */
85
+ natural: '[&>img]:max-w-none [&>img]:scale-50 [&>img]:origin-[var(--kol-media-focus,top_left)]',
86
+ compact: '[&>img]:max-w-none [&>img]:scale-[.3] [&>img]:origin-[var(--kol-media-focus,top_left)]',
83
87
  }
84
88
 
85
89
  /* the fade: the child <img> is cloned with loading="lazy" and `kol-media-fade`,
@@ -42,8 +42,10 @@ import IconFrame from '../atoms/IconFrame.jsx'
42
42
  * @param {boolean} props.titleUppercase — cases the title (default false)
43
43
  * @param {string} props.labelClassName — filter-group label type/ink (default `kol-eyebrow text-fg-96`)
44
44
  * @param {boolean} props.labelUppercase — cases the group label (default true)
45
- * groups: `{ label, key, values, stack?, className?, wrapClassName? }` — THE FIRST GROUP HUGS ITS CHIPS, EVERY
46
- * GROUP AFTER IT FLOWS (user law 2026-08-27, by position, never by chip count); `stack` is the explicit
45
+ * groups: `{ label, key, values, stack?, className?, wrapClassName? }` — THE FIRST GROUP IS ONE CATALOG COLUMN
46
+ * WIDE (`(row 120px) / 6`, the `1fr` of `repeat(6, 1fr)` gap 24 a fraction, never a px; `.kol-filters-first`,
47
+ * kol-theme ≥0.73.0), EVERY GROUP AFTER IT FLOWS (user law 2026-08-27, by position, never by chip count —
48
+ * the hug's width overruled the same day: "nope not hug, fix a size"); `stack` is the explicit
47
49
  * override; `className` / `wrapClassName` are per-group seams
48
50
  * @param {string} props.tagVariant — filter chip variant ('primary' grey fill)
49
51
  * @param {string} props.tagSize — filter chip size
@@ -247,16 +249,22 @@ const ContentFilters = ({
247
249
  ) : null
248
250
 
249
251
  const renderFilterGroup = (group, index = 0) => {
250
- /* THE LAW (user ruling 2026-08-27, said "for the 10th time" — ContentFiltersFirstGroupHugs):
251
- * THE FIRST FILTER GROUP HUGS ITS CHIPS IT IS NARROW. EVERY GROUP AFTER IT
252
- * FLOWS ACROSS THE REST OF THE ROW. By POSITION, never by chip count. The
253
- * reference is kol-website /prints: CATEGORY hugs at the left, YEAR flows
254
- * beside it. Not equal columns (0.104.1, a misread reverted), not "short
255
- * groups stack" (0.101, one page's ruling reverted). `stack` stays the
256
- * explicit override; kol-fxr pins `stack: i === 0`. */
252
+ /* THE LAW (user ruling 2026-08-27, said "for the 10th time" — ContentFiltersFirstGroupHugs,
253
+ * its WIDTH overruled the same dayContentFiltersFirstGroupFixedWidth, kol-monitor: "nope
254
+ * not hug, fix a size if columns, maybe just use one?"): THE FIRST FILTER GROUP IS ONE
255
+ * CATALOG COLUMN WIDE — `.kol-filters-first` (kol-theme): `(100cqw 120px) / 6`, the `1fr`
256
+ * of the catalog's `repeat(6, 1fr)` gap 24, measured on the header row as a container so
257
+ * the count/strip beside the groups never narrows it. It sits over the first card; EVERY
258
+ * GROUP AFTER IT FLOWS across the rest of the row, starting over the second. By POSITION,
259
+ * never by chip count. A page without a 6-column catalog gets the same fraction of its row.
260
+ * Not a hug (0.104.3 — the column was 78px on one surface and 92 on the next), not equal
261
+ * columns (0.104.1), not "short groups stack" (0.101). `stack` stays the explicit override
262
+ * (`stack: false` on the first makes it flow); `group.className` still wins on width — the
263
+ * rule sits in the components layer. */
257
264
  const stacked = group.stack ?? index === 0
265
+ const first = index === 0 && stacked
258
266
  return (
259
- <div key={group.key} className={`flex flex-col gap-3 ${stacked ? 'shrink-0' : 'min-w-0 flex-1'} ${group.className ?? ''}`.trim()}>
267
+ <div key={group.key} className={`flex flex-col gap-3 ${first ? 'kol-filters-first' : stacked ? 'shrink-0' : 'min-w-0 flex-1'} ${group.className ?? ''}`.trim()}>
260
268
  {/* THE CATEGORY LABEL IS THE ACTIVE INK — `kol-helper-12` at `text-fg-96`,
261
269
  * the same full opacity a SELECTED layout item wears (user ruling
262
270
  * 2026-08-15: "TAGS and other categories are ACTIVE state full opacity").
@@ -445,7 +453,7 @@ const ContentFilters = ({
445
453
  * the whole row on `isExpanded` hid the strip until you opened filters,
446
454
  * which is not a state anyone would guess at. */}
447
455
  {(isExpanded || (layoutPlacement === 'below' && layoutStrip) || leadingActions || belowActions) && (
448
- <div className="flex items-start justify-between gap-16">
456
+ <div className="kol-filters-row flex items-start justify-between gap-16">
449
457
  <div className="flex min-w-0 flex-1 items-start gap-16">
450
458
  {leadingActions}
451
459
  {isExpanded && filterGroups.map((group, i) => renderFilterGroup(group, i))}
@@ -130,11 +130,15 @@ export function SettingsSwitch({ on = false, onChange, disabled = false, disable
130
130
  )
131
131
  }
132
132
 
133
- /** SettingsSection — a section of the panel (SettingsPanelApproved, 2026-08-27):
134
- * the EYEBROW (`kol-eyebrow text-fg-80`) standing apart from the rows (gap-3),
135
- * the rows in their own stack — `rowGap` 1 for switch rows (24 tall already),
136
- * 2 for dropdown rows. `divided` = a hairline above (between sections). */
137
- export function SettingsSection({ label, divided = false, rowGap = 2, children, className = '' }) {
133
+ /** LabeledControlSection — a section of LabeledControls: the EYEBROW
134
+ * (`kol-eyebrow text-fg-80`) standing apart from the rows (gap-3), the rows in
135
+ * their own stack — `rowGap` 1 for switch rows (24 tall already), 2 for
136
+ * dropdown rows. `divided` = a hairline above (between sections).
137
+ * Was `SettingsSection` until 2026-08-27 (LabeledControlSection, kol-r2b2
138
+ * user ruling: it named the place it was first used, not what it is; kol-fxr
139
+ * took it for a params rail the same day). RENAMED, NO ALIAS — the render is
140
+ * the 0.104.0 locked composition, untouched. */
141
+ export function LabeledControlSection({ label, divided = false, rowGap = 2, children, className = '' }) {
138
142
  return (
139
143
  <div className={`flex flex-col gap-3 ${divided ? 'kol-section--divided' : ''} ${className}`.replace(/\s+/g, ' ').trim()}>
140
144
  {label && <p className="kol-eyebrow text-fg-80">{label}</p>}