@kolkrabbi/kol-theme 0.2.0 → 0.4.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/README.md CHANGED
@@ -19,6 +19,8 @@ In a Vite + Tailwind v4 app, import the barrel after Tailwind:
19
19
 
20
20
  Cascade order is load-bearing — `kol-theme` must come after Tailwind so its component classes and tokens resolve correctly. Individual files are also reachable (`@kolkrabbi/kol-theme/kol-color.css`, etc.) if you need finer control.
21
21
 
22
+ All KOL rule CSS lives in the `components` cascade layer, so your Tailwind utility classes always win over KOL chrome (`<Input className="hidden sm:block">` behaves as written). Your own unlayered CSS wins over everything KOL ships.
23
+
22
24
  ## Fonts
23
25
 
24
26
  The typography CSS references the brand fonts at absolute paths (`/fonts/Right-Grotesk/…`, `/fonts/jetbrains-mono/…`). **The package does not ship the font files** — your app must serve them from `/fonts/` (e.g. drop them in your `public/` dir). The showcase in this repo does exactly that. Without them, type falls back to system fonts.
@@ -737,3 +737,114 @@ a:hover .icon-hover,
737
737
  color: var(--kol-surface-on-primary);
738
738
  border-color: var(--kol-fg-48);
739
739
  }
