@kolkrabbi/kol-theme 0.42.2 → 0.43.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-color.css +14 -0
- package/kol-components-atoms.css +81 -2
- package/kol-components-molecules.css +78 -6
- package/kol-components-organisms.css +13 -0
- package/kol-components-workshop.css +4 -1
- package/kol-theme.css +11 -0
- package/kol-typography.css +8 -2
- package/kol-utilities.css +19 -0
- package/package.json +1 -1
package/kol-color.css
CHANGED
|
@@ -92,6 +92,20 @@
|
|
|
92
92
|
--kol-border-default: color-mix(in srgb, var(--kol-surface-on-primary) 8%, transparent);
|
|
93
93
|
--kol-border-focus: color-mix(in srgb, var(--kol-accent-primary) 70%, var(--kol-surface-on-primary));
|
|
94
94
|
--kol-focus-ring: var(--kol-accent-primary);
|
|
95
|
+
/* The QUIET ring — 1px, inset, for dense rows where the loud ring's 2px
|
|
96
|
+
* offset would bloom over its neighbours (nav rail leaves today).
|
|
97
|
+
*
|
|
98
|
+
* It exists so the pair is a real OFF SWITCH (FocusRingsInConsumers, kol-fxr
|
|
99
|
+
* 2026-08-15, user ruling): --kol-focus-ring alone was only ever half of one,
|
|
100
|
+
* because the rails hardcoded --kol-fg-32 and never read it, so a consumer
|
|
101
|
+
* setting it transparent lost button rings and kept rail rings. Set BOTH to
|
|
102
|
+
* transparent and the system is genuinely off — no `outline: none !important`
|
|
103
|
+
* in consumer CSS, which is the local override this replaces.
|
|
104
|
+
*
|
|
105
|
+
* Default is --kol-fg-32, exactly what the rails already drew, so minting
|
|
106
|
+
* this changes NOTHING visually. Both rings are white (--kol-accent-primary
|
|
107
|
+
* resolves to --kol-surface-on-primary); the two differ in weight, not hue. */
|
|
108
|
+
--kol-focus-ring-quiet: var(--kol-fg-32);
|
|
95
109
|
}
|
|
96
110
|
|
|
97
111
|
:root[data-theme="light"] {
|
package/kol-components-atoms.css
CHANGED
|
@@ -706,6 +706,23 @@
|
|
|
706
706
|
padding: 4px 0 4px;
|
|
707
707
|
}
|
|
708
708
|
|
|
709
|
+
/* The rail leaf had NO focus treatment at all (ThreeColumnEditorShell §3,
|
|
710
|
+
* kol-fxr 2026-08-15) — only .is-active and its dot — so it fell through to
|
|
711
|
+
* the browser's default ring, which a user reported as "a weird highlight bug".
|
|
712
|
+
*
|
|
713
|
+
* INSET 1px, matching its sibling .shell-nav-item (kol-components-workshop.css)
|
|
714
|
+
* rather than .kol-btn's `2px / offset 2px`: an offset ring blooms OUTSIDE a
|
|
715
|
+
* dense nav row and collides with the rows above and below. Same element role,
|
|
716
|
+
* same treatment — this closes an inconsistency, it does not mint a policy.
|
|
717
|
+
*
|
|
718
|
+
* NB this is deliberately NOT an answer to FocusRingsInConsumers, which asks
|
|
719
|
+
* whether a consumer can switch the whole focus system off with one token.
|
|
720
|
+
* That is a live ruling; this rule only stops one leaf being the odd one out. */
|
|
721
|
+
.kol-sidenav-link:focus-visible {
|
|
722
|
+
outline: 1px solid var(--kol-focus-ring-quiet);
|
|
723
|
+
outline-offset: -1px;
|
|
724
|
+
}
|
|
725
|
+
|
|
709
726
|
.kol-sidenav-link.is-active {
|
|
710
727
|
color: var(--kol-surface-on-primary);
|
|
711
728
|
}
|
|
@@ -787,20 +804,82 @@ a:hover .icon-hover,
|
|
|
787
804
|
display: inline-flex;
|
|
788
805
|
align-items: center;
|
|
789
806
|
justify-content: center;
|
|
790
|
-
|
|
807
|
+
/* PINNED SQUARE — the box never moves with glyph size (user law 2026-07-28,
|
|
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 */
|
|
791
819
|
background: color-mix(in srgb, var(--kol-surface-on-primary) 2%, transparent);
|
|
792
820
|
border: none;
|
|
793
821
|
border-radius: var(--kol-radius-sm);
|
|
794
|
-
|
|
822
|
+
/* ink on the OPAQUE scale (user ruling 2026-08-15): a 72%-transparent glyph
|
|
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);
|
|
795
826
|
cursor: pointer;
|
|
796
827
|
transition: background-color var(--kol-transition-base), color var(--kol-transition-base);
|
|
797
828
|
}
|
|
798
829
|
|
|
799
830
|
.kol-copy-btn:hover {
|
|
800
831
|
background: color-mix(in srgb, var(--kol-surface-on-primary) 8%, transparent);
|
|
832
|
+
/* FULL ink on hover — no mix, no stop below 100% */
|
|
801
833
|
color: var(--kol-surface-on-primary);
|
|
802
834
|
}
|
|
803
835
|
|
|
836
|
+
/* the click step, missing since this class was written — hover had a stop,
|
|
837
|
+
* pressed had none, so the button gave no feedback on the way down. */
|
|
838
|
+
.kol-copy-btn:active {
|
|
839
|
+
background: color-mix(in srgb, var(--kol-surface-on-primary) 12%, transparent);
|
|
840
|
+
color: var(--kol-surface-on-primary);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/* --grey's ladder runs DOWN, not up (user ruling 2026-08-15). oq mixes ink
|
|
844
|
+
* INTO the surface, so a higher stop is a LIGHTER box — the wrong direction for
|
|
845
|
+
* a control that has to hold its own against a bright image. Rest is oq-12,
|
|
846
|
+
* hover steps darker to oq-08, and the click lands on --kol-surface-primary
|
|
847
|
+
* itself: fully opaque, no mix, the strongest the scale goes. */
|
|
848
|
+
.kol-copy-btn--grey:hover {
|
|
849
|
+
background: var(--kol-oq-08);
|
|
850
|
+
color: var(--kol-surface-on-primary);
|
|
851
|
+
}
|
|
852
|
+
.kol-copy-btn--grey:active {
|
|
853
|
+
background: var(--kol-surface-primary);
|
|
854
|
+
color: var(--kol-surface-on-primary);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/* --grey — the same control, over MEDIA instead of over a surface
|
|
858
|
+
* (user ruling 2026-08-15). The base rule and every fg-scale variant are
|
|
859
|
+
* TRANSLUCENT, so they are content-dependent: a light wash vanishes on a light
|
|
860
|
+
* photo, a dark scrim vanishes on a dark one. There is no stop that survives
|
|
861
|
+
* both, because the problem is the transparency, not the value.
|
|
862
|
+
*
|
|
863
|
+
* `oq` is the answer and kol-opaque.css:11 already says so — "use oq when you
|
|
864
|
+
* need an OPAQUE grey (cover layer, CHIP OVER A PHOTO, any fill that must hide
|
|
865
|
+
* what's behind it)". It mixes into the surface instead of into transparent, so
|
|
866
|
+
* the control reads identically whatever the image does.
|
|
867
|
+
*
|
|
868
|
+
* The 12 / 16 / 24 ladder is not new — it is exactly .kol-btn-grey's, and
|
|
869
|
+
* `grey` is already this system's name for an oq fill (kol-btn-grey,
|
|
870
|
+
* kol-dd-trigger--grey, kol-icon-frame-grey). Same name, same stops. */
|
|
871
|
+
/* the same three squares as .kol-icon-frame-{sm,md,lg}. The SOLO glyph ladder
|
|
872
|
+
* (16/20/24) pairs 1:1 with them and ActionButton resolves it — the pairing is
|
|
873
|
+
* never re-typed at a call site. */
|
|
874
|
+
.kol-copy-btn-sm { width: 28px; height: 28px; }
|
|
875
|
+
.kol-copy-btn-md { width: 32px; height: 32px; }
|
|
876
|
+
.kol-copy-btn-lg { width: 36px; height: 36px; }
|
|
877
|
+
|
|
878
|
+
.kol-copy-btn--grey {
|
|
879
|
+
background: var(--kol-oq-12);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
|
|
804
883
|
/* ─────────────────────────────────────────────────────────────────────
|
|
805
884
|
* Figure — atoms/Figure.jsx
|
|
806
885
|
* Caption'd media shell for long-form prose (label above, aspect-locked
|
|
@@ -517,8 +517,14 @@
|
|
|
517
517
|
color: var(--kol-fg-emphasis);
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
+
/* Reads the quiet ring token so the segmented strip is part of the system-wide
|
|
521
|
+
* focus off switch (FocusRingsInConsumers, 2026-08-15). This is the LAST
|
|
522
|
+
* hardcoded focus colour in the theme — with it, `--kol-focus-ring` +
|
|
523
|
+
* `--kol-focus-ring-quiet` set to transparent turns every ring off, which is
|
|
524
|
+
* the whole point of the pair. Offset stays -2px: the cell's own inset, not
|
|
525
|
+
* the rails' -1px. */
|
|
520
526
|
.kol-seg-cell:focus-visible {
|
|
521
|
-
outline: 1px solid var(--kol-
|
|
527
|
+
outline: 1px solid var(--kol-focus-ring-quiet);
|
|
522
528
|
outline-offset: -2px;
|
|
523
529
|
}
|
|
524
530
|
|
|
@@ -692,13 +698,51 @@
|
|
|
692
698
|
margin-block-end: 0.75rem;
|
|
693
699
|
}
|
|
694
700
|
|
|
695
|
-
/*
|
|
696
|
-
*
|
|
697
|
-
*
|
|
701
|
+
/* ─────────────────────────────────────────────────────────────────────
|
|
702
|
+
* FRAME CONTROL — a control that floats in a host frame's corner
|
|
703
|
+
* (user ruling 2026-08-15).
|
|
704
|
+
*
|
|
705
|
+
* Position only. The control IS an existing atom — CopyButton (.kol-copy-btn
|
|
706
|
+
* carries the 32×32 look, promoted 2026-08-09) or IconFrame — and this rule
|
|
707
|
+
* says nothing about how it looks, only where it sits.
|
|
708
|
+
*
|
|
709
|
+
* It exists because the same 12px corner was hand-written three times and
|
|
710
|
+
* shared zero times: `.kol-codeblock-copy` as `0.75rem`, and MediaCard twice
|
|
711
|
+
* inline as `absolute top-3 right-3` (download) and `absolute top-3 left-3`
|
|
712
|
+
* (select). Same value, two spellings, three copies — the 4px-five-ways
|
|
713
|
+
* problem one layer up.
|
|
714
|
+
*
|
|
715
|
+
* NOT `overlay`. That word is ruled below (:704) and means the ONE floating
|
|
716
|
+
* surface; this is in-frame chrome, which is a different thing.
|
|
717
|
+
*
|
|
718
|
+
* The inset spells --kol-spacing-3 (12px) rather than a literal, per the
|
|
719
|
+
* tokens law. Default corner is top-right; the modifiers move it.
|
|
720
|
+
*
|
|
721
|
+
* `.kol-codeblock-copy` is kept as a DEPRECATED ALIAS so existing callers do
|
|
722
|
+
* not break — it is the same rule, not a second one. New hosts use
|
|
723
|
+
* `.kol-frame-control`. */
|
|
724
|
+
.kol-frame-control,
|
|
698
725
|
.kol-codeblock-copy {
|
|
699
726
|
position: absolute;
|
|
700
|
-
top:
|
|
701
|
-
right:
|
|
727
|
+
top: var(--kol-spacing-3);
|
|
728
|
+
right: var(--kol-spacing-3);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
.kol-frame-control--top-left {
|
|
732
|
+
right: auto;
|
|
733
|
+
left: var(--kol-spacing-3);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
.kol-frame-control--bottom-right {
|
|
737
|
+
top: auto;
|
|
738
|
+
bottom: var(--kol-spacing-3);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.kol-frame-control--bottom-left {
|
|
742
|
+
top: auto;
|
|
743
|
+
right: auto;
|
|
744
|
+
bottom: var(--kol-spacing-3);
|
|
745
|
+
left: var(--kol-spacing-3);
|
|
702
746
|
}
|
|
703
747
|
|
|
704
748
|
/* ─────────────────────────────────────────────────────────────────────
|
|
@@ -744,3 +788,31 @@
|
|
|
744
788
|
* another product. --kol-shadow-overlay was added for this an hour earlier
|
|
745
789
|
* and is deleted with it: it solved the wrong problem. */
|
|
746
790
|
}
|
|
791
|
+
|
|
792
|
+
/* ── Section — .kol-section-* ─────────────────────────────────────────────
|
|
793
|
+
* Between-siblings rhythm for stacked inspector sections (InspectorSectionRhythm,
|
|
794
|
+
* filed from kol-fxr 2026-08-15).
|
|
795
|
+
*
|
|
796
|
+
* Section's own anatomy — the label and the `gap-2` inner stack — stays as
|
|
797
|
+
* Tailwind utilities in Section.jsx. Only the BETWEEN-siblings rule lives here,
|
|
798
|
+
* because `+` cannot be expressed as a utility class: every consumer stacking
|
|
799
|
+
* sections in a rail was therefore forced to invent a hook class and retype
|
|
800
|
+
* this rule. kol-fxr's `.kol-params-section` block is the evidence, and it
|
|
801
|
+
* dies on adopt.
|
|
802
|
+
*
|
|
803
|
+
* The rule is on the ADJACENT PAIR, not on the element, so the first section
|
|
804
|
+
* in a stack never carries a stray top border and no consumer needs
|
|
805
|
+
* `:not(:first-child)`.
|
|
806
|
+
*
|
|
807
|
+
* DENSITY WAS NOT ADDED. The ticket asked for a `density` prop wanting "a
|
|
808
|
+
* tighter inner gap than gap-2" — but the consumer's own override is
|
|
809
|
+
* `gap: 0.5rem`, which IS `gap-2`. The rule it maintains locally is a no-op
|
|
810
|
+
* against Section's shipped default, so there is no second density to name
|
|
811
|
+
* until a consumer produces a value that actually differs. */
|
|
812
|
+
.kol-section--divided + .kol-section--divided {
|
|
813
|
+
border-top: 1px solid var(--kol-fg-08);
|
|
814
|
+
/* 20px below the rule; the 20px above falls out of the rail body's own
|
|
815
|
+
* gap-5, so a symmetric space–divider–space lands without this file
|
|
816
|
+
* knowing anything about the rail. */
|
|
817
|
+
padding-top: var(--kol-spacing-5);
|
|
818
|
+
}
|
|
@@ -614,3 +614,16 @@
|
|
|
614
614
|
.kol-table-sort svg { opacity: 0.25; transition: opacity var(--kol-transition-fast); }
|
|
615
615
|
.kol-table-sort:hover svg { opacity: 0.6; }
|
|
616
616
|
[aria-sort] .kol-table-sort svg { opacity: 1; }
|
|
617
|
+
|
|
618
|
+
/* ─────────────────────────────────────────────────────────────────────
|
|
619
|
+
* ContentCollection — src/organisms/ContentCollection.jsx (2026-08-15)
|
|
620
|
+
* The enter stagger the collection owns; cards never animate themselves.
|
|
621
|
+
* Delay is set per item inline; the curve is the house token. */
|
|
622
|
+
.kol-collection-item {
|
|
623
|
+
animation: kol-collection-in 300ms var(--kol-ease-house) both;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
@keyframes kol-collection-in {
|
|
627
|
+
from { opacity: 0; transform: translateY(6px); }
|
|
628
|
+
to { opacity: 1; transform: none; }
|
|
629
|
+
}
|
|
@@ -364,8 +364,11 @@
|
|
|
364
364
|
color: var(--kol-surface-on-primary);
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
+
/* Reads the quiet ring token, not --kol-fg-32 directly, so this rail is part
|
|
368
|
+
* of the system-wide focus off switch (FocusRingsInConsumers, 2026-08-15).
|
|
369
|
+
* Same value as before — this is a rewiring, not a restyle. */
|
|
367
370
|
.shell-nav-item:focus-visible {
|
|
368
|
-
outline: 1px solid var(--kol-
|
|
371
|
+
outline: 1px solid var(--kol-focus-ring-quiet);
|
|
369
372
|
outline-offset: -1px;
|
|
370
373
|
}
|
|
371
374
|
|
package/kol-theme.css
CHANGED
|
@@ -87,6 +87,17 @@
|
|
|
87
87
|
* indent instead of retyping it. */
|
|
88
88
|
--kol-pad-rail-row-x: 1.25rem;
|
|
89
89
|
|
|
90
|
+
/* CARD padding (2026-08-15, content-card ruling) — flat, stepped by the
|
|
91
|
+
* card's `size` prop, never by breakpoint. 12/16/24. */
|
|
92
|
+
--kol-pad-card-sm: 0.75rem;
|
|
93
|
+
--kol-pad-card-md: 1rem;
|
|
94
|
+
--kol-pad-card-lg: 1.5rem;
|
|
95
|
+
|
|
96
|
+
/* The house curve (2026-08-15) — the easing the cards actually use; was
|
|
97
|
+
* hardcoded in 5 files before this token existed. Curve only, no duration:
|
|
98
|
+
* it pairs with whatever duration the motion needs. */
|
|
99
|
+
--kol-ease-house: cubic-bezier(0.16, 1, 0.3, 1);
|
|
100
|
+
|
|
90
101
|
/* Content widths (2026-07-28; panel added 2026-07-30) — ONE frame, three
|
|
91
102
|
* inner caps (chess law). Every page: mx-auto max-w-shell + the one padding
|
|
92
103
|
* rhythm, content LEFT-ANCHORED inside. Width is a content decision, never a
|
package/kol-typography.css
CHANGED
|
@@ -815,8 +815,14 @@
|
|
|
815
815
|
font-weight: 600;
|
|
816
816
|
}
|
|
817
817
|
|
|
818
|
-
/*
|
|
819
|
-
*
|
|
818
|
+
/* display-03 shipped 2026-08-15 — its consumer arrived: ContentText's `work`
|
|
819
|
+
* row title (the ruled size-only step down from display-02). */
|
|
820
|
+
.kol-sans-display-03 {
|
|
821
|
+
font-family: var(--kol-font-family-sans-narrow);
|
|
822
|
+
font-size: var(--kol-text-display-03);
|
|
823
|
+
line-height: 100%;
|
|
824
|
+
font-weight: 600;
|
|
825
|
+
}
|
|
820
826
|
|
|
821
827
|
/* =============================================================================
|
|
822
828
|
* DISPLAY TIGHT — the condensed-caps display voice: the Tight cut by NAME
|
package/kol-utilities.css
CHANGED
|
@@ -123,3 +123,22 @@
|
|
|
123
123
|
transition: none;
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
+
|
|
127
|
+
/* Placeholder prose — the ONE gate (GatedEmptyState, kol-fxr 2026-08-15).
|
|
128
|
+
*
|
|
129
|
+
* "Select a layer", "No effects yet", "This layer has no parameters" — prose
|
|
130
|
+
* that narrates a surface the user already understands. The ruling is that it
|
|
131
|
+
* is OFF unless asked for, so the suppression is the DEFAULT and the attribute
|
|
132
|
+
* is the opt-IN; a consumer that never wires the toggle simply never sees it.
|
|
133
|
+
*
|
|
134
|
+
* Written as :not() on the root rather than a display:none + revert pair, so
|
|
135
|
+
* the element keeps whatever display it had — flex, grid, inline — instead of
|
|
136
|
+
* being forced back to block on reveal.
|
|
137
|
+
*
|
|
138
|
+
* The attribute is stamped by usePlaceholders() in kol-component. The class is
|
|
139
|
+
* here, in utilities, because it is not EmptyState's chrome: a consumer puts
|
|
140
|
+
* it on its OWN prose and gets the same switch. That is the whole point — one
|
|
141
|
+
* concept, one gate, not a per-component prop each app re-invents. */
|
|
142
|
+
:root:not([data-kol-placeholders]) .kol-placeholder {
|
|
143
|
+
display: none;
|
|
144
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.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",
|