@kolkrabbi/kol-theme 0.43.1 → 0.45.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/kol-components-atoms.css +97 -73
- package/kol-components-molecules.css +146 -0
- package/kol-theme.css +12 -2
- package/package.json +1 -1
package/kol-components-atoms.css
CHANGED
|
@@ -307,7 +307,7 @@
|
|
|
307
307
|
* navigation is location, not a toggled tool. */
|
|
308
308
|
.kol-btn-nav {
|
|
309
309
|
background: transparent;
|
|
310
|
-
color: var(--kol-oq-
|
|
310
|
+
color: var(--kol-oq-80);
|
|
311
311
|
border: 1px solid transparent;
|
|
312
312
|
}
|
|
313
313
|
@media (hover: hover) {
|
|
@@ -329,6 +329,8 @@
|
|
|
329
329
|
* Button variant on the opaque tier: rest oq-12, hover/active one stop past
|
|
330
330
|
* each, same state math as the rest of the family. */
|
|
331
331
|
.kol-btn-grey {
|
|
332
|
+
/* Ladder (user ruling 2026-08-15): 12 rest → 04 hover (darker) → 32 press,
|
|
333
|
+
* then eased back down. */
|
|
332
334
|
background: var(--kol-oq-12);
|
|
333
335
|
color: var(--kol-surface-on-primary);
|
|
334
336
|
border: 1px solid transparent;
|
|
@@ -804,95 +806,117 @@ a:hover .icon-hover,
|
|
|
804
806
|
display: inline-flex;
|
|
805
807
|
align-items: center;
|
|
806
808
|
justify-content: center;
|
|
807
|
-
|
|
808
|
-
* the same rule .kol-icon-frame-{sm,md,lg} obeys at 28/32/36). This class
|
|
809
|
-
* derived its size from `padding: 0.5rem` + whatever glyph it was handed, so
|
|
810
|
-
* a 14px icon silently produced a 30px control off the ladder. 32px is md on
|
|
811
|
-
* both the icon-frame squares and the button ladder (26/32/40), and it is
|
|
812
|
-
* exactly what 0.5rem + a 16px glyph already measured — CodeBlock's button
|
|
813
|
-
* does not move. */
|
|
814
|
-
width: 32px;
|
|
815
|
-
height: 32px;
|
|
816
|
-
padding: 0;
|
|
817
|
-
/* rungs below; the base stays md so a class used without one still lands
|
|
818
|
-
* on the ladder rather than collapsing to its content */
|
|
809
|
+
padding: 0.5rem;
|
|
819
810
|
background: color-mix(in srgb, var(--kol-surface-on-primary) 2%, transparent);
|
|
820
811
|
border: none;
|
|
821
812
|
border-radius: var(--kol-radius-sm);
|
|
822
|
-
|
|
823
|
-
* over a photograph lets the image through the strokes and reads as mush.
|
|
824
|
-
* oq is a baked grey — same value, nothing bleeds through. */
|
|
825
|
-
color: var(--kol-oq-72);
|
|
813
|
+
color: color-mix(in srgb, var(--kol-surface-on-primary) 72%, transparent);
|
|
826
814
|
cursor: pointer;
|
|
827
815
|
transition: background-color var(--kol-transition-base), color var(--kol-transition-base);
|
|
828
816
|
}
|
|
829
817
|
|
|
830
818
|
.kol-copy-btn:hover {
|
|
831
819
|
background: color-mix(in srgb, var(--kol-surface-on-primary) 8%, transparent);
|
|
832
|
-
/* FULL ink on hover — no mix, no stop below 100% */
|
|
833
820
|
color: var(--kol-surface-on-primary);
|
|
834
821
|
}
|
|
835
822
|
|
|
836
|
-
/*
|
|
837
|
-
*
|
|
838
|
-
.
|
|
839
|
-
|
|
840
|
-
color: var(--kol-surface-on-primary);
|
|
841
|
-
}
|
|
823
|
+
/* ─────────────────────────────────────────────────────────────────────
|
|
824
|
+
* TWO SEPARATE CONTROLS (user ruling 2026-08-15). They shared .kol-copy-btn
|
|
825
|
+
* as a base and every edit to one moved the other; the split is the point.
|
|
826
|
+
* ───────────────────────────────────────────────────────────────────── */
|
|
842
827
|
|
|
843
|
-
/*
|
|
844
|
-
*
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
828
|
+
/* over MEDIA — boxed, opaque plate, full ink. Sits on a photograph, so it
|
|
829
|
+
* needs its own fill to stay legible and never dims at rest. */
|
|
830
|
+
.kol-media-control {
|
|
831
|
+
display: inline-flex;
|
|
832
|
+
align-items: center;
|
|
833
|
+
justify-content: center;
|
|
834
|
+
width: 32px;
|
|
835
|
+
height: 32px;
|
|
836
|
+
padding: 0;
|
|
837
|
+
border: none;
|
|
838
|
+
border-radius: var(--kol-radius-sm);
|
|
839
|
+
background: var(--kol-oq-12);
|
|
854
840
|
color: var(--kol-surface-on-primary);
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
*
|
|
862
|
-
|
|
863
|
-
*
|
|
864
|
-
*
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
*
|
|
871
|
-
|
|
872
|
-
*
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
841
|
+
cursor: pointer;
|
|
842
|
+
/* the house curve, not --kol-transition-base's generic material ease: it
|
|
843
|
+
* settles instead of stopping, which is what reads as smooth on the press. */
|
|
844
|
+
transition: background-color 1500ms var(--kol-ease-bounce), color 1500ms var(--kol-ease-bounce);
|
|
845
|
+
}
|
|
846
|
+
/* hover goes LIGHTER, not darker (user ruling 2026-08-15) — oq mixes ink into
|
|
847
|
+
* the surface, so up the scale is up in brightness. */
|
|
848
|
+
/* the brand anchor, with the theme's own palette as the fallback — the yellow
|
|
849
|
+
* lives in kol-brand-color.css which the consumer supplies, so the theme must
|
|
850
|
+
* not hard-depend on it. NOT --kol-accent-primary, which resolves to white. */
|
|
851
|
+
.kol-media-control:hover {
|
|
852
|
+
background: var(--kol-surface-tertiary);
|
|
853
|
+
color: var(--kol-color-yellow-300, var(--kol-palette-yellow));
|
|
854
|
+
}
|
|
855
|
+
/* The press RAMPS. The rule that is being entered owns the curve going in, so
|
|
856
|
+
* this one carries a slow ease-in-out; the base rule's bounce owns coming back
|
|
857
|
+
* out. One bezier cannot do both — the bounce covers most of its distance in
|
|
858
|
+
* the first quarter, which is exactly why the press read as instant. */
|
|
859
|
+
/* the glyph GROWS on interaction: 18 at rest, 20 on hover and press, inside the
|
|
860
|
+
* unchanged 32px box. The SOLO md rung (20) stays the base and rest scales DOWN
|
|
861
|
+
* to it — 20 × 0.9 = 18 — so the ladder value is still what the component asks
|
|
862
|
+
* for. Scale, not a size swap: the box never moves (2026-07-28 law) and an SVG
|
|
863
|
+
* re-render would not tween. */
|
|
864
|
+
.kol-media-control .kol-action-glyph {
|
|
865
|
+
transform: scale(0.9);
|
|
866
|
+
transition: transform 500ms var(--kol-ease-house);
|
|
867
|
+
}
|
|
868
|
+
.kol-media-control:hover .kol-action-glyph,
|
|
869
|
+
.kol-media-control--pressed .kol-action-glyph {
|
|
870
|
+
transform: scale(1);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
.kol-media-control:active,
|
|
874
|
+
.kol-media-control--pressed {
|
|
875
|
+
background: var(--kol-oq-32);
|
|
876
|
+
transition: background-color 1500ms cubic-bezier(0.65, 0, 0.35, 1);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/* in TEXT — bare, no box, no fill. Rests on the SAME ink as the copy beside it
|
|
880
|
+
* (--kol-fg-meta = fg-48): these sit on a solid plate, not on a photograph, so
|
|
881
|
+
* the transparent scale is safe here and matching the text is worth more than
|
|
882
|
+
* the opaque rung. `--on` is a toggle's held state, not a hover. */
|
|
883
|
+
.kol-inline-control {
|
|
884
|
+
display: inline-flex;
|
|
885
|
+
align-items: center;
|
|
886
|
+
justify-content: center;
|
|
887
|
+
width: 16px;
|
|
888
|
+
height: 16px;
|
|
880
889
|
padding: 0;
|
|
890
|
+
border: none;
|
|
881
891
|
background: transparent;
|
|
892
|
+
color: var(--kol-oq-64);
|
|
893
|
+
cursor: pointer;
|
|
894
|
+
transition: color var(--kol-transition-base);
|
|
882
895
|
}
|
|
883
|
-
|
|
884
|
-
.
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
.kol-
|
|
889
|
-
|
|
890
|
-
.kol-
|
|
891
|
-
|
|
892
|
-
.kol-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
+
/* ONE rest tone for every inline control (user ruling 2026-08-15): the star and
|
|
897
|
+
* the trash beside it sit at the same weight until you reach for one. They part
|
|
898
|
+
* on HOVER — a neutral control brightens, the accent control goes yellow. Yellow
|
|
899
|
+
* is the accent's alone, on hover and on its on-state; spending it on every
|
|
900
|
+
* hover left the on-state with nothing of its own to say. */
|
|
901
|
+
.kol-inline-control:hover { color: var(--kol-oq-96); }
|
|
902
|
+
|
|
903
|
+
.kol-inline-control--accent:hover,
|
|
904
|
+
.kol-inline-control--on,
|
|
905
|
+
.kol-inline-control--on:hover { color: var(--kol-color-yellow-300, var(--kol-palette-yellow)); }
|
|
906
|
+
|
|
907
|
+
/* TWO GLYPHS, not one glyph and a fill (user ruling 2026-08-15: *"just create
|
|
908
|
+
* another star"*, *"leave the one alone in cards"*). `star` stays stroke-only
|
|
909
|
+
* and `star-solid` is the filled twin; a control that has an on-state names
|
|
910
|
+
* both and ActionButton crossfades them.
|
|
911
|
+
*
|
|
912
|
+
* The alternative — filling the shared `star.svg` and unfilling it here — was
|
|
913
|
+
* tried and reverted: a glyph that ships filled renders filled in every
|
|
914
|
+
* consumer that never asked for a state, and the DS cannot see those.
|
|
915
|
+
*
|
|
916
|
+
* The `--on` fill below is kept for a control that ships ONE glyph and carries
|
|
917
|
+
* its state in the class instead; it is a no-op on a stroke-only icon. */
|
|
918
|
+
.kol-inline-control svg path { transition: fill var(--kol-transition-base); }
|
|
919
|
+
.kol-inline-control--on svg path { fill: currentColor; }
|
|
896
920
|
|
|
897
921
|
/* ─────────────────────────────────────────────────────────────────────
|
|
898
922
|
* Figure — atoms/Figure.jsx
|
|
@@ -818,3 +818,149 @@
|
|
|
818
818
|
* (.kol-tag--sm, .kol-sidenav-group, .kol-control-* all set px directly). */
|
|
819
819
|
padding-top: 20px;
|
|
820
820
|
}
|
|
821
|
+
|
|
822
|
+
/* ─────────────────────────────────────────────────────────────────────
|
|
823
|
+
* Content family hover — molecules/ContentCard.jsx · ContentRow.jsx
|
|
824
|
+
*
|
|
825
|
+
* ONE rule, driven by a per-variant custom property the component sets
|
|
826
|
+
* (`--kol-content-hover-bg`). The alternative was a hover utility per
|
|
827
|
+
* variant in JSX, which Tailwind cannot generate from package source
|
|
828
|
+
* anyway (the SegmentedToggle lesson — component chrome belongs in
|
|
829
|
+
* kol-theme). A variant with no hover simply never sets the property,
|
|
830
|
+
* and the rule resolves to nothing.
|
|
831
|
+
*
|
|
832
|
+
* The fill is an oq-* step per 05-control-chrome.md's state model; the
|
|
833
|
+
* value itself is chosen in ContentCard/ContentRow's HOVER table.
|
|
834
|
+
* ───────────────────────────────────────────────────────────────────── */
|
|
835
|
+
@media (hover: hover) {
|
|
836
|
+
.kol-content-hover:hover {
|
|
837
|
+
background-color: var(--kol-content-hover-bg);
|
|
838
|
+
}
|
|
839
|
+
/* border is its OWN class, not a fallback chain off the bg one: a variant
|
|
840
|
+
* that steps its fill but not its frame (catalog) and one that steps its
|
|
841
|
+
* frame but not its fill (work) are both real, and an undefined var in a
|
|
842
|
+
* border-color declaration resolves to `unset` — which inherits, and quietly
|
|
843
|
+
* repaints a border nobody asked to move. */
|
|
844
|
+
.kol-content-hover-frame:hover {
|
|
845
|
+
border-color: var(--kol-content-hover-border);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/* ─────────────────────────────────────────────────────────────────────
|
|
850
|
+
* ContentRow — the responsive step
|
|
851
|
+
*
|
|
852
|
+
* The row publishes its box as custom properties and reads them here, so
|
|
853
|
+
* the md: step is ONE media query instead of per-variant Tailwind variants
|
|
854
|
+
* that Tailwind cannot generate from package source anyway. A variant that
|
|
855
|
+
* publishes no `-md` value falls back to its base and never steps.
|
|
856
|
+
* ───────────────────────────────────────────────────────────────────── */
|
|
857
|
+
.kol-row {
|
|
858
|
+
gap: var(--kol-row-gap);
|
|
859
|
+
padding: var(--kol-row-pad);
|
|
860
|
+
min-height: var(--kol-row-min-h);
|
|
861
|
+
background: var(--kol-row-bg);
|
|
862
|
+
border-color: var(--kol-row-border);
|
|
863
|
+
transition: background-color 300ms var(--kol-ease-house),
|
|
864
|
+
border-color 300ms var(--kol-ease-house);
|
|
865
|
+
}
|
|
866
|
+
.kol-row > .kol-row-thumb {
|
|
867
|
+
width: var(--kol-row-thumb);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/* 768 = the ruled `md` rung (08-breakpoints/01-values.md). A media query
|
|
871
|
+
* cannot read a custom property, so the number is a literal by necessity —
|
|
872
|
+
* naming the rung is the only thing that stops it reading as arbitrary. */
|
|
873
|
+
@media (min-width: 768px) {
|
|
874
|
+
.kol-row {
|
|
875
|
+
gap: var(--kol-row-gap-md, var(--kol-row-gap));
|
|
876
|
+
padding: var(--kol-row-pad-md, var(--kol-row-pad));
|
|
877
|
+
min-height: var(--kol-row-min-h-md, var(--kol-row-min-h));
|
|
878
|
+
}
|
|
879
|
+
.kol-row > .kol-row-thumb {
|
|
880
|
+
width: var(--kol-row-thumb-md, var(--kol-row-thumb));
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/* ─────────────────────────────────────────────────────────────────────
|
|
885
|
+
* ContentCard — the work DRAWER
|
|
886
|
+
*
|
|
887
|
+
* A caption plate that rises over the bottom of the artwork on hover.
|
|
888
|
+
* The card is image-only at rest by design: a work shelf is a wall of
|
|
889
|
+
* images, and the caption is the reveal.
|
|
890
|
+
*
|
|
891
|
+
* Absolute, so it never adds height to the card and never reflows the
|
|
892
|
+
* shelf. Opacity only — a translucent wash over a photograph is exactly
|
|
893
|
+
* what the oq-* tier exists to avoid, but this plate is OPAQUE
|
|
894
|
+
* (surface-inverse) and it is the plate's PRESENCE that animates, not
|
|
895
|
+
* its transparency over the image.
|
|
896
|
+
* ───────────────────────────────────────────────────────────────────── */
|
|
897
|
+
.kol-card-drawer {
|
|
898
|
+
position: absolute;
|
|
899
|
+
inset-inline: 0;
|
|
900
|
+
bottom: 0;
|
|
901
|
+
z-index: 10;
|
|
902
|
+
opacity: 0;
|
|
903
|
+
transition: opacity 300ms var(--kol-ease-house);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
@media (hover: hover) {
|
|
907
|
+
.group:hover > .kol-card-drawer {
|
|
908
|
+
opacity: 1;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/* touch has no hover — the caption must not be unreachable there */
|
|
913
|
+
@media (hover: none) {
|
|
914
|
+
.kol-card-drawer {
|
|
915
|
+
opacity: 1;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/* The card's text plate steps its padding at md where the variant declares
|
|
920
|
+
* one (work: 16 → 24, matching the shipped drawer's `p-4 md:p-6`). Same
|
|
921
|
+
* custom-property mechanism as .kol-row — Tailwind cannot generate an
|
|
922
|
+
* md: variant from package source, and these are per-variant data. */
|
|
923
|
+
/* the ruled `md` rung, same as .kol-row above */
|
|
924
|
+
@media (min-width: 768px) {
|
|
925
|
+
.kol-card-plate {
|
|
926
|
+
padding: var(--kol-plate-pad-md, var(--kol-plate-pad));
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/* The card's rest colours, for the same reason as .kol-row — an inline
|
|
931
|
+
* background/borderColor outranks .kol-content-hover:hover, so the step
|
|
932
|
+
* silently never rendered. */
|
|
933
|
+
.kol-card {
|
|
934
|
+
background: var(--kol-card-bg);
|
|
935
|
+
border-color: var(--kol-card-border);
|
|
936
|
+
transition: background-color 300ms var(--kol-ease-house),
|
|
937
|
+
border-color 300ms var(--kol-ease-house);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/* ─────────────────────────────────────────────────────────────────────
|
|
941
|
+
* The house expand — molecules/SearchInput.jsx · organisms/ContentFilters.jsx
|
|
942
|
+
*
|
|
943
|
+
* A collapsed control that opens to its field: the search glyph widening
|
|
944
|
+
* into an input. Two components hand-wrote the same three declarations
|
|
945
|
+
* inline (`width 600ms`, `background 400ms`, `opacity 300ms`, all on the
|
|
946
|
+
* house curve), which is how the curve came to be hardcoded in seven
|
|
947
|
+
* places to begin with.
|
|
948
|
+
*
|
|
949
|
+
* Durations are STAGGERED on purpose and that is the whole character of
|
|
950
|
+
* the move: the box travels longest (600), its fill settles sooner (400),
|
|
951
|
+
* and the content inside arrives last and quickest (300) so it does not
|
|
952
|
+
* smear across the widening box.
|
|
953
|
+
*
|
|
954
|
+
* The width VALUE stays inline — it is per-instance data (a consumer's
|
|
955
|
+
* expanded width), not chrome. Only the motion lives here.
|
|
956
|
+
* ───────────────────────────────────────────────────────────────────── */
|
|
957
|
+
.kol-expand {
|
|
958
|
+
transition: width 600ms var(--kol-ease-house),
|
|
959
|
+
background 400ms var(--kol-ease-house),
|
|
960
|
+
border-color 400ms var(--kol-ease-house);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/* the content that rides inside an expanding box */
|
|
964
|
+
.kol-expand-content {
|
|
965
|
+
transition: opacity 300ms var(--kol-ease-house);
|
|
966
|
+
}
|
package/kol-theme.css
CHANGED
|
@@ -95,8 +95,18 @@
|
|
|
95
95
|
|
|
96
96
|
/* The house curve (2026-08-15) — the easing the cards actually use; was
|
|
97
97
|
* hardcoded in 5 files before this token existed. Curve only, no duration:
|
|
98
|
-
* it pairs with whatever duration the motion needs.
|
|
99
|
-
|
|
98
|
+
* it pairs with whatever duration the motion needs.
|
|
99
|
+
*
|
|
100
|
+
* BALANCED, not expo (user ruling 2026-08-15). This was easeOutExpo
|
|
101
|
+
* `cubic-bezier(0.16, 1, 0.3, 1)`, which spends ~90% of its distance in the
|
|
102
|
+
* first fifth of the duration and the rest on a settle you cannot see — so
|
|
103
|
+
* every motion read as instant no matter what duration it was given, and a
|
|
104
|
+
* small hover affordance never showed its move at all. */
|
|
105
|
+
--kol-ease-house: cubic-bezier(0.4, 0, 0.2, 1);
|
|
106
|
+
/* the bounce — overshoots past its target and settles back (y > 1 is what
|
|
107
|
+
* makes it bounce; house only decelerates). For a press that should feel
|
|
108
|
+
* springy rather than merely smooth. */
|
|
109
|
+
--kol-ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
100
110
|
|
|
101
111
|
/* Content widths (2026-07-28; panel added 2026-07-30) — ONE frame, three
|
|
102
112
|
* inner caps (chess law). Every page: mx-auto max-w-shell + the one padding
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"description": "KOL (Kolkrabbi) design-system tokens + base CSS — brand-neutral. The canonical token/cascade layer every other KOL package and consumer builds on.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|