740
+
741
+ /* ─────────────────────────────────────────────────────────────────────
742
+ * Figure — atoms/Figure.jsx
743
+ * Caption'd media shell for long-form prose (label above, aspect-locked
744
+ * frame, figcaption below). Ported from the monorepo prose sheet; the
745
+ * label's text-transform:uppercase was dropped per the KOL no-auto-
746
+ * casing rule — author labels in their final case.
747
+ * ───────────────────────────────────────────────────────────────────── */
748
+ .kol-prose-figure {
749
+ margin-block: 2rem;
750
+ }
751
+
752
+ .kol-caption-label {
753
+ font-family: var(--kol-font-family-mono);
754
+ font-size: 12px;
755
+ font-weight: 500;
756
+ letter-spacing: 0.05em;
757
+ color: color-mix(in srgb, var(--kol-surface-on-primary) 48%, transparent);
758
+ margin-block-end: 0.5rem;
759
+ }
760
+
761
+ .kol-caption-text {
762
+ font-family: var(--kol-font-family-mono);
763
+ font-size: 14px;
764
+ font-weight: 400;
765
+ line-height: 140%;
766
+ color: color-mix(in srgb, var(--kol-surface-on-primary) 64%, transparent);
767
+ margin-block-start: 0.75rem;
768
+ }
769
+
770
+ /* ── target-file: packages/theme/kol-components-atoms.css ──────────────
771
+ * Append directly after the .kol-btn-quiet block
772
+ * (after `.kol-btn-quiet:not(:disabled):hover { opacity: 1; }`). */
773
+
774
+ /* .kol-btn-pressed — toggle-on state (Button `pressed` prop, aria-pressed).
775
+ * Hover-level fill held at rest so a pressed toggle reads as lit. Pairs
776
+ * with `quiet`: Button drops kol-btn-quiet while pressed, giving the
777
+ * tool-palette dim-at-rest → lit-when-active idiom. */
778
+ .kol-btn-pressed {
779
+ background-color: var(--kol-fg-08);
780
+ color: var(--kol-surface-on-primary);
781
+ }
782
+
783
+ /* ─────────────────────────────────────────────────────────────────────
784
+ * Type-specimen kit — TARGET: packages/theme/kol-components-atoms.css
785
+ * (the two .kol-prose-* rules may alternatively land in the prose
786
+ * section of packages/theme/kol-typography.css per the ProsePreview
787
+ * lobby spec — pick one home, not both.)
788
+ *
789
+ * Ported from kol-monorepo apps/brand/src/components/framework/
790
+ * kol-framework.css. NOT ported (already in kol-typography.css, richer
791
+ * DS versions win): .kol-prose pre, .kol-prose code, .kol-prose pre code.
792
+ * Also already in kol-components-atoms.css: .kol-prose-figure,
793
+ * .kol-caption-label, .kol-caption-text.
794
+ * No text-transform rules existed in the source CSS; the components'
795
+ * Tailwind `uppercase` classes were dropped per the KOL no-auto-casing
796
+ * rule — author labels/pullouts in their final case.
797
+ * ───────────────────────────────────────────────────────────────────── */
798
+
799
+ /* ─── TypeSample — atoms/TypeSample.jsx ───
800
+ Wrapper padding inlined in the component; adjacent-sibling hairline
801
+ separates stacked samples. */
802
+ .kol-type-sample + .kol-type-sample {
803
+ border-top: 1px solid var(--kol-fg-08);
804
+ }
805
+
806
+ /* ─── TypeSpecCard — atoms/TypeSpecCard.jsx ───
807
+ Layout inlined in the component; descendant <p> overrides give sample
808
+ prose 16px rhythm (Preflight zeroes <p> margin). */
809
+ .kol-type-spec-sample p { margin: 0 0 16px; }
810
+ .kol-type-spec-sample p:last-child { margin-bottom: 0; }
811
+
812
+ /* ─── ProsePreview — atoms/ProsePreview.jsx ───
813
+ Prose chrome beyond the base .kol-prose contract. */
814
+ .kol-prose-indented {
815
+ padding-left: 32px;
816
+ border-left: 1px solid var(--kol-fg-16);
817
+ }
818
+
819
+ .kol-prose-pullout {
820
+ padding: 16px 0;
821
+ margin: 32px 0;
822
+ border-top: 1px solid var(--kol-fg-08);
823
+ border-bottom: 1px solid var(--kol-fg-08);
824
+ }
825
+
826
+ /* =============================================================================
827
+ * TextPressure (atom)
828
+ * append to packages/theme/kol-components-atoms.css
829
+ *
830
+ * Only the outlined-ghost layer lives here — it needs a ::after pseudo-element
831
+ * and -webkit-text-stroke, neither expressible in Tailwind or inline style.
832
+ * Everything else (layout, sizing, weight) is Tailwind utilities / inline vars
833
+ * on the JSX. Stroke color comes in through the inline `--kol-text-pressure-
834
+ * stroke` custom property (defaulted here so the rule is standalone-safe).
835
+ * ============================================================================= */
836
+
837
+ .kol-text-pressure--stroke .kol-text-pressure-char {
838
+ position: relative;
839
+ }
840
+
841
+ .kol-text-pressure--stroke .kol-text-pressure-char::after {
842
+ content: attr(data-char);
843
+ position: absolute;
844
+ left: 0;
845
+ top: 0;
846
+ color: transparent;
847
+ z-index: -1;
848
+ -webkit-text-stroke-width: 3px;
849
+ -webkit-text-stroke-color: var(--kol-text-pressure-stroke, var(--kol-accent-primary));
850
+ }
@@ -303,3 +303,49 @@
303
303
  outline: 1px solid var(--kol-fg-emphasis);
304
304
  outline-offset: -2px;
305
305
  }
