@timbal-ai/timbal-react 0.8.1 → 0.8.2
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/CHANGELOG.md +19 -0
- package/README.md +14 -4
- package/dist/app.cjs +220 -35
- package/dist/app.d.cts +1 -1
- package/dist/app.d.ts +1 -1
- package/dist/app.esm.js +3 -1
- package/dist/{chart-artifact-DwfRtQWL.d.ts → chart-artifact-Bl67kre7.d.ts} +60 -11
- package/dist/{chart-artifact-DWkqIAK5.d.cts → chart-artifact-BzcvblDe.d.cts} +60 -11
- package/dist/{chunk-GBBLAM3G.esm.js → chunk-6YVKCVEP.esm.js} +329 -145
- package/dist/index.cjs +220 -35
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@timbal-ai/timbal-react` are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.8.2] — 2026-06-02
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Full theme personalities** — extend programmatic theming beyond brand color to **roundness, shadows, and fonts** in one intent object:
|
|
10
|
+
- **`createTimbalTheme`** — new options: `shadow` (`none` | `hairline` | `soft` | `medium` | `strong`), `typography` (`sans`, optional `display`/`mono`, `importUrl` for web fonts). `radius` now also sets `--radius-2xl` (composer shell). Returns `fontFamily` + `fontImportUrl` for runtime font loading.
|
|
11
|
+
- **`themeToCss(theme, { scope?, includeFontImport? })`** — emits a `font-family: var(--font-sans)` rule when the theme carries a font; optional `@import` for standalone stylesheets.
|
|
12
|
+
- **`ensureThemeFontLink(url)`** — inject/remove a managed font `<link>` in `<head>`.
|
|
13
|
+
- **`applyTimbalTheme` / `TimbalThemeStyle`** — auto-load preset/custom web fonts via `<link>`.
|
|
14
|
+
- **`TIMBAL_THEME_PRESETS`** — each preset is now a full personality (color + radius + shadow + font), not color-only. New presets: **`folio`** (Fraunces serif, sharp corners), **`carbon`** (JetBrains Mono, green accent). Existing presets (`indigo`, `violet`, …) ship distinct fonts and radii.
|
|
15
|
+
- **`ThemePresetGallery`** — shows font name on each swatch card.
|
|
16
|
+
- **`THEME_AGENT_INSTRUCTIONS`** — documents typography, shadow, and font-loading rules for UI-generation agents.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **`TimbalThemePreset`** — adds optional `font` label for pickers.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
5
24
|
## [0.8.1] — 2026-06-02
|
|
6
25
|
|
|
7
26
|
### Added
|
package/README.md
CHANGED
|
@@ -73,20 +73,30 @@ Both light AND dark blocks must be defined for every overridden token — otherw
|
|
|
73
73
|
|
|
74
74
|
### Programmatic theming (no hand-authored OKLCH)
|
|
75
75
|
|
|
76
|
-
Instead of hand-writing paired `:root` / `.dark` blocks, derive a complete,
|
|
76
|
+
Instead of hand-writing paired `:root` / `.dark` blocks, derive a complete **personality** — color, roundness, shadows, and fonts — from a single intent object. The package owns the OKLCH math for every color token (primary, foreground, ring, the full button gradient, the playground tint):
|
|
77
77
|
|
|
78
78
|
```ts
|
|
79
79
|
import { createTimbalTheme, themeToCss, applyTimbalTheme } from "@timbal-ai/timbal-react";
|
|
80
80
|
|
|
81
|
-
const theme = createTimbalTheme({
|
|
81
|
+
const theme = createTimbalTheme({
|
|
82
|
+
brand: "#4f46e5",
|
|
83
|
+
radius: 0.875, // corner roundness (rem) → --radius + --radius-2xl
|
|
84
|
+
shadow: "soft", // "none" | "hairline" | "soft" | "medium" | "strong"
|
|
85
|
+
typography: { // optional — re-skins every component's font
|
|
86
|
+
sans: '"Geist", ui-sans-serif, system-ui, sans-serif',
|
|
87
|
+
importUrl: "https://fonts.googleapis.com/css2?family=Geist:wght@400..600&display=swap",
|
|
88
|
+
},
|
|
89
|
+
});
|
|
82
90
|
|
|
83
91
|
// Build-time / SSR — paste into index.css (paired light + dark, always in sync):
|
|
84
92
|
const css = themeToCss(theme);
|
|
85
93
|
|
|
86
|
-
// Runtime — inject a managed <style
|
|
94
|
+
// Runtime — inject a managed <style> (+ font <link>), swappable, returns a disposer:
|
|
87
95
|
const dispose = applyTimbalTheme(theme);
|
|
88
96
|
```
|
|
89
97
|
|
|
98
|
+
> **Fonts must be loaded.** `applyTimbalTheme` and `TimbalThemeStyle` inject the `<link>` for `typography.importUrl` automatically. For build-time `themeToCss`, add the `<link rel="stylesheet">` to `index.html` yourself (or pass `themeToCss(theme, { includeFontImport: true })` when the result is a standalone stylesheet).
|
|
99
|
+
|
|
90
100
|
Or render it as a component near your app root:
|
|
91
101
|
|
|
92
102
|
```tsx
|
|
@@ -98,7 +108,7 @@ import { TimbalThemeStyle } from "@timbal-ai/timbal-react";
|
|
|
98
108
|
|
|
99
109
|
### Presets + the picker
|
|
100
110
|
|
|
101
|
-
A small closed catalog (`TIMBAL_THEME_PRESETS
|
|
111
|
+
A small closed catalog (`TIMBAL_THEME_PRESETS`) lets you offer styles by stable id and apply on selection. Each preset is a **full personality** (color + radius + shadows + font), not just a color: `platform` (system), `indigo` (Geist), `violet` (Sora), `forest`/`warm` (Lexend), `slate` (Inter), `folio` (Fraunces serif), `carbon` (JetBrains Mono). `ThemePresetGallery` previews each option with real components, scoped so the live app doesn't change until the user picks:
|
|
102
112
|
|
|
103
113
|
```tsx
|
|
104
114
|
import { ThemePresetGallery, applyThemePreset } from "@timbal-ai/timbal-react";
|
package/dist/app.cjs
CHANGED
|
@@ -98,6 +98,7 @@ __export(app_exports, {
|
|
|
98
98
|
clearTimbalTheme: () => clearTimbalTheme,
|
|
99
99
|
connectionRowListClass: () => connectionRowListClass,
|
|
100
100
|
createTimbalTheme: () => createTimbalTheme,
|
|
101
|
+
ensureThemeFontLink: () => ensureThemeFontLink,
|
|
101
102
|
getStoredThemePreset: () => getStoredThemePreset,
|
|
102
103
|
getThemePreset: () => getThemePreset,
|
|
103
104
|
themeToCss: () => themeToCss,
|
|
@@ -431,6 +432,38 @@ function relativeLuminance(color) {
|
|
|
431
432
|
}
|
|
432
433
|
|
|
433
434
|
// src/design/theme.ts
|
|
435
|
+
var SHADOW_PRESETS = {
|
|
436
|
+
none: {
|
|
437
|
+
lightCard: "none",
|
|
438
|
+
lightElevated: "none",
|
|
439
|
+
darkCard: "none",
|
|
440
|
+
darkElevated: "none"
|
|
441
|
+
},
|
|
442
|
+
hairline: {
|
|
443
|
+
lightCard: "0 0 0 1px rgba(15, 23, 42, 0.06)",
|
|
444
|
+
lightElevated: "0 1px 2px rgba(15, 23, 42, 0.06)",
|
|
445
|
+
darkCard: "0 0 0 1px rgba(255, 255, 255, 0.06)",
|
|
446
|
+
darkElevated: "0 2px 8px rgba(0, 0, 0, 0.4)"
|
|
447
|
+
},
|
|
448
|
+
soft: {
|
|
449
|
+
lightCard: "0 1px 2px rgba(15, 23, 42, 0.04)",
|
|
450
|
+
lightElevated: "0 8px 30px rgba(15, 23, 42, 0.07)",
|
|
451
|
+
darkCard: "0 1px 2px rgba(0, 0, 0, 0.3)",
|
|
452
|
+
darkElevated: "0 10px 34px rgba(0, 0, 0, 0.45)"
|
|
453
|
+
},
|
|
454
|
+
medium: {
|
|
455
|
+
lightCard: "0 1px 2px -0.5px rgba(0, 0, 0, 0.05)",
|
|
456
|
+
lightElevated: "0 4px 24px rgba(0, 0, 0, 0.06)",
|
|
457
|
+
darkCard: "0 1px 3px rgba(0, 0, 0, 0.22)",
|
|
458
|
+
darkElevated: "0 4px 24px rgba(0, 0, 0, 0.35)"
|
|
459
|
+
},
|
|
460
|
+
strong: {
|
|
461
|
+
lightCard: "0 2px 6px rgba(15, 23, 42, 0.10)",
|
|
462
|
+
lightElevated: "0 16px 48px rgba(15, 23, 42, 0.16)",
|
|
463
|
+
darkCard: "0 2px 6px rgba(0, 0, 0, 0.4)",
|
|
464
|
+
darkElevated: "0 18px 50px rgba(0, 0, 0, 0.6)"
|
|
465
|
+
}
|
|
466
|
+
};
|
|
434
467
|
function primaryForMode(brand, mode) {
|
|
435
468
|
if (mode === "light") {
|
|
436
469
|
return { ...brand, l: Math.min(Math.max(brand.l, 0.42), 0.68) };
|
|
@@ -466,8 +499,26 @@ function createTimbalTheme(intent) {
|
|
|
466
499
|
const light = {};
|
|
467
500
|
const dark = {};
|
|
468
501
|
const root = {};
|
|
502
|
+
let fontFamily;
|
|
503
|
+
let fontImportUrl;
|
|
469
504
|
if (typeof intent.radius === "number") {
|
|
470
505
|
root["--radius"] = `${intent.radius}rem`;
|
|
506
|
+
root["--radius-2xl"] = `${Math.max(intent.radius + 0.25, 0)}rem`;
|
|
507
|
+
}
|
|
508
|
+
if (intent.typography) {
|
|
509
|
+
const { sans, display, mono, importUrl } = intent.typography;
|
|
510
|
+
root["--font-sans"] = sans;
|
|
511
|
+
if (display) root["--font-display"] = display;
|
|
512
|
+
if (mono) root["--font-mono"] = mono;
|
|
513
|
+
fontFamily = sans;
|
|
514
|
+
fontImportUrl = importUrl;
|
|
515
|
+
}
|
|
516
|
+
if (intent.shadow) {
|
|
517
|
+
const s = SHADOW_PRESETS[intent.shadow];
|
|
518
|
+
light["--shadow-card-value"] = s.lightCard;
|
|
519
|
+
light["--shadow-card-elevated-value"] = s.lightElevated;
|
|
520
|
+
dark["--shadow-card-value"] = s.darkCard;
|
|
521
|
+
dark["--shadow-card-elevated-value"] = s.darkElevated;
|
|
471
522
|
}
|
|
472
523
|
const primaryLight = primaryForMode(brand, "light");
|
|
473
524
|
const primaryDark = primaryForMode(brand, "dark");
|
|
@@ -565,7 +616,7 @@ function createTimbalTheme(intent) {
|
|
|
565
616
|
);
|
|
566
617
|
}
|
|
567
618
|
}
|
|
568
|
-
return { light, dark, root };
|
|
619
|
+
return { light, dark, root, fontFamily, fontImportUrl };
|
|
569
620
|
}
|
|
570
621
|
function declarations(map, indent) {
|
|
571
622
|
return Object.entries(map).map(([name, value]) => `${indent}${name}: ${value};`).join("\n");
|
|
@@ -588,6 +639,11 @@ ${declarations(theme.dark, indent)}
|
|
|
588
639
|
}`
|
|
589
640
|
);
|
|
590
641
|
}
|
|
642
|
+
if (theme.fontFamily) {
|
|
643
|
+
blocks.push(`${sel} {
|
|
644
|
+
${indent}font-family: var(--font-sans);
|
|
645
|
+
}`);
|
|
646
|
+
}
|
|
591
647
|
} else {
|
|
592
648
|
if (Object.keys(lightVars).length) {
|
|
593
649
|
blocks.push(`:root {
|
|
@@ -597,15 +653,45 @@ ${declarations(lightVars, indent)}
|
|
|
597
653
|
if (Object.keys(theme.dark).length) {
|
|
598
654
|
blocks.push(`.dark {
|
|
599
655
|
${declarations(theme.dark, indent)}
|
|
656
|
+
}`);
|
|
657
|
+
}
|
|
658
|
+
if (theme.fontFamily) {
|
|
659
|
+
blocks.push(`:root,
|
|
660
|
+
body {
|
|
661
|
+
${indent}font-family: var(--font-sans);
|
|
600
662
|
}`);
|
|
601
663
|
}
|
|
602
664
|
}
|
|
603
|
-
|
|
665
|
+
const css = blocks.join("\n\n");
|
|
666
|
+
if (options.includeFontImport && theme.fontImportUrl) {
|
|
667
|
+
return `@import url("${theme.fontImportUrl}");
|
|
668
|
+
|
|
669
|
+
${css}`;
|
|
670
|
+
}
|
|
671
|
+
return css;
|
|
604
672
|
}
|
|
605
673
|
var RUNTIME_STYLE_ID = "timbal-theme-runtime";
|
|
674
|
+
var FONT_LINK_ATTR = "data-timbal-theme-font";
|
|
675
|
+
function ensureThemeFontLink(url) {
|
|
676
|
+
if (typeof document === "undefined") return;
|
|
677
|
+
const existing = document.head.querySelector(
|
|
678
|
+
`link[${FONT_LINK_ATTR}]`
|
|
679
|
+
);
|
|
680
|
+
if (!url) {
|
|
681
|
+
existing?.remove();
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
if (existing?.getAttribute("href") === url) return;
|
|
685
|
+
const link = existing ?? document.createElement("link");
|
|
686
|
+
link.rel = "stylesheet";
|
|
687
|
+
link.href = url;
|
|
688
|
+
link.setAttribute(FONT_LINK_ATTR, "");
|
|
689
|
+
if (!existing) document.head.appendChild(link);
|
|
690
|
+
}
|
|
606
691
|
function applyTimbalTheme(theme) {
|
|
607
692
|
if (typeof document === "undefined") return () => {
|
|
608
693
|
};
|
|
694
|
+
ensureThemeFontLink(theme.fontImportUrl);
|
|
609
695
|
let el = document.getElementById(RUNTIME_STYLE_ID);
|
|
610
696
|
if (!el) {
|
|
611
697
|
el = document.createElement("style");
|
|
@@ -616,11 +702,13 @@ function applyTimbalTheme(theme) {
|
|
|
616
702
|
el.textContent = themeToCss(theme);
|
|
617
703
|
return () => {
|
|
618
704
|
el?.parentNode?.removeChild(el);
|
|
705
|
+
ensureThemeFontLink(void 0);
|
|
619
706
|
};
|
|
620
707
|
}
|
|
621
708
|
function clearTimbalTheme() {
|
|
622
709
|
if (typeof document === "undefined") return;
|
|
623
710
|
document.getElementById(RUNTIME_STYLE_ID)?.remove();
|
|
711
|
+
ensureThemeFontLink(void 0);
|
|
624
712
|
}
|
|
625
713
|
function isDev() {
|
|
626
714
|
if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") {
|
|
@@ -671,48 +759,122 @@ var STORAGE_KEYS = {
|
|
|
671
759
|
|
|
672
760
|
// src/design/theme-presets.ts
|
|
673
761
|
var EMPTY_TOKENS = { light: {}, dark: {}, root: {} };
|
|
762
|
+
var FONT_URL = {
|
|
763
|
+
geist: "https://fonts.googleapis.com/css2?family=Geist:wght@400..600&display=swap",
|
|
764
|
+
sora: "https://fonts.googleapis.com/css2?family=Sora:wght@400..600&display=swap",
|
|
765
|
+
lexend: "https://fonts.googleapis.com/css2?family=Lexend:wght@400..600&display=swap",
|
|
766
|
+
inter: "https://fonts.googleapis.com/css2?family=Inter:wght@400..600&display=swap",
|
|
767
|
+
fraunces: "https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400..600&display=swap",
|
|
768
|
+
jetbrains: "https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400..600&display=swap"
|
|
769
|
+
};
|
|
770
|
+
var STACK = {
|
|
771
|
+
geist: '"Geist", ui-sans-serif, system-ui, sans-serif',
|
|
772
|
+
sora: '"Sora", ui-sans-serif, system-ui, sans-serif',
|
|
773
|
+
lexend: '"Lexend", ui-sans-serif, system-ui, sans-serif',
|
|
774
|
+
inter: '"Inter", ui-sans-serif, system-ui, sans-serif',
|
|
775
|
+
fraunces: '"Fraunces", ui-serif, Georgia, "Times New Roman", serif',
|
|
776
|
+
jetbrains: '"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace'
|
|
777
|
+
};
|
|
674
778
|
var TIMBAL_THEME_PRESETS = [
|
|
675
779
|
{
|
|
676
780
|
id: "platform",
|
|
677
781
|
label: "Platform",
|
|
678
|
-
description: "Shipped neutral monochrome \u2014 the Timbal Platform default. Calm, brand-agnostic.",
|
|
782
|
+
description: "Shipped neutral monochrome \u2014 the Timbal Platform default. Calm, brand-agnostic, system font.",
|
|
679
783
|
swatch: "oklch(0.205 0 0)",
|
|
784
|
+
font: null,
|
|
680
785
|
tokens: EMPTY_TOKENS
|
|
681
786
|
},
|
|
682
787
|
{
|
|
683
788
|
id: "indigo",
|
|
684
789
|
label: "Indigo",
|
|
685
|
-
description: "Cool, trustworthy blue-violet \u2014
|
|
790
|
+
description: "Cool, trustworthy blue-violet, Geist type, generous radius, soft shadows \u2014 analytics & ops dashboards.",
|
|
686
791
|
swatch: "#4f46e5",
|
|
687
|
-
|
|
792
|
+
font: "Geist",
|
|
793
|
+
tokens: createTimbalTheme({
|
|
794
|
+
brand: "#4f46e5",
|
|
795
|
+
radius: 0.875,
|
|
796
|
+
shadow: "soft",
|
|
797
|
+
typography: { sans: STACK.geist, importUrl: FONT_URL.geist }
|
|
798
|
+
})
|
|
688
799
|
},
|
|
689
800
|
{
|
|
690
801
|
id: "violet",
|
|
691
802
|
label: "Violet",
|
|
692
|
-
description: "Vivid purple \u2014 expressive
|
|
803
|
+
description: "Vivid purple, Sora type, rounded, soft shadows \u2014 expressive product / marketing surfaces.",
|
|
693
804
|
swatch: "#7c3aed",
|
|
694
|
-
|
|
805
|
+
font: "Sora",
|
|
806
|
+
tokens: createTimbalTheme({
|
|
807
|
+
brand: "#7c3aed",
|
|
808
|
+
radius: 1,
|
|
809
|
+
shadow: "soft",
|
|
810
|
+
typography: { sans: STACK.sora, importUrl: FONT_URL.sora }
|
|
811
|
+
})
|
|
695
812
|
},
|
|
696
813
|
{
|
|
697
814
|
id: "forest",
|
|
698
815
|
label: "Forest",
|
|
699
|
-
description: "Grounded green \u2014 finance, sustainability, status-positive apps.",
|
|
816
|
+
description: "Grounded green, Lexend type, compact radius \u2014 finance, sustainability, status-positive apps.",
|
|
700
817
|
swatch: "#16a34a",
|
|
701
|
-
|
|
818
|
+
font: "Lexend",
|
|
819
|
+
tokens: createTimbalTheme({
|
|
820
|
+
brand: "#16a34a",
|
|
821
|
+
radius: 0.625,
|
|
822
|
+
shadow: "soft",
|
|
823
|
+
typography: { sans: STACK.lexend, importUrl: FONT_URL.lexend }
|
|
824
|
+
})
|
|
702
825
|
},
|
|
703
826
|
{
|
|
704
827
|
id: "warm",
|
|
705
828
|
label: "Warm",
|
|
706
|
-
description: "Energetic orange \u2014 consumer, creative, high-engagement tools.",
|
|
829
|
+
description: "Energetic orange, Lexend type, friendly radius \u2014 consumer, creative, high-engagement tools.",
|
|
707
830
|
swatch: "#ea580c",
|
|
708
|
-
|
|
831
|
+
font: "Lexend",
|
|
832
|
+
tokens: createTimbalTheme({
|
|
833
|
+
brand: "#ea580c",
|
|
834
|
+
radius: 0.875,
|
|
835
|
+
shadow: "soft",
|
|
836
|
+
typography: { sans: STACK.lexend, importUrl: FONT_URL.lexend }
|
|
837
|
+
})
|
|
709
838
|
},
|
|
710
839
|
{
|
|
711
840
|
id: "slate",
|
|
712
841
|
label: "Slate",
|
|
713
|
-
description: "Muted
|
|
842
|
+
description: "Muted enterprise gray-blue, Inter type, tight radius, hairline shadows, tinted neutrals.",
|
|
714
843
|
swatch: "#475569",
|
|
715
|
-
|
|
844
|
+
font: "Inter",
|
|
845
|
+
tokens: createTimbalTheme({
|
|
846
|
+
brand: "#475569",
|
|
847
|
+
radius: 0.5,
|
|
848
|
+
shadow: "hairline",
|
|
849
|
+
tintNeutrals: true,
|
|
850
|
+
typography: { sans: STACK.inter, importUrl: FONT_URL.inter }
|
|
851
|
+
})
|
|
852
|
+
},
|
|
853
|
+
{
|
|
854
|
+
id: "folio",
|
|
855
|
+
label: "Folio",
|
|
856
|
+
description: "Editorial serif (Fraunces), near-sharp corners, hairline shadows \u2014 content / docs / reports.",
|
|
857
|
+
swatch: "#9a3412",
|
|
858
|
+
font: "Fraunces",
|
|
859
|
+
tokens: createTimbalTheme({
|
|
860
|
+
brand: "#9a3412",
|
|
861
|
+
radius: 0.25,
|
|
862
|
+
shadow: "hairline",
|
|
863
|
+
typography: { sans: STACK.fraunces, importUrl: FONT_URL.fraunces }
|
|
864
|
+
})
|
|
865
|
+
},
|
|
866
|
+
{
|
|
867
|
+
id: "carbon",
|
|
868
|
+
label: "Carbon",
|
|
869
|
+
description: "Terminal monospace (JetBrains Mono), crisp corners, green accent \u2014 developer / infra tools.",
|
|
870
|
+
swatch: "#15803d",
|
|
871
|
+
font: "JetBrains Mono",
|
|
872
|
+
tokens: createTimbalTheme({
|
|
873
|
+
brand: "#15803d",
|
|
874
|
+
radius: 0.375,
|
|
875
|
+
shadow: "hairline",
|
|
876
|
+
typography: { sans: STACK.jetbrains, importUrl: FONT_URL.jetbrains }
|
|
877
|
+
})
|
|
716
878
|
}
|
|
717
879
|
];
|
|
718
880
|
var PRESET_BY_ID = new Map(
|
|
@@ -753,18 +915,29 @@ The package ships a complete light + dark token system (\`styles.css\`). Compone
|
|
|
753
915
|
|
|
754
916
|
**Never write \`oklch(...)\` / hex literals or hand-author paired \`:root\` + \`.dark\` blocks.** Express intent and let the package derive a complete, contrast-correct, paired palette.
|
|
755
917
|
|
|
756
|
-
###
|
|
918
|
+
### Generate a full personality (color + roundness + fonts + shadows)
|
|
757
919
|
|
|
758
920
|
\`\`\`ts
|
|
759
921
|
import { createTimbalTheme, themeToCss } from "@timbal-ai/timbal-react";
|
|
760
922
|
|
|
761
|
-
const theme = createTimbalTheme({
|
|
762
|
-
|
|
763
|
-
|
|
923
|
+
const theme = createTimbalTheme({
|
|
924
|
+
brand: "#4f46e5",
|
|
925
|
+
radius: 0.875, // corner roundness in rem (sets --radius + --radius-2xl)
|
|
926
|
+
shadow: "soft", // "none" | "hairline" | "soft" | "medium" | "strong"
|
|
927
|
+
tintNeutrals: false, // tint background/border toward the brand hue
|
|
928
|
+
accent: "#10b981", // optional secondary accent
|
|
929
|
+
typography: { // optional \u2014 re-skins every component's font
|
|
930
|
+
sans: '"Geist", ui-sans-serif, system-ui, sans-serif',
|
|
931
|
+
importUrl: "https://fonts.googleapis.com/css2?family=Geist:wght@400..600&display=swap",
|
|
932
|
+
// display?, mono? also supported
|
|
933
|
+
},
|
|
934
|
+
});
|
|
935
|
+
const css = themeToCss(theme); // paired light + dark, guaranteed in sync
|
|
764
936
|
\`\`\`
|
|
765
937
|
|
|
766
|
-
-
|
|
767
|
-
-
|
|
938
|
+
- \`createTimbalTheme\` derives \`--primary\`, its foreground, ring, the full button gradient, and a soft playground tint from \`brand\`. \`radius\` sets roundness, \`shadow\` sets card depth, \`typography\` sets fonts. You only supply intent \u2014 never raw OKLCH.
|
|
939
|
+
- For a real company, look up the actual brand hex first (brandfetch / "<company> brand color hex").
|
|
940
|
+
- **Web fonts must be loaded.** \`applyTimbalTheme\` / \`TimbalThemeStyle\` inject the \`<link>\` for \`typography.importUrl\` automatically. For build-time \`themeToCss\`, add the \`<link rel="stylesheet" href="\u2026">\` to your \`index.html\` yourself (or pass \`themeToCss(theme, { includeFontImport: true })\` when the result is a standalone stylesheet).
|
|
768
941
|
|
|
769
942
|
### Apply a theme
|
|
770
943
|
|
|
@@ -781,14 +954,18 @@ import { TIMBAL_THEME_PRESETS, applyThemePreset } from "@timbal-ai/timbal-react"
|
|
|
781
954
|
// TIMBAL_THEME_PRESETS: { id, label, description, swatch, tokens }[]
|
|
782
955
|
\`\`\`
|
|
783
956
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
|
787
|
-
|
|
788
|
-
| \`
|
|
789
|
-
| \`
|
|
790
|
-
| \`
|
|
791
|
-
| \`
|
|
957
|
+
Each preset is a **full personality** (color + radius + shadows + font), not just a color:
|
|
958
|
+
|
|
959
|
+
| Preset id | Personality |
|
|
960
|
+
|-----------|-------------|
|
|
961
|
+
| \`platform\` | Neutral monochrome, system font (the default \u2014 no brand) |
|
|
962
|
+
| \`indigo\` | Blue-violet, Geist, generous radius, soft shadows \u2014 analytics / ops |
|
|
963
|
+
| \`violet\` | Purple, Sora, rounded \u2014 product / marketing |
|
|
964
|
+
| \`forest\` | Green, Lexend, compact \u2014 finance / sustainability |
|
|
965
|
+
| \`warm\` | Orange, Lexend, friendly \u2014 consumer / creative |
|
|
966
|
+
| \`slate\` | Enterprise gray-blue, Inter, tight radius, hairline shadows |
|
|
967
|
+
| \`folio\` | Editorial serif (Fraunces), near-sharp corners \u2014 content / docs |
|
|
968
|
+
| \`carbon\` | Terminal monospace (JetBrains Mono), green accent \u2014 dev / infra |
|
|
792
969
|
|
|
793
970
|
- To present options visually, render \`<ThemePresetGallery value={id} onSelect={setId} />\` \u2014 each swatch previews real components (Button + metric tile) scoped via \`data-timbal-theme\`, so the live app doesn't change until the user picks.
|
|
794
971
|
- On selection, call \`applyThemePreset(id)\` (persists to \`localStorage\` and restores on reload).
|
|
@@ -812,14 +989,17 @@ var TimbalThemeStyle = ({
|
|
|
812
989
|
if (!tokens) return null;
|
|
813
990
|
const css = themeToCss(tokens, scope ? { scope } : void 0);
|
|
814
991
|
if (!css) return null;
|
|
815
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
816
|
-
"
|
|
817
|
-
|
|
818
|
-
"
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
992
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
993
|
+
tokens.fontImportUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("link", { rel: "stylesheet", href: tokens.fontImportUrl }) : null,
|
|
994
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
995
|
+
"style",
|
|
996
|
+
{
|
|
997
|
+
"data-timbal-theme-style": scope ?? "root",
|
|
998
|
+
nonce,
|
|
999
|
+
dangerouslySetInnerHTML: { __html: css }
|
|
1000
|
+
}
|
|
1001
|
+
)
|
|
1002
|
+
] });
|
|
823
1003
|
};
|
|
824
1004
|
|
|
825
1005
|
// src/utils.ts
|
|
@@ -1403,6 +1583,10 @@ var ThemePresetGallery = ({
|
|
|
1403
1583
|
selected ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-xs font-medium text-primary", children: "Selected" }) : null
|
|
1404
1584
|
] }),
|
|
1405
1585
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-xs leading-snug text-muted-foreground", children: preset.description }),
|
|
1586
|
+
preset.font ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: [
|
|
1587
|
+
"Aa \xB7 ",
|
|
1588
|
+
preset.font
|
|
1589
|
+
] }) : null,
|
|
1406
1590
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-2 rounded-lg border border-border bg-background p-2", children: [
|
|
1407
1591
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1408
1592
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { size: "xs", className: "pointer-events-none", children: "Primary" }),
|
|
@@ -7446,6 +7630,7 @@ function TimbalChat({
|
|
|
7446
7630
|
clearTimbalTheme,
|
|
7447
7631
|
connectionRowListClass,
|
|
7448
7632
|
createTimbalTheme,
|
|
7633
|
+
ensureThemeFontLink,
|
|
7449
7634
|
getStoredThemePreset,
|
|
7450
7635
|
getThemePreset,
|
|
7451
7636
|
themeToCss,
|
package/dist/app.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as APP_KIT_AGENT_INSTRUCTIONS, a as AppChatPanel, b as AppChatPanelProps, c as AppConfirmDialog, d as AppConfirmDialogProps, e as AppCopilotContextValue, f as AppCopilotProvider, g as AppCopilotProviderProps, h as AppShell, i as AppShellChatControls, j as AppShellChatTrigger, k as AppShellChatTriggerProps, l as AppShellProps, m as AppShellTopbar, n as AppShellTopbarProps, B as BreadcrumbItem, o as Breadcrumbs, p as BreadcrumbsProps, C as CHART_PALETTE, q as ChartArtifactView, r as ChartLayout, s as ChartPanel, t as ChartPanelProps, u as ChartSeries, v as ChartVariant, w as ConnectionRow, x as ConnectionRowList, y as ConnectionRowListProps, z as ConnectionRowProps, D as DangerZone, E as DangerZoneAction, F as DangerZoneActionProps, G as DangerZoneProps, H as DataTable, I as DataTableColumn, J as DataTableProps, K as DataTableSort, L as DataTableSortDirection, M as DescriptionItem, N as DescriptionList, O as DescriptionListProps, P as EmptyState, Q as EmptyStateProps, R as ExpandableSection, S as ExpandableSectionProps, T as Field, U as FieldInput, V as FieldInputProps, W as FieldProps, X as FieldRow, Y as FieldRowProps, Z as FieldSelect, _ as FieldSelectProps, $ as FieldSwitch, a0 as FieldSwitchProps, a1 as FieldTextarea, a2 as FieldTextareaProps, a3 as FilterBar, a4 as FilterBarProps, a5 as FloatingUnsavedChangesBar, a6 as FloatingUnsavedChangesBarProps, a7 as FormSection, a8 as FormSectionProps, a9 as INTEGRATION_CATALOG_CARD_HEIGHT_CLASS, aa as InfoCard, ab as InfoCardProps, ac as InfoCardTone, ad as IntegrationCard, ae as IntegrationCardProps, af as IntegrationCardStatus, ag as IntegrationsEmptyState, ah as IntegrationsEmptyStateProps, ai as LineAreaChart, aj as LineAreaChartProps, ak as MetricChartCard, al as MetricChartCardProps, am as MetricChartMetric, an as MetricRow, ao as MetricRowItem, ap as MetricRowProps, aq as MetricTile, ar as MetricTileProps, as as Page, at as PageHeader, au as PageHeaderProps, av as PageProps, aw as PlanBadge, ax as PlanBadgeProps, ay as PlanBadgeTone, az as ResourceCard, aA as ResourceCardProps, aB as SearchInput, aC as SearchInputProps, aD as Section, aE as SectionProps, aF as SettingsSection, aG as SettingsSectionHeader, aH as SettingsSectionHeaderProps, aI as SettingsSectionProps, aJ as Sparkline, aK as SparklineProps, aL as StatTile, aM as StatTileProps, aN as StatusBadge, aO as StatusBadgeProps, aP as StatusBadgeTone, aQ as StatusDot, aR as StatusDotProps, aS as StatusDotTone, aT as SubNav, aU as SubNavItem, aV as SubNavProps, aW as SurfaceCard, aX as SurfaceCardProps, aY as THEME_AGENT_INSTRUCTIONS, aZ as TIMBAL_THEME_PRESETS, a_ as ThemePresetGallery, a$ as ThemePresetGalleryProps, b0 as
|
|
1
|
+
export { A as APP_KIT_AGENT_INSTRUCTIONS, a as AppChatPanel, b as AppChatPanelProps, c as AppConfirmDialog, d as AppConfirmDialogProps, e as AppCopilotContextValue, f as AppCopilotProvider, g as AppCopilotProviderProps, h as AppShell, i as AppShellChatControls, j as AppShellChatTrigger, k as AppShellChatTriggerProps, l as AppShellProps, m as AppShellTopbar, n as AppShellTopbarProps, B as BreadcrumbItem, o as Breadcrumbs, p as BreadcrumbsProps, C as CHART_PALETTE, q as ChartArtifactView, r as ChartLayout, s as ChartPanel, t as ChartPanelProps, u as ChartSeries, v as ChartVariant, w as ConnectionRow, x as ConnectionRowList, y as ConnectionRowListProps, z as ConnectionRowProps, D as DangerZone, E as DangerZoneAction, F as DangerZoneActionProps, G as DangerZoneProps, H as DataTable, I as DataTableColumn, J as DataTableProps, K as DataTableSort, L as DataTableSortDirection, M as DescriptionItem, N as DescriptionList, O as DescriptionListProps, P as EmptyState, Q as EmptyStateProps, R as ExpandableSection, S as ExpandableSectionProps, T as Field, U as FieldInput, V as FieldInputProps, W as FieldProps, X as FieldRow, Y as FieldRowProps, Z as FieldSelect, _ as FieldSelectProps, $ as FieldSwitch, a0 as FieldSwitchProps, a1 as FieldTextarea, a2 as FieldTextareaProps, a3 as FilterBar, a4 as FilterBarProps, a5 as FloatingUnsavedChangesBar, a6 as FloatingUnsavedChangesBarProps, a7 as FormSection, a8 as FormSectionProps, a9 as INTEGRATION_CATALOG_CARD_HEIGHT_CLASS, aa as InfoCard, ab as InfoCardProps, ac as InfoCardTone, ad as IntegrationCard, ae as IntegrationCardProps, af as IntegrationCardStatus, ag as IntegrationsEmptyState, ah as IntegrationsEmptyStateProps, ai as LineAreaChart, aj as LineAreaChartProps, ak as MetricChartCard, al as MetricChartCardProps, am as MetricChartMetric, an as MetricRow, ao as MetricRowItem, ap as MetricRowProps, aq as MetricTile, ar as MetricTileProps, as as Page, at as PageHeader, au as PageHeaderProps, av as PageProps, aw as PlanBadge, ax as PlanBadgeProps, ay as PlanBadgeTone, az as ResourceCard, aA as ResourceCardProps, aB as SearchInput, aC as SearchInputProps, aD as Section, aE as SectionProps, aF as SettingsSection, aG as SettingsSectionHeader, aH as SettingsSectionHeaderProps, aI as SettingsSectionProps, aJ as Sparkline, aK as SparklineProps, aL as StatTile, aM as StatTileProps, aN as StatusBadge, aO as StatusBadgeProps, aP as StatusBadgeTone, aQ as StatusDot, aR as StatusDotProps, aS as StatusDotTone, aT as SubNav, aU as SubNavItem, aV as SubNavProps, aW as SurfaceCard, aX as SurfaceCardProps, aY as THEME_AGENT_INSTRUCTIONS, aZ as TIMBAL_THEME_PRESETS, a_ as ThemePresetGallery, a$ as ThemePresetGalleryProps, b0 as ThemeShadow, b1 as ThemeToCssOptions, b2 as ThemeTokenMap, b3 as TimbalThemeIntent, b4 as TimbalThemePreset, b5 as TimbalThemePresetId, b6 as TimbalThemeStyle, b7 as TimbalThemeStyleProps, b8 as TimbalThemeTokens, b9 as TimbalThemeTypography, ba as applyThemePreset, bb as applyTimbalTheme, bc as clearTimbalTheme, bd as connectionRowListClass, be as createTimbalTheme, bf as ensureThemeFontLink, bg as getStoredThemePreset, bh as getThemePreset, bi as themeToCss, bj as useAppCopilotContext, bk as useAppShellChat } from './chart-artifact-BzcvblDe.cjs';
|
|
2
2
|
export { B as Button } from './button-ClSgD6OF.cjs';
|
|
3
3
|
export { C as ChartArtifact, T as ThreadVariant, a as TimbalChat, b as TimbalChatProps } from './chat-Bed4FQSl.cjs';
|
|
4
4
|
import 'react';
|
package/dist/app.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as APP_KIT_AGENT_INSTRUCTIONS, a as AppChatPanel, b as AppChatPanelProps, c as AppConfirmDialog, d as AppConfirmDialogProps, e as AppCopilotContextValue, f as AppCopilotProvider, g as AppCopilotProviderProps, h as AppShell, i as AppShellChatControls, j as AppShellChatTrigger, k as AppShellChatTriggerProps, l as AppShellProps, m as AppShellTopbar, n as AppShellTopbarProps, B as BreadcrumbItem, o as Breadcrumbs, p as BreadcrumbsProps, C as CHART_PALETTE, q as ChartArtifactView, r as ChartLayout, s as ChartPanel, t as ChartPanelProps, u as ChartSeries, v as ChartVariant, w as ConnectionRow, x as ConnectionRowList, y as ConnectionRowListProps, z as ConnectionRowProps, D as DangerZone, E as DangerZoneAction, F as DangerZoneActionProps, G as DangerZoneProps, H as DataTable, I as DataTableColumn, J as DataTableProps, K as DataTableSort, L as DataTableSortDirection, M as DescriptionItem, N as DescriptionList, O as DescriptionListProps, P as EmptyState, Q as EmptyStateProps, R as ExpandableSection, S as ExpandableSectionProps, T as Field, U as FieldInput, V as FieldInputProps, W as FieldProps, X as FieldRow, Y as FieldRowProps, Z as FieldSelect, _ as FieldSelectProps, $ as FieldSwitch, a0 as FieldSwitchProps, a1 as FieldTextarea, a2 as FieldTextareaProps, a3 as FilterBar, a4 as FilterBarProps, a5 as FloatingUnsavedChangesBar, a6 as FloatingUnsavedChangesBarProps, a7 as FormSection, a8 as FormSectionProps, a9 as INTEGRATION_CATALOG_CARD_HEIGHT_CLASS, aa as InfoCard, ab as InfoCardProps, ac as InfoCardTone, ad as IntegrationCard, ae as IntegrationCardProps, af as IntegrationCardStatus, ag as IntegrationsEmptyState, ah as IntegrationsEmptyStateProps, ai as LineAreaChart, aj as LineAreaChartProps, ak as MetricChartCard, al as MetricChartCardProps, am as MetricChartMetric, an as MetricRow, ao as MetricRowItem, ap as MetricRowProps, aq as MetricTile, ar as MetricTileProps, as as Page, at as PageHeader, au as PageHeaderProps, av as PageProps, aw as PlanBadge, ax as PlanBadgeProps, ay as PlanBadgeTone, az as ResourceCard, aA as ResourceCardProps, aB as SearchInput, aC as SearchInputProps, aD as Section, aE as SectionProps, aF as SettingsSection, aG as SettingsSectionHeader, aH as SettingsSectionHeaderProps, aI as SettingsSectionProps, aJ as Sparkline, aK as SparklineProps, aL as StatTile, aM as StatTileProps, aN as StatusBadge, aO as StatusBadgeProps, aP as StatusBadgeTone, aQ as StatusDot, aR as StatusDotProps, aS as StatusDotTone, aT as SubNav, aU as SubNavItem, aV as SubNavProps, aW as SurfaceCard, aX as SurfaceCardProps, aY as THEME_AGENT_INSTRUCTIONS, aZ as TIMBAL_THEME_PRESETS, a_ as ThemePresetGallery, a$ as ThemePresetGalleryProps, b0 as
|
|
1
|
+
export { A as APP_KIT_AGENT_INSTRUCTIONS, a as AppChatPanel, b as AppChatPanelProps, c as AppConfirmDialog, d as AppConfirmDialogProps, e as AppCopilotContextValue, f as AppCopilotProvider, g as AppCopilotProviderProps, h as AppShell, i as AppShellChatControls, j as AppShellChatTrigger, k as AppShellChatTriggerProps, l as AppShellProps, m as AppShellTopbar, n as AppShellTopbarProps, B as BreadcrumbItem, o as Breadcrumbs, p as BreadcrumbsProps, C as CHART_PALETTE, q as ChartArtifactView, r as ChartLayout, s as ChartPanel, t as ChartPanelProps, u as ChartSeries, v as ChartVariant, w as ConnectionRow, x as ConnectionRowList, y as ConnectionRowListProps, z as ConnectionRowProps, D as DangerZone, E as DangerZoneAction, F as DangerZoneActionProps, G as DangerZoneProps, H as DataTable, I as DataTableColumn, J as DataTableProps, K as DataTableSort, L as DataTableSortDirection, M as DescriptionItem, N as DescriptionList, O as DescriptionListProps, P as EmptyState, Q as EmptyStateProps, R as ExpandableSection, S as ExpandableSectionProps, T as Field, U as FieldInput, V as FieldInputProps, W as FieldProps, X as FieldRow, Y as FieldRowProps, Z as FieldSelect, _ as FieldSelectProps, $ as FieldSwitch, a0 as FieldSwitchProps, a1 as FieldTextarea, a2 as FieldTextareaProps, a3 as FilterBar, a4 as FilterBarProps, a5 as FloatingUnsavedChangesBar, a6 as FloatingUnsavedChangesBarProps, a7 as FormSection, a8 as FormSectionProps, a9 as INTEGRATION_CATALOG_CARD_HEIGHT_CLASS, aa as InfoCard, ab as InfoCardProps, ac as InfoCardTone, ad as IntegrationCard, ae as IntegrationCardProps, af as IntegrationCardStatus, ag as IntegrationsEmptyState, ah as IntegrationsEmptyStateProps, ai as LineAreaChart, aj as LineAreaChartProps, ak as MetricChartCard, al as MetricChartCardProps, am as MetricChartMetric, an as MetricRow, ao as MetricRowItem, ap as MetricRowProps, aq as MetricTile, ar as MetricTileProps, as as Page, at as PageHeader, au as PageHeaderProps, av as PageProps, aw as PlanBadge, ax as PlanBadgeProps, ay as PlanBadgeTone, az as ResourceCard, aA as ResourceCardProps, aB as SearchInput, aC as SearchInputProps, aD as Section, aE as SectionProps, aF as SettingsSection, aG as SettingsSectionHeader, aH as SettingsSectionHeaderProps, aI as SettingsSectionProps, aJ as Sparkline, aK as SparklineProps, aL as StatTile, aM as StatTileProps, aN as StatusBadge, aO as StatusBadgeProps, aP as StatusBadgeTone, aQ as StatusDot, aR as StatusDotProps, aS as StatusDotTone, aT as SubNav, aU as SubNavItem, aV as SubNavProps, aW as SurfaceCard, aX as SurfaceCardProps, aY as THEME_AGENT_INSTRUCTIONS, aZ as TIMBAL_THEME_PRESETS, a_ as ThemePresetGallery, a$ as ThemePresetGalleryProps, b0 as ThemeShadow, b1 as ThemeToCssOptions, b2 as ThemeTokenMap, b3 as TimbalThemeIntent, b4 as TimbalThemePreset, b5 as TimbalThemePresetId, b6 as TimbalThemeStyle, b7 as TimbalThemeStyleProps, b8 as TimbalThemeTokens, b9 as TimbalThemeTypography, ba as applyThemePreset, bb as applyTimbalTheme, bc as clearTimbalTheme, bd as connectionRowListClass, be as createTimbalTheme, bf as ensureThemeFontLink, bg as getStoredThemePreset, bh as getThemePreset, bi as themeToCss, bj as useAppCopilotContext, bk as useAppShellChat } from './chart-artifact-Bl67kre7.js';
|
|
2
2
|
export { B as Button } from './button-ClSgD6OF.js';
|
|
3
3
|
export { C as ChartArtifact, T as ThreadVariant, a as TimbalChat, b as TimbalChatProps } from './chat-Bed4FQSl.js';
|
|
4
4
|
import 'react';
|
package/dist/app.esm.js
CHANGED
|
@@ -62,12 +62,13 @@ import {
|
|
|
62
62
|
clearTimbalTheme,
|
|
63
63
|
connectionRowListClass,
|
|
64
64
|
createTimbalTheme,
|
|
65
|
+
ensureThemeFontLink,
|
|
65
66
|
getStoredThemePreset,
|
|
66
67
|
getThemePreset,
|
|
67
68
|
themeToCss,
|
|
68
69
|
useAppCopilotContext,
|
|
69
70
|
useAppShellChat
|
|
70
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-6YVKCVEP.esm.js";
|
|
71
72
|
import "./chunk-QIABF4KB.esm.js";
|
|
72
73
|
import {
|
|
73
74
|
CHART_PALETTE,
|
|
@@ -148,6 +149,7 @@ export {
|
|
|
148
149
|
clearTimbalTheme,
|
|
149
150
|
connectionRowListClass,
|
|
150
151
|
createTimbalTheme,
|
|
152
|
+
ensureThemeFontLink,
|
|
151
153
|
getStoredThemePreset,
|
|
152
154
|
getThemePreset,
|
|
153
155
|
themeToCss,
|