@kolkrabbi/kol-component 0.238.0 → 0.239.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.239.0",
|
|
4
4
|
"description": "The core component library",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"embla-carousel-react": "^8.6.0",
|
|
27
27
|
"pdfjs-dist": "^6.3.289",
|
|
28
28
|
"react-syntax-highlighter": "^16.1.1",
|
|
29
|
-
"@kolkrabbi/kol-
|
|
30
|
-
"@kolkrabbi/kol-
|
|
29
|
+
"@kolkrabbi/kol-search": "^0.3.0",
|
|
30
|
+
"@kolkrabbi/kol-markdown": "^0.1.3"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@kolkrabbi/kol-icons": ">=0.22.0",
|
package/src/atoms/Button.jsx
CHANGED
|
@@ -12,7 +12,7 @@ import { glyphSize } from '../hooks/glyphLadders.js'
|
|
|
12
12
|
*
|
|
13
13
|
* @param {Object} props
|
|
14
14
|
* @param {ReactNode} props.children - Button content
|
|
15
|
-
* @param {'accent'|'danger'|'nav'} props.variant - The intent; the GROUND is `tone`. `primary · secondary · outline · ghost · grey · control` are deprecated aliases of tones (2026-10-01, on the retirement ledger): pass `tone` — `secondary` is `tone="inverted"`, `control` is `tone="ghost"`, the rest keep their name. Visual variant. UNSET (the default) inherits the tone of the nearest `kol-tone-*` wrapper, else renders primary; the five ground variants are aliases of `tone`. `danger` is the destructive treatment (--ui-error fill); `nav` is the chrome rung — transparent, oq-64 ink, one step brighter than `ghost`; `control` is an alias for `ghost` (legacy call-sites).
|
|
15
|
+
* @param {'accent'|'danger'|'nav'|'tab'} props.variant - The intent; the GROUND is `tone`. `tab` is the tab chip (2026-10-02): quiet text at rest, a soft filled chip while `pressed`. `primary · secondary · outline · ghost · grey · control` are deprecated aliases of tones (2026-10-01, on the retirement ledger): pass `tone` — `secondary` is `tone="inverted"`, `control` is `tone="ghost"`, the rest keep their name. Visual variant. UNSET (the default) inherits the tone of the nearest `kol-tone-*` wrapper, else renders primary; the five ground variants are aliases of `tone`. `danger` is the destructive treatment (--ui-error fill); `nav` is the chrome rung — transparent, oq-64 ink, one step brighter than `ghost`; `control` is an alias for `ghost` (legacy call-sites).
|
|
16
16
|
* @param {'xs'|'sm'|'md'|'lg'} props.size - Button size (default: 'md'); xs is the panel rung (ControlsXsRung, 2026-09-01) — kol-mono-8 in a 22px shell (20 icon-only), opt-in by prop
|
|
17
17
|
* @param {string} props.iconLeft - Icon name to display on the left
|
|
18
18
|
* @param {string} props.iconRight - Icon name to display on the right
|
|
@@ -93,7 +93,7 @@ const Button = ({
|
|
|
93
93
|
warned.add(variant)
|
|
94
94
|
console.warn(`Button: variant="${variant}" is deprecated — use tone="${VARIANT_TONE[variant]}"`)
|
|
95
95
|
}
|
|
96
|
-
const KNOWN = ['primary', 'secondary', 'accent', 'outline', 'ghost', 'nav', 'danger', 'grey']
|
|
96
|
+
const KNOWN = ['primary', 'secondary', 'accent', 'outline', 'ghost', 'nav', 'tab', 'danger', 'grey']
|
|
97
97
|
const known = !resolvedVariant || KNOWN.includes(resolvedVariant)
|
|
98
98
|
if (!known && import.meta.env.DEV) console.warn(`Button: unknown variant "${resolvedVariant}" — rendering the inherited tone (primary)`)
|
|
99
99
|
const variantClass = resolvedVariant && known ? `kol-btn-${resolvedVariant}` : ''
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import Button from '../atoms/Button.jsx'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* TabChips — Tabs as a row of chips. the active tab is a filled chip, the rest are quiet text.
|
|
3
5
|
*
|
|
@@ -10,28 +12,37 @@
|
|
|
10
12
|
*
|
|
11
13
|
* Labels render as authored. Selection is the parent's.
|
|
12
14
|
*
|
|
15
|
+
* ON THE CONTROL SIZE RAMP (user 2026-10-02: *"can we make the tabtoggle align with the size ramp
|
|
16
|
+
* controls use?"*). A chip was a bare button at its own 24px, beside a 26px dropdown in the same
|
|
17
|
+
* bar. Each chip is a `Button variant="tab"` now, so `size` is the ramp's (22 · 26 · 32 · 40) and
|
|
18
|
+
* the chip's look is a Button state anything else can wear.
|
|
19
|
+
*
|
|
13
20
|
* @param {Array} tabs `[{ id, label }]`
|
|
14
21
|
* @param {string} value id of the active tab (controlled)
|
|
15
22
|
* @param {Function} onChange `(id) => void`
|
|
23
|
+
* @param {'xs'|'sm'|'md'|'lg'} [size='sm'] the control ramp's rung
|
|
16
24
|
* @param {string} [ariaLabel] accessible name for the strip
|
|
17
25
|
* @param {string} [className] extra classes on the row
|
|
18
26
|
*/
|
|
19
|
-
export default function TabChips({ tabs = [], value, onChange, ariaLabel, className = '' }) {
|
|
27
|
+
export default function TabChips({ tabs = [], value, onChange, size = 'sm', ariaLabel, className = '' }) {
|
|
20
28
|
return (
|
|
21
29
|
<div className={`flex items-center gap-1 ${className}`.trim()} role="tablist" aria-label={ariaLabel}>
|
|
22
30
|
{tabs.map((t) => {
|
|
23
31
|
const active = t.id === value
|
|
24
32
|
return (
|
|
25
|
-
|
|
33
|
+
/* a tab is selected, not pressed — the role's own attribute replaces Button's aria-pressed */
|
|
34
|
+
<Button
|
|
26
35
|
key={t.id}
|
|
27
|
-
|
|
36
|
+
variant="tab"
|
|
37
|
+
size={size}
|
|
38
|
+
pressed={active}
|
|
28
39
|
role="tab"
|
|
29
40
|
aria-selected={active}
|
|
41
|
+
aria-pressed={undefined}
|
|
30
42
|
onClick={() => onChange?.(t.id)}
|
|
31
|
-
className={`kol-mono-12 rounded-[var(--kol-radius-sm)] px-3 py-1 transition-colors [@media(pointer:coarse)]:min-h-8 ${active ? 'bg-fg-08 text-emphasis' : 'text-meta hover:text-emphasis'}`}
|
|
32
43
|
>
|
|
33
44
|
{t.label}
|
|
34
|
-
</
|
|
45
|
+
</Button>
|
|
35
46
|
)
|
|
36
47
|
})}
|
|
37
48
|
</div>
|
|
@@ -47,7 +47,10 @@ import { minHeightClass } from './sectionHeights.js'
|
|
|
47
47
|
* @param {ReactNode} media image / video / interactive node for the visual column
|
|
48
48
|
* @param {string} [ratio='4/5'] aspect ratio of the media frame. THE FRAME IS BOUNDED BY THE
|
|
49
49
|
* RUNG (SectionSplitMediaBounded, 2026-08-27): its height is the section's `height` rung minus
|
|
50
|
-
* the vertical padding, its width follows the ratio (capped at the column)
|
|
50
|
+
* the vertical padding, its width follows the ratio (capped at the column) — the RUNG is the
|
|
51
|
+
* dimension that holds and the WIDTH the one that yields, so a frame can stand narrower than its
|
|
52
|
+
* column; take a taller rung for bigger media. Below 901px, stacked, it is the other way round:
|
|
53
|
+
* the frame is as wide as the column and its height follows the ratio. Before, the frame
|
|
51
54
|
* was as wide as its column and set the section's height itself, so `40` · `60` · `80`
|
|
52
55
|
* rendered identically — a ladder that only floors is decorative for a card with media.
|
|
53
56
|
* @param {boolean} [mediaHover=false] zoom the media on frame hover (the CardFeatureItem numbers)
|
|
@@ -251,7 +254,15 @@ export default function SectionSplit({
|
|
|
251
254
|
* silently, which is why it was reported as a ProfileCard crop.
|
|
252
255
|
* Below 901 the frame is `w-full` + ratio: width decides, height
|
|
253
256
|
* follows, media never clips. ≥901 nothing moves. */
|
|
254
|
-
|
|
257
|
+
/* …AND THE WIDTH FOLLOWS AGAIN FROM 901 (section-split-frame-ratio-holds-at-desktop,
|
|
258
|
+
* kol-website 2026-10-02; user: *"why is this no longer its original ratio? its
|
|
259
|
+
* squished?"*). `w-full` was written for the stack and applied at every width, so
|
|
260
|
+
* beside the text the frame had BOTH dimensions fixed — the column's width and the
|
|
261
|
+
* rung's height — and `aspect-ratio` had nothing left to decide: a `5/4` frame
|
|
262
|
+
* rendered 2.22 at 1600 and cropped its image. From 901 the width is `auto` again:
|
|
263
|
+
* the rung sets the height, the ratio sets the width, the column caps it — the
|
|
264
|
+
* 2026-08-27 rule written on the `ratio` prop. */
|
|
265
|
+
className={`kol-section-split-visual relative w-full min-[901px]:w-auto max-w-full justify-self-center rounded-[var(--kol-radius-sm)] min-[901px]:h-[calc(var(--kol-section-h,60vh)_-_2*var(--kol-section-py,4rem))] ${mediaClip ? 'overflow-hidden' : ''} ${mediaHover ? 'is-hoverable' : ''} ${mediaFirst ? 'order-1' : ''} ${centred ? 'max-w-[640px]' : ''}`.replace(/\s+/g, ' ').trim()}
|
|
255
266
|
style={{ aspectRatio: ratio }}
|
|
256
267
|
>
|
|
257
268
|
{media ?? (placeholder === true
|