306
+
307
+ /* =============================================================================
308
+ * Color-doc widget chrome — ColorRamp · SpectrumGrid
309
+ *
310
+ * Almost everything is Tailwind/inline in the components; only the ONE bit
311
+ * Tailwind can't express against a dynamic grid template lives here: the
312
+ * SpectrumGrid responsive narrowing. Fold into packages/theme (e.g.
313
+ * kol-components-organisms.css) when these graduate from the lobby.
314
+ *
315
+ * How it works: SpectrumGrid declares its template inline with var() fallbacks —
316
+ * grid-template-columns: var(--kol-sg-label-w, 100px) repeat(N, minmax(0,1fr));
317
+ * gap: var(--kol-sg-gap, 4px);
318
+ * so it renders correctly with NO stylesheet loaded. On narrow viewports we just
319
+ * re-declare those two custom properties on the grid CLASS; the inline var()
320
+ * reads pick them up (the class provides the value, so a media-query override
321
+ * cascades — there's no competing inline *set* of the same property). The
322
+ * per-cell hex readout is hidden separately via Tailwind `max-[900px]:hidden`.
323
+ * ============================================================================= */
324
+
325
+ @media (max-width: 900px) {
326
+ .kol-spectrum-grid {
327
+ --kol-sg-label-w: 72px;
328
+ --kol-sg-gap: 2px;
329
+ }
330
+ }
331
+
332
+ /* SwatchControls chrome — the only bespoke CSS the component needs.
333
+ *
334
+ * The paint chips + sample chip reuse ColorSwatch's `halo` variant for the
335
+ * tuned double-ring, so the ONLY rule left is the NoneMarker's "set to none"
336
+ * affordance: a red diagonal slash on white (a two-stop 45° gradient) over a
337
+ * themed 1px ring. Neither is a Tailwind utility, so it lands here.
338
+ *
339
+ * Append to packages/theme/kol-components-molecules.css. */
340
+ .kol-swatch-none-marker {
341
+ background: linear-gradient(
342
+ 45deg,
343
+ #fff 0%,
344
+ #fff 42%,
345
+ #DC2626 42%,
346
+ #DC2626 58%,
347
+ #fff 58%,
348
+ #fff 100%
349
+ );
350
+ box-shadow: 0 0 0 1px var(--kol-fg-32);
351
+ }
@@ -45,6 +45,17 @@
45
45
  scrollbar-width: none;
46
46
  border: 1px solid var(--kol-fg-08);
47
47
  border-radius: 4px;
48
+ /* Scroll affordance: with the scrollbar hidden, a clipped table reads as
49
+ * cut-off data on narrow viewports. Edge shadows appear only on the
50
+ * scrollable side (surface-colored covers ride the content and mask them
51
+ * at the extremes). */
52
+ background:
53
+ linear-gradient(to right, var(--kol-surface-primary) 40%, transparent) left / 32px 100%,
54
+ linear-gradient(to left, var(--kol-surface-primary) 40%, transparent) right / 32px 100%,
55
+ linear-gradient(to right, var(--kol-fg-16), transparent) left / 14px 100%,
56
+ linear-gradient(to left, var(--kol-fg-16), transparent) right / 14px 100%;
57
+ background-repeat: no-repeat;
58
+ background-attachment: local, local, scroll, scroll;
48
59
  }
49
60
 
50
61
  .kol-table-wrapper::-webkit-scrollbar { display: none; }
@@ -218,3 +229,192 @@
218
229
  padding: 6px 10px;
219
230
  }
220
231
  }
