@kolkrabbi/kol-component 0.229.0 → 0.230.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 +4 -4
- package/src/atoms/ActionButton.jsx +1 -1
- package/src/atoms/Button.jsx +2 -2
- package/src/atoms/IconFrame.jsx +1 -1
- package/src/atoms/SizeOrDownload.jsx +1 -1
- package/src/atoms/Tag.jsx +3 -3
- package/src/hooks/colorMath.js +1 -1
- package/src/index.js +6 -6
- package/src/molecules/ColorInputRow.jsx +1 -1
- package/src/molecules/ContentCard.jsx +5 -5
- package/src/molecules/ContentMedia.jsx +1 -1
- package/src/molecules/ContentRow.jsx +2 -2
- package/src/{utilities → molecules}/ContextMenu.jsx +1 -1
- package/src/molecules/DocsToc.jsx +1 -1
- package/src/molecules/PaletteHarmonyWheel.jsx +4 -4
- package/src/molecules/ProfileCard.jsx +1 -1
- package/src/molecules/QuickLookFrame.jsx +1 -1
- package/src/molecules/ShellDrawer.jsx +1 -1
- package/src/molecules/SwatchControls.jsx +3 -3
- package/src/molecules/TabsRow.jsx +1 -1
- package/src/organisms/Canvas.jsx +1 -1
- package/src/organisms/ContentFilters.jsx +1 -1
- package/src/organisms/MediaLibraryPages.jsx +1 -1
- package/src/organisms/SectionCards.jsx +1 -1
- package/src/organisms/SectionCta.jsx +1 -1
- package/src/organisms/SectionFaq.jsx +1 -1
- package/src/organisms/SectionNewsletter.jsx +1 -1
- package/src/organisms/sectionBleed.js +1 -1
- package/src/utilities/FullscreenOverlay.jsx +1 -1
- package/src/utilities/LoaderOverlay.jsx +2 -2
- package/src/utilities/Popover.jsx +10 -1
- package/src/utilities/tone.js +1 -1
- /package/src/{utilities → atoms}/CloseButton.jsx +0 -0
- /package/src/{atoms → utilities}/CropOverlay.jsx +0 -0
- /package/src/{atoms → utilities}/CurveOverlay.jsx +0 -0
- /package/src/{atoms → utilities}/PathNodeOverlay.jsx +0 -0
- /package/src/{atoms → utilities}/SelectionOverlay.jsx +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.230.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",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"embla-carousel-react": "^8.6.0",
|
|
26
26
|
"pdfjs-dist": "^6.3.289",
|
|
27
27
|
"react-syntax-highlighter": "^16.1.1",
|
|
28
|
-
"@kolkrabbi/kol-
|
|
29
|
-
"@kolkrabbi/kol-
|
|
28
|
+
"@kolkrabbi/kol-search": "^0.2.0",
|
|
29
|
+
"@kolkrabbi/kol-markdown": "^0.1.2"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"@kolkrabbi/kol-icons": ">=0.22.0",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"react-dom": "^18.3.0 || ^19.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@kolkrabbi/kol-icons": "^0.29.
|
|
40
|
+
"@kolkrabbi/kol-icons": "^0.29.1"
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
43
43
|
"src",
|
|
@@ -146,7 +146,7 @@ export default function ActionButton({
|
|
|
146
146
|
const rest = restRef.current
|
|
147
147
|
const on = onRef.current
|
|
148
148
|
/* no confirmIcon = ONE glyph, and the state is carried by the class alone
|
|
149
|
-
* (a fill, a
|
|
149
|
+
* (a fill, a color). Nothing to crossfade. */
|
|
150
150
|
if (!rest || !on) return undefined
|
|
151
151
|
|
|
152
152
|
if (first.current) {
|
package/src/atoms/Button.jsx
CHANGED
|
@@ -18,7 +18,7 @@ import { glyphSize } from '../hooks/glyphLadders.js'
|
|
|
18
18
|
* @param {string} props.iconRight - Icon name to display on the right
|
|
19
19
|
* @param {string} props.iconLeftHover - Icon to show on hover (left position)
|
|
20
20
|
* @param {string} props.iconRightHover - Icon to show on hover (right position)
|
|
21
|
-
* @param {'primary'|'secondary'|'inverted'|'outline'|'ghost'|'grey'|'sunken'} props.tone - the ground (`secondary` = the page surface, `inverted` = the text
|
|
21
|
+
* @param {'primary'|'secondary'|'inverted'|'outline'|'ghost'|'grey'|'sunken'} props.tone - the ground (`secondary` = the page surface, `inverted` = the text color as fill — what `variant="secondary"` paints) (tone-is-the-ground-axis, 2026-09-03) — wins over `variant` on the same element; unset = inherit the wrapper's. `sunken` = the control set's dark well + fg-96 ink (ControlToneSunken); `inverse` aliased
|
|
22
22
|
* @param {string} props.iconOnly - Icon name for icon-only button
|
|
23
23
|
* @param {string} props.iconOnlyHover - Icon to show on hover (icon-only)
|
|
24
24
|
* @param {boolean} props.animateIcon - Disable default hover states to focus on icon animation
|
|
@@ -82,7 +82,7 @@ const Button = ({
|
|
|
82
82
|
* rung, oq-80 ink + aria-current — it had lived in the theme with no
|
|
83
83
|
* component able to emit it, the direct cause of the four-container header. */
|
|
84
84
|
/* An UNKNOWN variant is a typo, not a request for the inverted fill — it fell back to
|
|
85
|
-
* `kol-btn-secondary` (the text
|
|
85
|
+
* `kol-btn-secondary` (the text color as fill) until 2026-09-29, so a misspelt variant
|
|
86
86
|
* shipped inverted. It now stamps nothing, like an unset variant: the wrapper's tone,
|
|
87
87
|
* else primary — and says so in dev. */
|
|
88
88
|
const KNOWN = ['primary', 'secondary', 'accent', 'outline', 'ghost', 'nav', 'danger', 'grey']
|
package/src/atoms/IconFrame.jsx
CHANGED
|
@@ -22,7 +22,7 @@ import { toneClass } from '../utilities/tone.js'
|
|
|
22
22
|
* frame's own classes declare the same background, foreground and geometry and
|
|
23
23
|
* simply have no state rules to inherit.
|
|
24
24
|
*
|
|
25
|
-
* `variant` borrows the kol-btn
|
|
25
|
+
* `variant` borrows the kol-btn COLOR SET verbatim so the frame sits in the
|
|
26
26
|
* same visual system as real buttons; `primary` and `secondary` are inverse
|
|
27
27
|
* pairs that flip with the theme, so light/dark comes free from the tokens with
|
|
28
28
|
* no per-theme props.
|
|
@@ -21,7 +21,7 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
21
21
|
* click, and this one is inside the link rather than being it.
|
|
22
22
|
*
|
|
23
23
|
* Both hover parts ink on the OPACITY scale (`text-oq-80`), not an `fg-*` role:
|
|
24
|
-
* a stroke glyph on a flat fg
|
|
24
|
+
* a stroke glyph on a flat fg color reads wrong against the plate, and oq is
|
|
25
25
|
* what the rest of the chrome uses.
|
|
26
26
|
*
|
|
27
27
|
* ONE type class throughout — kol-mono-12. helper-12 is line-height 1 against
|
package/src/atoms/Tag.jsx
CHANGED
|
@@ -18,7 +18,7 @@ const ICON_SIZES = { xs: 8, sm: 10, md: 12, lg: 14 }
|
|
|
18
18
|
* `:hover` rule between them, on `.tag-control`. Every other path rendered
|
|
19
19
|
* dead.
|
|
20
20
|
* - `color` was a SECOND axis that silently swapped the base class from
|
|
21
|
-
* `tag-control` to `tag tag--{color}` — so passing a
|
|
21
|
+
* `tag-control` to `tag tag--{color}` — so passing a color cost you the
|
|
22
22
|
* interaction state, invisibly. That is what shipped a solid blue pill you
|
|
23
23
|
* could not hover.
|
|
24
24
|
* - `variant="solid"` and a `solid` boolean did the same job.
|
|
@@ -28,8 +28,8 @@ const ICON_SIZES = { xs: 8, sm: 10, md: 12, lg: 14 }
|
|
|
28
28
|
*
|
|
29
29
|
* Now it is Pill's vocabulary — `primary` (filled) · `secondary` (outlined) ·
|
|
30
30
|
* `inverse` — one size scale, ONE class scheme (`kol-tag--*`), and every
|
|
31
|
-
* variant carries hover + active.
|
|
32
|
-
* variant, exactly as it is on Pill and Button. Tag
|
|
31
|
+
* variant carries hover + active. Color is not a prop: a chip's look is its
|
|
32
|
+
* variant, exactly as it is on Pill and Button. Tag color BY TAXONOMY returns
|
|
33
33
|
* later as its own decision, on top of the variants rather than instead of them.
|
|
34
34
|
*
|
|
35
35
|
* @param {ReactNode} children label content
|
package/src/hooks/colorMath.js
CHANGED
|
@@ -116,7 +116,7 @@ export function harmonyColors(hue, harmony, { saturation = 100, lightness = 50 }
|
|
|
116
116
|
* package's `colors` payload was half-ignorable).
|
|
117
117
|
*
|
|
118
118
|
* A slot that is `locked`, empty, or has no hex is passed through untouched —
|
|
119
|
-
* locking a
|
|
119
|
+
* locking a color is the one instruction a re-hue must not overrule.
|
|
120
120
|
*
|
|
121
121
|
* @param {number} hue base hue, 0–360
|
|
122
122
|
* @param {string|object} harmony harmony id or object
|
package/src/index.js
CHANGED
|
@@ -22,14 +22,14 @@ export { default as AssetPlaceholder } from './utilities/AssetPlaceholder.jsx'
|
|
|
22
22
|
export { default as Avatar } from './atoms/Avatar.jsx'
|
|
23
23
|
export { default as Badge } from './atoms/Badge.jsx'
|
|
24
24
|
export { default as Button } from './atoms/Button.jsx'
|
|
25
|
-
export { default as CloseButton } from './
|
|
25
|
+
export { default as CloseButton } from './atoms/CloseButton.jsx'
|
|
26
26
|
export { default as ActionButton } from './atoms/ActionButton.jsx'
|
|
27
27
|
export { default as FileIcon } from './atoms/FileIcon.jsx'
|
|
28
28
|
export { default as SizeOrDownload } from './atoms/SizeOrDownload.jsx'
|
|
29
29
|
export { default as SortHeader } from './atoms/SortHeader.jsx'
|
|
30
30
|
export { default as SortControls } from './molecules/SortControls.jsx'
|
|
31
31
|
export { default as CopyButton } from './molecules/CopyButton.jsx'
|
|
32
|
-
export { default as CurveOverlay } from './
|
|
32
|
+
export { default as CurveOverlay } from './utilities/CurveOverlay.jsx'
|
|
33
33
|
export { default as Divider } from './atoms/Divider.jsx'
|
|
34
34
|
export { default as DocsToc } from './molecules/DocsToc.jsx'
|
|
35
35
|
export { default as DropdownTagFilter } from './molecules/DropdownTagFilter.jsx'
|
|
@@ -49,7 +49,7 @@ export { default as LabeledControl } from './molecules/LabeledControl.jsx'
|
|
|
49
49
|
export { default as OverlayGlassPanel } from './utilities/OverlayGlassPanel.jsx'
|
|
50
50
|
export { default as Pill } from './atoms/Pill.jsx'
|
|
51
51
|
export { usePopover, PopoverPanel, Tooltip } from './utilities/Popover.jsx'
|
|
52
|
-
export { default as ContextMenu, useContextMenu } from './
|
|
52
|
+
export { default as ContextMenu, useContextMenu } from './molecules/ContextMenu.jsx'
|
|
53
53
|
export { default as ProsePreview } from './utilities/ProsePreview.jsx'
|
|
54
54
|
export { default as QuantityInput } from './molecules/QuantityInput.jsx'
|
|
55
55
|
export { default as RotaryDial } from './atoms/RotaryDial.jsx'
|
|
@@ -73,8 +73,8 @@ export { default as InspectorRail } from './molecules/InspectorRail.jsx'
|
|
|
73
73
|
/* SelectionOverlay's two siblings — same 1080-virtual contract, same zoom
|
|
74
74
|
* division (editor-panels-the-held-specs B2). `pathMath` is their geometry,
|
|
75
75
|
* exported because the editor engine re-exports it rather than keep a copy. */
|
|
76
|
-
export { default as PathNodeOverlay } from './
|
|
77
|
-
export { default as CropOverlay } from './
|
|
76
|
+
export { default as PathNodeOverlay } from './utilities/PathNodeOverlay.jsx'
|
|
77
|
+
export { default as CropOverlay } from './utilities/CropOverlay.jsx'
|
|
78
78
|
export { default as LayerStack, AddLayerButton, BLEND_MODES } from './organisms/LayerStack.jsx'
|
|
79
79
|
export { TYPE_LABELS, BOOL_OP_LABELS, SHAPE_KIND_LABELS, labelForLayer, rowLabelForLayer, findLayerDeep } from './hooks/layerTree.js'
|
|
80
80
|
export { default as TimelineDock, sampleTrack, TIMELINE_EASINGS } from './organisms/TimelineDock.jsx'
|
|
@@ -88,7 +88,7 @@ export { default as ButtonGroup } from './utilities/ButtonGroup.jsx'
|
|
|
88
88
|
/* monorepo sets (P6–P10) — molecule members */
|
|
89
89
|
export { default as AlignmentGrid } from './molecules/AlignmentGrid.jsx'
|
|
90
90
|
export { default as ImageBlock } from './molecules/ImageBlock.jsx'
|
|
91
|
-
export { default as SelectionOverlay } from './
|
|
91
|
+
export { default as SelectionOverlay } from './utilities/SelectionOverlay.jsx'
|
|
92
92
|
export { default as VideoBlock, getEmbedUrl } from './molecules/VideoBlock.jsx'
|
|
93
93
|
export { default as SectionCardItem, default as CardFeatureItem } from './molecules/SectionCardItem.jsx'
|
|
94
94
|
export { default as CodeBlock } from './molecules/CodeBlock.jsx'
|
|
@@ -243,7 +243,7 @@ export default function ColorInputRow({
|
|
|
243
243
|
)}
|
|
244
244
|
{/* QUICK STATES. Theme (the auto value — a token that flips with
|
|
245
245
|
light/dark) is offered only where the field HAS one; None is
|
|
246
|
-
always available, because clearing a
|
|
246
|
+
always available, because clearing a color is not a palette
|
|
247
247
|
decision. Both were dropped in the first port, which is what left
|
|
248
248
|
`value == null` renderable but unreachable. */}
|
|
249
249
|
<div className="flex items-center gap-2">
|
|
@@ -47,7 +47,7 @@ const BOX = {
|
|
|
47
47
|
/* SLIDE (slide-variant-and-shelf-preset, kol-client-olina 2026-09-03; user: "they are genuinely
|
|
48
48
|
* different with 16:9 layout and those exposed properties"): file's stack — cover on top, the
|
|
49
49
|
* plate below — on the PAGE'S surface, rest and hover (olina's /slide-deck, read off the render).
|
|
50
|
-
* The plate is not a tone (user: "no just controls"); it is this kind's
|
|
50
|
+
* The plate is not a tone (user: "no just controls"); it is this kind's color. */
|
|
51
51
|
slide: { layout: 'stack', border: null, bg: 'var(--kol-surface-primary)', pad: 'var(--kol-pad-card-sm)' },
|
|
52
52
|
/* THE FRAME READS BACKWARDS (CatalogCardFrameAndZoom, kol-website 2026-08-28 — user, on a 212-tile
|
|
53
53
|
* grid: no frame at rest; the old rest value is the hover): a wall of fg-04 frames is a grid of boxes,
|
|
@@ -94,7 +94,7 @@ const BOX = {
|
|
|
94
94
|
* has no surface of its own to step, so it dims its title instead. */
|
|
95
95
|
const HOVER = {
|
|
96
96
|
file: 'var(--kol-oq-04)',
|
|
97
|
-
slide: 'var(--kol-surface-primary)', /* the plate holds its
|
|
97
|
+
slide: 'var(--kol-surface-primary)', /* the plate holds its color on hover; the drawer control is the affordance */
|
|
98
98
|
catalog: 'var(--kol-surface-tertiary)',
|
|
99
99
|
/* article and work take NO surface hover, and that is a decision not a gap:
|
|
100
100
|
* article has no surface of its own (its media frame, when on, steps its
|
|
@@ -168,7 +168,7 @@ export default function ContentCard({
|
|
|
168
168
|
* print draw it); `false` turns it off without an `!important` in a consumer sheet */
|
|
169
169
|
plateRule,
|
|
170
170
|
/* `bg` — the card's REST fill, overriding the variant's. It sets
|
|
171
|
-
* `--kol-card-bg`, not a background, because the rest
|
|
171
|
+
* `--kol-card-bg`, not a background, because the rest colors are custom
|
|
172
172
|
* properties so the hover class can win; that is also why
|
|
173
173
|
* `className="bg-oq-48"` does nothing here and a consumer reaching around the
|
|
174
174
|
* component had to write `className="[--kol-card-bg:var(--kol-oq-48)]"`
|
|
@@ -437,7 +437,7 @@ export default function ContentCard({
|
|
|
437
437
|
'data-tags': isHero && Array.isArray(text.tags) && text.tags.length ? text.tags.join(' ') : undefined,
|
|
438
438
|
className: `kol-card group flex ${box.layout === 'canvas' && reveal != null ? 'has-reveal' : ''} ${expanded ? 'flex-col md:flex-row-reverse' : 'flex-col'} ${box.layout === 'drawer' ? 'relative overflow-hidden rounded-[var(--kol-radius-sm)]' : ''} ${framed ? 'overflow-hidden rounded-[var(--kol-radius-sm)]' : ''} ${box.border ? 'border' : ''} ${box.layout === 'canvas' ? 'relative' : ''} ${interactive ? 'cursor-pointer select-none' : ''} ${hoverBg && interactive ? 'kol-content-hover' : ''} ${interactive && box.frameHover ? 'kol-content-hover-frame' : ''} ${className}`.trim(),
|
|
439
439
|
style: {
|
|
440
|
-
/* same reason as ContentRow: rest
|
|
440
|
+
/* same reason as ContentRow: rest colors are PROPERTIES, because an
|
|
441
441
|
* inline background/borderColor outranks the hover class and the step
|
|
442
442
|
* would never render. */
|
|
443
443
|
'--kol-card-bg': bg ?? box.bg ?? undefined,
|
|
@@ -464,7 +464,7 @@ export default function ContentCard({
|
|
|
464
464
|
onContextMenu={onContextMenu}
|
|
465
465
|
onDoubleClick={onDoubleClick}
|
|
466
466
|
/* SELECTED HAS TO SHOW (user 2026-09-23: a click in the grid "should select/highlight").
|
|
467
|
-
* `selected` only flipped a border
|
|
467
|
+
* `selected` only flipped a border color on the variants that HAVE a border, and the file
|
|
468
468
|
* wall's cards have none — so the prop was true and the card looked untouched. The
|
|
469
469
|
* attribute is the hook; the theme paints it, once, for every variant. */
|
|
470
470
|
data-selected={selected || undefined}
|
|
@@ -57,7 +57,7 @@ import AssetPlaceholder from '../utilities/AssetPlaceholder.jsx'
|
|
|
57
57
|
* @param {boolean} frame tinted box + border UNDER the media
|
|
58
58
|
* @param {boolean} border border only, no tint
|
|
59
59
|
* @param {string} bg tint only, no border — a raw token value
|
|
60
|
-
* @param {string} borderHover border
|
|
60
|
+
* @param {string} borderHover border color on hover (article's fg-16 step)
|
|
61
61
|
* @param {boolean} ring hairline border OVER the media, inset
|
|
62
62
|
* @param {boolean|'hero'} zoom the artwork creeps up inside its frame on the
|
|
63
63
|
* card's hover — 1.06; `'hero'` = the hero rung, 1.02
|
|
@@ -130,7 +130,7 @@ const BOX = {
|
|
|
130
130
|
* its background and read as a different interaction from every other listing.
|
|
131
131
|
*
|
|
132
132
|
* The three passes are the lesson: the ask was "make it look like /work" and
|
|
133
|
-
* each ticket named only the state someone had looked at — rest
|
|
133
|
+
* each ticket named only the state someone had looked at — rest colors, then
|
|
134
134
|
* the derive's collateral, then this. When a user says make X look like Y,
|
|
135
135
|
* diff EVERY state: rest, hover, selected, focus. */
|
|
136
136
|
bg: 'var(--kol-surface-secondary)', frame: 'transparent', frameHover: 'var(--kol-fg-08)' },
|
|
@@ -171,7 +171,7 @@ export default function ContentRow({
|
|
|
171
171
|
* card, `contentcard-bg-and-text-props` 2026-09-03; the pair ships together
|
|
172
172
|
* and a consumer that re-grounds one hits the same wall on the other in the
|
|
173
173
|
* same grid). It sets `--kol-row-bg`, not a background, because the rest
|
|
174
|
-
*
|
|
174
|
+
* colors are custom properties so the hover class can win — which is why
|
|
175
175
|
* `className="bg-oq-48"` does nothing here either. `selected` still wins:
|
|
176
176
|
* a selected row is the list's state, not the consumer's ground. For the
|
|
177
177
|
* row's INK, pass `text` — it falls through to ContentText. */
|
|
@@ -128,7 +128,7 @@ export default function DocsToc({
|
|
|
128
128
|
* `block transition-colors focus-visible:ring-focus
|
|
129
129
|
* hover:text-emphasis text-body` as utilities, so the same rung
|
|
130
130
|
* rendered a different className here than in the left tree.
|
|
131
|
-
* Layout,
|
|
131
|
+
* Layout, color, hover and focus live in `.shell-nav-item` now;
|
|
132
132
|
* active is the shared `is-active` marker, not `text-emphasis`
|
|
133
133
|
* typed at one call site. Matches RailRow exactly. */
|
|
134
134
|
className={`shell-nav-item kol-mono-14${activeId === item.id ? ' is-active' : ''}`}
|
|
@@ -43,11 +43,11 @@ import { HARMONIES, harmonyById, harmonyColors, normHue, reHueSlots } from '../h
|
|
|
43
43
|
* @param {Array} harmonies injectable scheme table (default HARMONIES)
|
|
44
44
|
* @param {Array} slots the CURRENT palette in role order (`{hex, locked}` objects or plain hex strings). Given, `colors` re-hues these — each slot keeps its own S/L, locked and empty entries pass through — instead of generating flat ones
|
|
45
45
|
* @param {Function} onChange ({ hue, colors }) => void
|
|
46
|
-
* @param {Function} onHueChange (hue) => void — the payload-free seam, for a caller that derives its own
|
|
46
|
+
* @param {Function} onHueChange (hue) => void — the payload-free seam, for a caller that derives its own colors
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
49
|
/* Marker outline — white for contrast against the fully-saturated ring hues
|
|
50
|
-
* (theme-independent: the wheel's
|
|
50
|
+
* (theme-independent: the wheel's colors, not the surface, sit behind it). */
|
|
51
51
|
const MARKER_STROKE = '#FFFFFF'
|
|
52
52
|
|
|
53
53
|
export default function PaletteHarmonyWheel({
|
|
@@ -74,9 +74,9 @@ export default function PaletteHarmonyWheel({
|
|
|
74
74
|
/* Emit next hue + its harmony colors. Held in a ref so the pointer/key
|
|
75
75
|
* handlers stay stable while always seeing the latest props.
|
|
76
76
|
*
|
|
77
|
-
* With `slots`, the
|
|
77
|
+
* With `slots`, the colors are the CALLER'S palette re-hued — each slot
|
|
78
78
|
* keeping its own saturation and lightness — rather than a fresh flat set.
|
|
79
|
-
* Both fire, so a caller can take the hue and ignore the
|
|
79
|
+
* Both fire, so a caller can take the hue and ignore the colors. */
|
|
80
80
|
emitRef.current = (nextHue) => {
|
|
81
81
|
const h = normHue(nextHue)
|
|
82
82
|
const colors = slots?.length
|
|
@@ -27,7 +27,7 @@ import { surfaceClass } from '../utilities/sectionSurface.js'
|
|
|
27
27
|
* `inverse` with inverse ink, exactly as shipped. `controlVariant` goes straight
|
|
28
28
|
* to the disclosure's `IconFrame`; `pad` is `ContentCard`'s — one step on
|
|
29
29
|
* `--kol-pad-card-*`, overriding the size ramp's padding. The logo is a slot
|
|
30
|
-
* and stays one: its ink follows the shelf, a
|
|
30
|
+
* and stays one: its ink follows the shelf, a colored mark is the asset's.
|
|
31
31
|
*
|
|
32
32
|
* THE SHELF SIZES TO ITS CONTENT. The source held it in a fixed box
|
|
33
33
|
* (`h-60`/`h-44`/`h-32`/`h-24`, `overflow-hidden`) and every vertical size
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useRef, useState } from 'react'
|
|
2
2
|
import Button from '../atoms/Button.jsx'
|
|
3
|
-
import CloseButton from '../
|
|
3
|
+
import CloseButton from '../atoms/CloseButton.jsx'
|
|
4
4
|
|
|
5
5
|
/* taxonomy-ok: molecule — nests Button (atom) + CloseButton (utility). */
|
|
6
6
|
|
|
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react'
|
|
|
2
2
|
import { pushLayer, popLayer, isTopLayer } from '../utilities/layerStack.js'
|
|
3
3
|
import { createPortal } from 'react-dom'
|
|
4
4
|
import { Icon } from '@kolkrabbi/kol-icons'
|
|
5
|
-
import CloseButton from '../
|
|
5
|
+
import CloseButton from '../atoms/CloseButton.jsx'
|
|
6
6
|
import usePrefersReducedMotion from '../hooks/usePrefersReducedMotion.js'
|
|
7
7
|
|
|
8
8
|
/* taxonomy-ok: nests kol-icons's Icon (a package import the relative-import
|
|
@@ -4,7 +4,7 @@ import ColorSwatch from '../atoms/ColorSwatch.jsx'
|
|
|
4
4
|
import { Tooltip } from '../utilities/Popover.jsx'
|
|
5
5
|
|
|
6
6
|
/*
|
|
7
|
-
* SwatchControls — the Photoshop-style top row of the
|
|
7
|
+
* SwatchControls — the Photoshop-style top row of the color panel, in two
|
|
8
8
|
* hand-tuned pieces plus the composed row. Hand-tuned visuals (fixed pixel
|
|
9
9
|
* slots, the overlap-by-DOM-order trick, the double-ring halo, the red-slash
|
|
10
10
|
* "none" marker). DO NOT refactor pieces to atoms — the look is intentional
|
|
@@ -20,7 +20,7 @@ import { Tooltip } from '../utilities/Popover.jsx'
|
|
|
20
20
|
* No z-index, no transform — DOM order alone stacks them.
|
|
21
21
|
*
|
|
22
22
|
* <EyedropPick sampleColor onPick disabled />
|
|
23
|
-
* — eyedropper icon button + a small sample chip of the sampled
|
|
23
|
+
* — eyedropper icon button + a small sample chip of the sampled color.
|
|
24
24
|
* The button is feature-gated: it is hidden entirely when the browser
|
|
25
25
|
* EyeDropper API is unavailable, and `disabled` dims it when supported
|
|
26
26
|
* but unusable. The actual EyeDropper call + canvas sampling live at the
|
|
@@ -92,7 +92,7 @@ export function SwatchStack({
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
* EyedropPick — eyedropper icon button + a sample chip of the sampled
|
|
95
|
+
* EyedropPick — eyedropper icon button + a sample chip of the sampled color.
|
|
96
96
|
* The button is hidden when the browser EyeDropper API is unavailable (no
|
|
97
97
|
* affordance for an action that can't run); when supported but unusable pass
|
|
98
98
|
* `disabled` to dim it. `onPick` is the app seam where EyeDropper + canvas
|
package/src/organisms/Canvas.jsx
CHANGED
|
@@ -93,7 +93,7 @@ export function CanvasFrame({
|
|
|
93
93
|
const { ratio, label } = resolveAspect(aspect, customRatio, aspects)
|
|
94
94
|
/* NO guideColor → THEME INK (editor DS sync 2026-09-27, from the design editor's copy, which had
|
|
95
95
|
* it and this lift had not): the border and label follow the theme, so the frame reads on a light
|
|
96
|
-
* page and a dark one. A guideColor (a type frame's own
|
|
96
|
+
* page and a dark one. A guideColor (a type frame's own color) still tints both. */
|
|
97
97
|
const borderColor = guideColor ? `color-mix(in srgb, ${guideColor} 24%, transparent)` : 'var(--kol-oq-24)'
|
|
98
98
|
const labelColor = guideColor ? `color-mix(in srgb, ${guideColor} 70%, transparent)` : 'var(--kol-fg-64)'
|
|
99
99
|
const virtualH = CANVAS_VIRTUAL_W / ratio
|
|
@@ -395,7 +395,7 @@ const ContentFilters = ({
|
|
|
395
395
|
* 05-control-chrome.md:109 — "any icon-only control in chrome is
|
|
396
396
|
* IconFrame; nothing hand-writes the square". This wore
|
|
397
397
|
* `kol-btn-md kol-btn-icon` and then removed the background, the
|
|
398
|
-
* border and the
|
|
398
|
+
* border and the color by inline style, which is the whole button
|
|
399
399
|
* paid for and thrown away — and it left the control with no
|
|
400
400
|
* states at all while the search beside it had hover.
|
|
401
401
|
*
|
|
@@ -22,7 +22,7 @@ import { filterMedia, rankMedia } from '../utilities/mediaSearch.js'
|
|
|
22
22
|
import { useMasthead, mastheadTitleClass } from '../utilities/masthead.js'
|
|
23
23
|
import { MenuItem, MenuDropdownItem, MenuDropdownDivider } from '../molecules/MenuItem.jsx'
|
|
24
24
|
import { Tooltip } from '../utilities/Popover.jsx'
|
|
25
|
-
import ContextMenu, { useContextMenu } from '../
|
|
25
|
+
import ContextMenu, { useContextMenu } from '../molecules/ContextMenu.jsx'
|
|
26
26
|
import useMarquee from '../hooks/useMarquee.js'
|
|
27
27
|
import useLongPress from '../hooks/useLongPress.js'
|
|
28
28
|
import useMediaQuery from '../hooks/useMediaQuery.js'
|
|
@@ -36,7 +36,7 @@ import { minHeightClass } from './sectionHeights.js'
|
|
|
36
36
|
* @param {boolean} [fullBleed=false] the FILL breaks the page gutter while the content keeps it —
|
|
37
37
|
* the family's shared breakout (`sectionBleed.js`, SectionFamilyFullBleed, kol-website 2026-08-31).
|
|
38
38
|
* Any member of this family can be a filled surface, and a filled surface inside `.kol-page` has its
|
|
39
|
-
*
|
|
39
|
+
* color clipped by the gutter on mobile. Viewport-relative, so unlike `.kol-full-bleed` it does not
|
|
40
40
|
* over-bleed in a parent with no gutter of its own. The section's horizontal padding re-insets the
|
|
41
41
|
* CONTENT, so only the fill moves. Default false — nothing renders differently until it is passed.
|
|
42
42
|
* @param {string} sectionClassName · wrapperClassName · cardsWrapperClassName · actionsClassName · headerClassName · headerTextWidthClass layout seams
|
|
@@ -30,7 +30,7 @@ import { minHeightClass } from './sectionHeights.js'
|
|
|
30
30
|
* @param {boolean} [fullBleed=false] the FILL breaks the page gutter while the content keeps it —
|
|
31
31
|
* the family's shared breakout (`sectionBleed.js`, SectionFamilyFullBleed, kol-website 2026-08-31).
|
|
32
32
|
* Any member of this family can be a filled surface, and a filled surface inside `.kol-page` has its
|
|
33
|
-
*
|
|
33
|
+
* color clipped by the gutter on mobile. Viewport-relative, so unlike `.kol-full-bleed` it does not
|
|
34
34
|
* over-bleed in a parent with no gutter of its own. The section's horizontal padding re-insets the
|
|
35
35
|
* CONTENT, so only the fill moves. Default false — nothing renders differently until it is passed.
|
|
36
36
|
* @param {string} className extra classes on the section
|
|
@@ -21,7 +21,7 @@ import { minHeightClass } from './sectionHeights.js'
|
|
|
21
21
|
* @param {boolean} [fullBleed=false] the FILL breaks the page gutter while the content keeps it —
|
|
22
22
|
* the family's shared breakout (`sectionBleed.js`, SectionFamilyFullBleed, kol-website 2026-08-31).
|
|
23
23
|
* Any member of this family can be a filled surface, and a filled surface inside `.kol-page` has its
|
|
24
|
-
*
|
|
24
|
+
* color clipped by the gutter on mobile. Viewport-relative, so unlike `.kol-full-bleed` it does not
|
|
25
25
|
* over-bleed in a parent with no gutter of its own. The section's horizontal padding re-insets the
|
|
26
26
|
* CONTENT, so only the fill moves. Default false — nothing renders differently until it is passed.
|
|
27
27
|
* @param {string} className · innerClassName layout seams
|
|
@@ -60,7 +60,7 @@ import { minHeightClass } from './sectionHeights.js'
|
|
|
60
60
|
* @param {boolean} [fullBleed=false] the FILL breaks the page gutter while the content keeps it
|
|
61
61
|
* (SectionNewsletterFullBleed, kol-website 2026-08-31). This card is a filled surface inside
|
|
62
62
|
* `.kol-page`, so the gutter clipped its background and left strips of page down both sides of the
|
|
63
|
-
*
|
|
63
|
+
* color. Fill and content padding are the same box, so a consumer could not bleed one without
|
|
64
64
|
* dragging the other out with it. The breakout literal is SectionHero's, character for character —
|
|
65
65
|
* two organisms in one family must not invent two ways to leave a gutter. The section's own
|
|
66
66
|
* `px-5 sm:px-8` then re-insets the content, so only the fill moves.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* WHY IT IS A FILLED-SECTION PROBLEM, not a newsletter one. Any member of the
|
|
9
9
|
* family can be a filled surface, and a filled surface inside `.kol-page` has its
|
|
10
|
-
*
|
|
10
|
+
* color clipped by the page gutter on mobile — strips of page down both sides of
|
|
11
11
|
* the fill. Reported once per organism until the prop is shared.
|
|
12
12
|
*
|
|
13
13
|
* NOT `.kol-full-bleed`: that escape is CONTAINER-relative, so on an organism
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react'
|
|
2
2
|
import { pushLayer, popLayer, isTopLayer } from './layerStack.js'
|
|
3
|
-
import CloseButton from '
|
|
3
|
+
import CloseButton from '../atoms/CloseButton.jsx'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* FullscreenOverlay — the scrim + centred sheet every overlay in the repo
|
|
@@ -8,7 +8,7 @@ import FullscreenOverlay from './FullscreenOverlay.jsx'
|
|
|
8
8
|
* `loader` slot — as the overlay content. Being mounted IS being visible;
|
|
9
9
|
* the parent removes this element to dismiss.
|
|
10
10
|
*
|
|
11
|
-
* The loader is a SLOT, not a built-in: inject a curtain (e.g. `<
|
|
11
|
+
* The loader is a SLOT, not a built-in: inject a curtain (e.g. `<IntroLoader/>`
|
|
12
12
|
* from `@kolkrabbi/kol-foundry`) via `loader` and wire its completion callback
|
|
13
13
|
* yourself. The slot is mounted in a `fixed inset-0` box that escapes
|
|
14
14
|
* FullscreenOverlay's centered, `--kol-container-max`-width sheet so the loader
|
|
@@ -16,7 +16,7 @@ import FullscreenOverlay from './FullscreenOverlay.jsx'
|
|
|
16
16
|
* With neither, the loader slot renders nothing (the overlay still works).
|
|
17
17
|
*
|
|
18
18
|
* @param {ReactNode} children overlay content, centered (takes precedence)
|
|
19
|
-
* @param {ReactNode} loader full-screen loading curtain, e.g. foundry's
|
|
19
|
+
* @param {ReactNode} loader full-screen loading curtain, e.g. foundry's IntroLoader
|
|
20
20
|
*/
|
|
21
21
|
export default function LoaderOverlay({ children, loader }) {
|
|
22
22
|
return (
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
flip as flipMw,
|
|
8
8
|
shift as shiftMw,
|
|
9
9
|
size as sizeMw,
|
|
10
|
+
hide as hideMw,
|
|
10
11
|
FloatingPortal,
|
|
11
12
|
FloatingFocusManager,
|
|
12
13
|
useClick,
|
|
@@ -101,6 +102,7 @@ export function usePopover({
|
|
|
101
102
|
* external DOM node (e.g. a parent container ref) instead of wiring
|
|
102
103
|
* `setReference` onto the trigger. Used by TypeBlockToolbar to anchor
|
|
103
104
|
* to its TypeFrame parent. */
|
|
105
|
+
middleware.push(hideMw({ strategy: 'referenceHidden' })) // last — it reads the final position
|
|
104
106
|
const data = useFloating({
|
|
105
107
|
open,
|
|
106
108
|
onOpenChange,
|
|
@@ -110,6 +112,13 @@ export function usePopover({
|
|
|
110
112
|
elements: referenceElement ? { reference: referenceElement } : undefined,
|
|
111
113
|
})
|
|
112
114
|
|
|
115
|
+
/* A PANEL FOLLOWS ITS TRIGGER OUT OF SIGHT (2026-09-30 — docs/menus: an open menu painted over
|
|
116
|
+
* the sticky header when its trigger scrolled under it, and floated at the page's foot when it
|
|
117
|
+
* scrolled away). The panel is portalled to <body>, so no scroll container can clip it; `hide`
|
|
118
|
+
* reports when the trigger is clipped, and the panel hides with it. */
|
|
119
|
+
const hidden = data.middlewareData.hide?.referenceHidden
|
|
120
|
+
const floatingStyles = hidden ? { ...data.floatingStyles, visibility: 'hidden' } : data.floatingStyles
|
|
121
|
+
|
|
113
122
|
const interactions = useInteractions([
|
|
114
123
|
useClick(data.context, { enabled: click }),
|
|
115
124
|
useHover(data.context, { enabled: hover, delay: hoverDelay, move: false }),
|
|
@@ -118,7 +127,7 @@ export function usePopover({
|
|
|
118
127
|
useRole(data.context, { role }),
|
|
119
128
|
])
|
|
120
129
|
|
|
121
|
-
return { ...data, ...interactions, open }
|
|
130
|
+
return { ...data, floatingStyles, ...interactions, open }
|
|
122
131
|
}
|
|
123
132
|
|
|
124
133
|
/**
|
package/src/utilities/tone.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* primary · secondary · inverted · outline · ghost · grey · sunken
|
|
6
6
|
*
|
|
7
|
-
* `secondary` paints the PAGE SURFACE and `inverted` the text
|
|
7
|
+
* `secondary` paints the PAGE SURFACE and `inverted` the text color as fill
|
|
8
8
|
* (tone-secondary-is-inverse, 2026-09-03; user: "that tone should be called
|
|
9
9
|
* secondary. What is currently secondary should be called inverted") — 0.134.0
|
|
10
10
|
* had lifted `secondary` from Button's variant, which was already an inverse.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|