232
+
233
+ /*
234
+ * ─────────────────────────────────────────────────────────────────────
235
+ * FeatureSplit — src/components/organisms/FeatureSplit.jsx
236
+ * ─────────────────────────────────────────────────────────────────────
237
+ *
238
+ * Destined for kol-components-organisms.css.
239
+ *
240
+ * Structural (applied by FeatureSplit.jsx; do not use directly):
241
+ * .kol-feature-split section — surface bg + ink
242
+ * .kol-feature-split-kicker mono eyebrow, accent color
243
+ * .kol-feature-split-pull display pull headline
244
+ * .kol-feature-split-pull em italic accent inside the pull
245
+ * .kol-feature-split-body lede paragraph, 72% ink
246
+ * .kol-feature-split-meta stats strip — hairline top border
247
+ * .kol-feature-split-meta-num stat number
248
+ * .kol-feature-split-meta-label stat label — mono, 48% ink
249
+ * .kol-feature-split-visual img cover-fit media
250
+ * .kol-feature-split-visual-veil bottom gradient (caption legibility)
251
+ * .kol-feature-split-visual-caption mono caption over the media
252
+ *
253
+ * Port notes (from apps/brand kol-site.css `.site-feature*`):
254
+ * - text-transform: uppercase DROPPED from kicker / meta-label / caption —
255
+ * KOL rule: author casing at the call site.
256
+ * - Hardcoded "Right Grotesk" / "Right Grotesk Text" / "Right Grotesk Wide"
257
+ * families → var(--kol-font-family-sans) (this theme tokenizes only
258
+ * sans / sans-narrow / sans-compact; no Text/Wide tokens).
259
+ * - App `--kol-pad-page-*` padding and the shared `.site-*` full-bleed
260
+ * rule moved to Tailwind utilities in the JSX (`fullBleed` prop).
261
+ */
262
+
263
+ /* =============================================================================
264
+ * FeatureSplit
265
+ * ============================================================================= */
266
+
267
+ .kol-feature-split {
268
+ background: var(--kol-surface-primary);
269
+ color: var(--kol-surface-on-primary);
270
+ }
271
+
272
+ .kol-feature-split-kicker {
273
+ font-family: var(--kol-font-family-mono, monospace);
274
+ font-size: 18px;
275
+ font-weight: 600;
276
+ letter-spacing: 0.14em;
277
+ color: var(--kol-accent-primary);
278
+ }
279
+
280
+ .kol-feature-split-pull {
281
+ margin: 0;
282
+ font-family: var(--kol-font-family-sans);
283
+ font-weight: 700;
284
+ font-size: clamp(40px, 5.5vw, 72px);
285
+ line-height: 1.05;
286
+ letter-spacing: -0.02em;
287
+ color: var(--kol-surface-on-primary);
288
+ }
289
+
290
+ .kol-feature-split-pull em {
291
+ font-family: var(--kol-font-family-sans);
292
+ font-feature-settings: "liga", "dlig";
293
+ font-weight: 700;
294
+ font-style: italic;
295
+ color: var(--kol-accent-primary);
296
+ }
297
+
298
+ .kol-feature-split-body {
299
+ margin: 0;
300
+ font-family: var(--kol-font-family-sans);
301
+ font-weight: 400;
302
+ font-size: 16px;
303
+ line-height: 1.6;
304
+ color: color-mix(in srgb, var(--kol-surface-on-primary) 72%, transparent);
305
+ }
306
+
307
+ .kol-feature-split-meta {
308
+ border-top: 1px solid color-mix(in srgb, var(--kol-surface-on-primary) 8%, transparent);
309
+ }
310
+
311
+ .kol-feature-split-meta-num {
312
+ font-family: var(--kol-font-family-sans);
313
+ font-weight: 900;
314
+ font-size: 32px;
315
+ line-height: 1;
316
+ letter-spacing: -0.02em;
317
+ color: var(--kol-surface-on-primary);
318
+ }
319
+
320
+ .kol-feature-split-meta-label {
321
+ font-family: var(--kol-font-family-mono, monospace);
322
+ font-size: 10px;
323
+ font-weight: 600;
324
+ letter-spacing: 0.14em;
325
+ color: color-mix(in srgb, var(--kol-surface-on-primary) 48%, transparent);
326
+ }
327
+
328
+ .kol-feature-split-visual img {
329
+ width: 100%;
330
+ height: 100%;
331
+ object-fit: cover;
332
+ display: block;
333
+ }
334
+
335
+ .kol-feature-split-visual-veil {
336
+ position: absolute;
337
+ inset: 0;
338
+ background: linear-gradient(
339
+ 180deg,
340
+ transparent 0%,
341
+ transparent 60%,
342
+ color-mix(in srgb, var(--kol-surface-primary) 48%, transparent) 100%
343
+ );
344
+ pointer-events: none;
345
+ }
346
+
347
+ .kol-feature-split-visual-caption {
348
+ position: absolute;
349
+ left: 24px;
350
+ bottom: 24px;
351
+ font-family: var(--kol-font-family-mono, monospace);
352
+ font-size: 10px;
353
+ font-weight: 600;
354
+ letter-spacing: 0.14em;
355
+ color: color-mix(in srgb, var(--kol-surface-on-primary) 80%, transparent);
356
+ }
357
+
358
+ /*
359
+ * ─────────────────────────────────────────────────────────────────────
360
+ * FullBleedHero — src/components/organisms/FullBleedHero.jsx
361
+ * ─────────────────────────────────────────────────────────────────────
362
+ *
363
+ * Destined for kol-components-organisms.css.
364
+ *
365
+ * Structural (applied by FullBleedHero.jsx; do not use directly):
366
+ * .kol-full-bleed-hero section root — surface bg behind the media
367
+ * .kol-full-bleed-hero-media cover-fit media layer (Image / HlsVideo /
368
+ * plain <video> / caller-supplied node)
369
+ *
370
+ * Why CSS and not Tailwind for the media layer: the Image molecule bakes
371
+ * `max-w-full h-auto` into every <img>; an inline `h-full` utility can't
372
+ * reliably out-cascade `h-auto` (same utilities layer — stylesheet order,
373
+ * not class order, decides). This unlayered rule wins over Tailwind's
374
+ * layered utilities and pins cover-fill identically for img, video, and
375
+ * caller-supplied media nodes.
376
+ */
377
+
378
+ /* =============================================================================
379
+ * FullBleedHero
380
+ * ============================================================================= */
381
+
382
+ .kol-full-bleed-hero {
383
+ background: var(--kol-surface-primary);
384
+ }
385
+
386
+ .kol-full-bleed-hero-media {
387
+ position: absolute;
388
+ inset: 0;
389
+ width: 100%;
390
+ height: 100%;
391
+ object-fit: cover;
392
+ object-position: center;
393
+ }
394
+
395
+ /* =============================================================================
396
+ * FeaturedCarousel — src/components/organisms/FeaturedCarousel.jsx
397
+ * =============================================================================
398
+ *
399
+ * Append to packages/theme/kol-components-organisms.css.
400
+ *
401
+ * FeaturedCarousel rebuilds on the shared Carousel *core* — it reuses the
402
+ * `.kol-embla*` chrome verbatim (viewport / container / slide / controls /
403
+ * `.is-slides` wide-slide width), so NONE of that is redefined here. Frame,
404
+ * progress bar, and glass positioning are Tailwind utilities on the JSX.
405
+ *
406
+ * The one rule that can't be a utility: the media layer. The Image molecule
407
+ * bakes `h-auto` (a Tailwind layer utility), so an inline `h-full` can't
408
+ * reliably out-cascade it — the same reason FullBleedHero uses a theme rule.
409
+ * This unlayered rule wins and pins image / video / caller-node media as a
410
+ * cover-fit background over the frame.
411
+ */
412
+
413
+ .kol-featured-carousel-media {
414
+ position: absolute;
415
+ inset: 0;
416
+ width: 100%;
417
+ height: 100%;
418
+ object-fit: cover;
419
+ object-position: center;
420
+ }
package/kol-theme.css CHANGED
@@ -25,19 +25,25 @@
25
25
  * (NEW) → kol-utilities.css (curated slice of _new-css/utilities.css)
26
26
  */
27
27
 
28
- @import "./kol-base-tokens.css";
29
- @import "./kol-color.css";
30
- @import "./kol-opacity.css";
31
- @import "./kol-opaque.css";
32
- @import "./kol-typography.css";
33
- @import "./kol-typography-mono.css";
34
- @import "./kol-type-mono-classes.css";
35
- @import "./kol-utilities.css";
36
- @import "./kol-components-atoms.css";
37
- @import "./kol-components-molecules.css";
38
- @import "./kol-components-organisms.css";
28
+ /* Everything KOL ships lives in the `components` cascade layer. Tailwind v4
29
+ * declares `@layer theme, base, components, utilities` — layering here means
30
+ * consumer utility classes (a later layer) can always override KOL chrome,
31
+ * while KOL still beats preflight (`base`). Unlayered consumer CSS wins over
32
+ * all of it, as it should. */
33
+ @import "./kol-base-tokens.css" layer(components);
34
+ @import "./kol-color.css" layer(components);
35
+ @import "./kol-opacity.css" layer(components);
36
+ @import "./kol-opaque.css" layer(components);
37
+ @import "./kol-typography.css" layer(components);
38
+ @import "./kol-typography-mono.css" layer(components);
39
+ @import "./kol-type-mono-classes.css" layer(components);
40
+ @import "./kol-utilities.css" layer(components);
41
+ @import "./kol-components-atoms.css" layer(components);
42
+ @import "./kol-components-molecules.css" layer(components);
43
+ @import "./kol-components-organisms.css" layer(components);
39
44
 
40
45
  /* Theme-level design tokens */
46
+ @layer components {
41
47
  :root {
42
48
  /* Spacing scale (4px base) */
43
49
  --kol-spacing-1: 0.25rem; /* 4px */
@@ -108,3 +114,4 @@
108
114
  --kol-shadow-xl: 0 20px 25px rgba(255, 255, 255, 0.1);
109
115
  --kol-shadow-inner: inset 0 2px 4px rgba(255, 255, 255, 0.04);
110
116
  }
117
+ }
@@ -1088,3 +1088,45 @@
1088
1088
  --font-narrow: 'Right Grotesk Narrow', 'Right Grotesk', sans-serif;
1089
1089
  /* --font-mono lives in kol-typography-mono.css — keeps mono concerns in one file. */
1090
1090
  }
1091
+
1092
+ /* TARGET: packages/theme/kol-typography.css — append to the `.kol-prose` section
1093
+ * ---------------------------------------------------------------------------
1094
+ * PortableTextRenderer (organism) leans on three prose hooks this repo's prose
1095
+ * sheet is missing. Figure's `.kol-prose-figure` / `.kol-caption-label` /
1096
+ * `.kol-caption-text` already exist; these three do not. The renderer degrades
1097
+ * gracefully without them (segmentTitle → plain inline text, caption → normal
1098
+ * paragraph, links → inherited color), so this is polish, not a hard dep.
1099
+ *
1100
+ * NOTE ON PLACEMENT: these are prose *typography* classes, so their home is
1101
+ * kol-typography.css, NOT the kol-components-{atoms,molecules,organisms}.css
1102
+ * component-chrome sheets. Flagged so it lands in the right file.
1103
+ */
1104
+
1105
+ /* inline mark — emphasised run inside a paragraph */
1106
+ .kol-segment-title {
1107
+ font-family: var(--kol-font-family-sans-compact);
1108
+ font-weight: 500;
1109
+ color: var(--kol-surface-on-primary);
1110
+ }
1111
+
1112
+ /* standalone caption block (the `caption` block type) */
1113
+ .kol-prose .caption {
1114
+ margin: -12px 0 24px;
1115
+ font-family: var(--kol-font-family-mono);
1116
+ font-size: 12px;
1117
+ line-height: 16px;
1118
+ letter-spacing: 0.02em;
1119
+ color: var(--kol-fg-48, color-mix(in srgb, var(--kol-surface-on-primary) 48%, transparent));
1120
+ }
1121
+
1122
+ /* inline links (the `link` mark) */
1123
+ .kol-prose a {
1124
+ color: inherit;
1125
+ text-decoration: underline;
1126
+ text-underline-offset: 3px;
1127
+ text-decoration-color: var(--kol-fg-32, color-mix(in srgb, var(--kol-surface-on-primary) 32%, transparent));
1128
+ transition: text-decoration-color 0.15s ease;
1129
+ }
1130
+ .kol-prose a:hover {
1131
+ text-decoration-color: currentColor;
1132
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-theme",
3
- "version": "0.2.0",
3
+ "version": "0.4.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",