@bundu/ui 0.1.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/LICENSE +21 -0
- package/README.md +97 -0
- package/package.json +79 -0
- package/src/Breadcrumb.astro +105 -0
- package/src/Container.astro +39 -0
- package/src/Hero.astro +101 -0
- package/src/Icon.astro +50 -0
- package/src/MineralStrip.astro +51 -0
- package/src/Section.astro +59 -0
- package/src/SectionHeader.astro +73 -0
- package/src/SocialIcon.astro +166 -0
- package/src/breadcrumbs.ts +83 -0
- package/src/index.ts +5 -0
- package/src/lib/utils.ts +44 -0
- package/src/ui/alert.tsx +99 -0
- package/src/ui/avatar.tsx +69 -0
- package/src/ui/badge.tsx +58 -0
- package/src/ui/button.tsx +114 -0
- package/src/ui/card.tsx +63 -0
- package/src/ui/checkbox.tsx +40 -0
- package/src/ui/input.tsx +34 -0
- package/src/ui/label.tsx +41 -0
- package/src/ui/select.tsx +41 -0
- package/src/ui/separator.tsx +45 -0
- package/src/ui/skeleton.tsx +30 -0
- package/src/ui/switch.tsx +101 -0
- package/src/ui/tabs.tsx +199 -0
- package/src/ui/textarea.tsx +34 -0
- package/src/ui/tooltip.tsx +78 -0
- package/styles/brand-bundu.css +10 -0
- package/styles/brand-mukoko.css +10 -0
- package/styles/brand-nyuchi.css +10 -0
- package/styles/globals.css +358 -0
- package/tailwind-preset.mjs +177 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Tooltip — a minimal accessible tooltip.
|
|
9
|
+
*
|
|
10
|
+
* Deliberately NOT Radix (kept dependency-light): wraps a single
|
|
11
|
+
* trigger and shows `content` on hover AND keyboard focus (so it's not
|
|
12
|
+
* hover-only), wiring `aria-describedby` to the tooltip's generated id
|
|
13
|
+
* and `role="tooltip"`. Escape dismisses it. Positioning is CSS-only
|
|
14
|
+
* (absolute, `side` prop) — no floating-ui. Colours come from the
|
|
15
|
+
* semantic `foreground` / `background` tokens — no hex.
|
|
16
|
+
*
|
|
17
|
+
* The trigger must be a single focusable element (button/link). The
|
|
18
|
+
* wrapper is an inline span so it doesn't disturb layout.
|
|
19
|
+
*/
|
|
20
|
+
export interface TooltipProps {
|
|
21
|
+
/** Tooltip text (or rich node) shown on hover/focus. */
|
|
22
|
+
content: React.ReactNode;
|
|
23
|
+
side?: "top" | "bottom" | "left" | "right";
|
|
24
|
+
class?: string;
|
|
25
|
+
className?: string;
|
|
26
|
+
children: React.ReactNode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const sideClasses: Record<NonNullable<TooltipProps["side"]>, string> = {
|
|
30
|
+
top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
|
|
31
|
+
bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
|
|
32
|
+
left: "right-full top-1/2 -translate-y-1/2 mr-2",
|
|
33
|
+
right: "left-full top-1/2 -translate-y-1/2 ml-2",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function Tooltip({
|
|
37
|
+
content,
|
|
38
|
+
side = "top",
|
|
39
|
+
class: astroClass,
|
|
40
|
+
className,
|
|
41
|
+
children,
|
|
42
|
+
}: TooltipProps) {
|
|
43
|
+
const [open, setOpen] = React.useState(false);
|
|
44
|
+
const id = React.useId();
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<span
|
|
48
|
+
className="relative inline-flex"
|
|
49
|
+
data-slot="tooltip"
|
|
50
|
+
onMouseEnter={() => setOpen(true)}
|
|
51
|
+
onMouseLeave={() => setOpen(false)}
|
|
52
|
+
onFocusCapture={() => setOpen(true)}
|
|
53
|
+
onBlurCapture={() => setOpen(false)}
|
|
54
|
+
onKeyDown={(e) => {
|
|
55
|
+
if (e.key === "Escape") setOpen(false);
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
<span aria-describedby={open ? id : undefined} className="inline-flex">
|
|
59
|
+
{children}
|
|
60
|
+
</span>
|
|
61
|
+
<span
|
|
62
|
+
role="tooltip"
|
|
63
|
+
id={id}
|
|
64
|
+
hidden={!open}
|
|
65
|
+
data-slot="tooltip-content"
|
|
66
|
+
data-state={open ? "open" : "closed"}
|
|
67
|
+
className={cn(
|
|
68
|
+
"pointer-events-none absolute z-50 w-max max-w-xs rounded-md bg-foreground px-2.5 py-1.5 text-caption font-medium text-background shadow-md",
|
|
69
|
+
sideClasses[side],
|
|
70
|
+
astroClass,
|
|
71
|
+
className,
|
|
72
|
+
)}
|
|
73
|
+
>
|
|
74
|
+
{content}
|
|
75
|
+
</span>
|
|
76
|
+
</span>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* brand-bundu — Bundu Foundation primary: terracotta.
|
|
2
|
+
Import AFTER globals.css to select this brand's primary + ring. */
|
|
3
|
+
:root {
|
|
4
|
+
--primary: var(--color-terracotta);
|
|
5
|
+
--ring: var(--color-terracotta);
|
|
6
|
+
}
|
|
7
|
+
.dark {
|
|
8
|
+
--primary: var(--color-terracotta);
|
|
9
|
+
--ring: var(--color-terracotta);
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* brand-mukoko — Mukoko primary: tanzanite.
|
|
2
|
+
Import AFTER globals.css to select this brand's primary + ring. */
|
|
3
|
+
:root {
|
|
4
|
+
--primary: var(--color-tanzanite);
|
|
5
|
+
--ring: var(--color-tanzanite);
|
|
6
|
+
}
|
|
7
|
+
.dark {
|
|
8
|
+
--primary: var(--color-tanzanite);
|
|
9
|
+
--ring: var(--color-tanzanite);
|
|
10
|
+
}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
@layer base {
|
|
2
|
+
:root {
|
|
3
|
+
/* === Seven African Minerals — light mode (canonical from mzizi.dev) === */
|
|
4
|
+
--color-cobalt: #0047ab;
|
|
5
|
+
--color-cobalt-container: #e3f2fd;
|
|
6
|
+
--color-cobalt-on-container: #002966;
|
|
7
|
+
|
|
8
|
+
--color-tanzanite: #4b0082;
|
|
9
|
+
--color-tanzanite-container: #f3e5f5;
|
|
10
|
+
--color-tanzanite-on-container: #2e004d;
|
|
11
|
+
|
|
12
|
+
--color-malachite: #004d40;
|
|
13
|
+
--color-malachite-container: #e0f2f1;
|
|
14
|
+
--color-malachite-on-container: #00332b;
|
|
15
|
+
|
|
16
|
+
--color-gold: #5d4037;
|
|
17
|
+
--color-gold-container: #fff8e1;
|
|
18
|
+
--color-gold-on-container: #3e2723;
|
|
19
|
+
|
|
20
|
+
--color-terracotta: #a0522d;
|
|
21
|
+
--color-terracotta-container: #f5e6d3;
|
|
22
|
+
--color-terracotta-on-container: #5d2906;
|
|
23
|
+
|
|
24
|
+
--color-sodalite: #283593;
|
|
25
|
+
--color-sodalite-container: #e8eaf6;
|
|
26
|
+
--color-sodalite-on-container: #141a5c;
|
|
27
|
+
|
|
28
|
+
--color-copper: #bf5a36;
|
|
29
|
+
--color-copper-container: #fbe4da;
|
|
30
|
+
--color-copper-on-container: #5c2410;
|
|
31
|
+
|
|
32
|
+
/* === Semantic tokens — light (canonical from mzizi.dev) === */
|
|
33
|
+
--background: #faf9f4; /* warm cream canvas */
|
|
34
|
+
--canvas: #faf9f4;
|
|
35
|
+
--foreground: #1a1a17; /* near-black ink */
|
|
36
|
+
--ink: #1a1a17;
|
|
37
|
+
|
|
38
|
+
--card: #ffffff;
|
|
39
|
+
--card-foreground: #1a1a17;
|
|
40
|
+
--popover: #ffffff;
|
|
41
|
+
--popover-foreground: #1a1a17;
|
|
42
|
+
|
|
43
|
+
/* Brand primary: cobalt is the canonical Mzizi / Design MCP default.
|
|
44
|
+
Each brand site overrides --primary and --ring to its own mineral
|
|
45
|
+
via the brand-*.css overlay imported after this file. */
|
|
46
|
+
--primary: var(--color-cobalt);
|
|
47
|
+
--primary-foreground: #ffffff;
|
|
48
|
+
|
|
49
|
+
--secondary: #f4f2ec;
|
|
50
|
+
--secondary-foreground: #1a1a17;
|
|
51
|
+
|
|
52
|
+
--muted: #f4f2ec;
|
|
53
|
+
--muted-foreground: #5d5c57;
|
|
54
|
+
|
|
55
|
+
--accent: var(--color-cobalt-container);
|
|
56
|
+
--accent-foreground: var(--color-cobalt-on-container);
|
|
57
|
+
|
|
58
|
+
--destructive: #b3261e;
|
|
59
|
+
--destructive-foreground: #ffffff;
|
|
60
|
+
--destructive-container: #fdeded;
|
|
61
|
+
|
|
62
|
+
--border: #e7e5e0; /* warm stone */
|
|
63
|
+
--input: #ffffff;
|
|
64
|
+
--ring: var(--color-cobalt);
|
|
65
|
+
|
|
66
|
+
--success: var(--color-malachite);
|
|
67
|
+
--warning: #7a5c00;
|
|
68
|
+
--error: #b3261e;
|
|
69
|
+
--info: var(--color-cobalt);
|
|
70
|
+
|
|
71
|
+
/* Touch targets */
|
|
72
|
+
--touch-target: 56px;
|
|
73
|
+
--touch-target-sm: 48px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.dark {
|
|
77
|
+
--color-cobalt: #00b0ff;
|
|
78
|
+
--color-cobalt-container: #001f3f;
|
|
79
|
+
--color-cobalt-on-container: #b3e5fc;
|
|
80
|
+
|
|
81
|
+
--color-tanzanite: #b388ff;
|
|
82
|
+
--color-tanzanite-container: #1a0033;
|
|
83
|
+
--color-tanzanite-on-container: #e1bee7;
|
|
84
|
+
|
|
85
|
+
--color-malachite: #64ffda;
|
|
86
|
+
--color-malachite-container: #00251a;
|
|
87
|
+
--color-malachite-on-container: #a7ffeb;
|
|
88
|
+
|
|
89
|
+
--color-gold: #ffd740;
|
|
90
|
+
--color-gold-container: #332200;
|
|
91
|
+
--color-gold-on-container: #ffecb3;
|
|
92
|
+
|
|
93
|
+
--color-terracotta: #e1b07e;
|
|
94
|
+
--color-terracotta-container: #3e2817;
|
|
95
|
+
--color-terracotta-on-container: #f5e6d3;
|
|
96
|
+
|
|
97
|
+
--color-sodalite: #3d5afe;
|
|
98
|
+
--color-sodalite-container: #0d1442;
|
|
99
|
+
--color-sodalite-on-container: #c5cae9;
|
|
100
|
+
|
|
101
|
+
--color-copper: #ff8a65;
|
|
102
|
+
--color-copper-container: #3a1a0e;
|
|
103
|
+
--color-copper-on-container: #ffd3c2;
|
|
104
|
+
|
|
105
|
+
--background: #100f0e;
|
|
106
|
+
--canvas: #100f0e;
|
|
107
|
+
--foreground: #f0efe9;
|
|
108
|
+
--ink: #f0efe9;
|
|
109
|
+
|
|
110
|
+
--card: #1a1917;
|
|
111
|
+
--card-foreground: #f0efe9;
|
|
112
|
+
--popover: #1a1917;
|
|
113
|
+
--popover-foreground: #f0efe9;
|
|
114
|
+
|
|
115
|
+
--primary: var(--color-cobalt);
|
|
116
|
+
--primary-foreground: #1a1917;
|
|
117
|
+
|
|
118
|
+
--secondary: #2a2927;
|
|
119
|
+
--secondary-foreground: #f0efe9;
|
|
120
|
+
|
|
121
|
+
--muted: #2a2927;
|
|
122
|
+
--muted-foreground: #a8a6a0;
|
|
123
|
+
|
|
124
|
+
--accent: var(--color-cobalt-container);
|
|
125
|
+
--accent-foreground: var(--color-cobalt-on-container);
|
|
126
|
+
|
|
127
|
+
--destructive: #f2b8b5;
|
|
128
|
+
--destructive-foreground: #1a1917;
|
|
129
|
+
--destructive-container: #3e1818;
|
|
130
|
+
|
|
131
|
+
--border: #2a2927;
|
|
132
|
+
--input: #100f0e;
|
|
133
|
+
--ring: var(--color-cobalt);
|
|
134
|
+
|
|
135
|
+
--success: var(--color-malachite);
|
|
136
|
+
--warning: #ffd866;
|
|
137
|
+
--error: #f2b8b5;
|
|
138
|
+
--info: var(--color-cobalt);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
html {
|
|
142
|
+
scroll-behavior: smooth;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
body {
|
|
146
|
+
background-color: var(--background);
|
|
147
|
+
color: var(--foreground);
|
|
148
|
+
font-family: "Noto Sans", system-ui, sans-serif;
|
|
149
|
+
@apply antialiased;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
h1,
|
|
153
|
+
h2,
|
|
154
|
+
h3,
|
|
155
|
+
h4,
|
|
156
|
+
h5,
|
|
157
|
+
h6 {
|
|
158
|
+
font-family: "Noto Serif", Georgia, serif;
|
|
159
|
+
color: var(--foreground);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
code,
|
|
163
|
+
pre,
|
|
164
|
+
kbd,
|
|
165
|
+
samp {
|
|
166
|
+
font-family: "JetBrains Mono", ui-monospace, monospace;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
*:focus-visible {
|
|
170
|
+
outline: 2px solid var(--ring);
|
|
171
|
+
outline-offset: 2px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* Selection */
|
|
175
|
+
::selection {
|
|
176
|
+
background-color: var(--color-cobalt-container);
|
|
177
|
+
color: var(--color-cobalt-on-container);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@layer components {
|
|
182
|
+
/* Containers — Anthropic-style narrow editorial column + wide grid */
|
|
183
|
+
.container-custom {
|
|
184
|
+
@apply max-w-7xl mx-auto px-6 sm:px-8 lg:px-10;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.container-narrow {
|
|
188
|
+
@apply max-w-narrow mx-auto px-6 sm:px-8;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.container-prose {
|
|
192
|
+
@apply max-w-3xl mx-auto px-6 sm:px-8;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Pill buttons — design system mandate */
|
|
196
|
+
.btn-primary {
|
|
197
|
+
@apply inline-flex items-center justify-center gap-2 px-6 h-12
|
|
198
|
+
bg-primary text-primary-foreground font-medium rounded-full
|
|
199
|
+
transition-all duration-200 ease-soft
|
|
200
|
+
hover:opacity-90
|
|
201
|
+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background
|
|
202
|
+
disabled:opacity-50 disabled:cursor-not-allowed;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.btn-secondary {
|
|
206
|
+
@apply inline-flex items-center justify-center gap-2 px-6 h-12
|
|
207
|
+
bg-foreground text-background font-medium rounded-full
|
|
208
|
+
transition-all duration-200 ease-soft
|
|
209
|
+
hover:opacity-90
|
|
210
|
+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background
|
|
211
|
+
disabled:opacity-50 disabled:cursor-not-allowed;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.btn-outline {
|
|
215
|
+
@apply inline-flex items-center justify-center gap-2 px-6 h-12
|
|
216
|
+
border border-foreground text-foreground font-medium rounded-full
|
|
217
|
+
transition-all duration-200 ease-soft
|
|
218
|
+
hover:bg-foreground hover:text-background
|
|
219
|
+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background
|
|
220
|
+
disabled:opacity-50 disabled:cursor-not-allowed;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.btn-ghost {
|
|
224
|
+
@apply inline-flex items-center justify-center gap-2 px-4 h-10
|
|
225
|
+
text-foreground font-medium rounded-full
|
|
226
|
+
transition-colors duration-200 ease-soft
|
|
227
|
+
hover:bg-muted
|
|
228
|
+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background
|
|
229
|
+
disabled:opacity-50 disabled:cursor-not-allowed;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Cards */
|
|
233
|
+
.card {
|
|
234
|
+
@apply bg-card text-card-foreground rounded-lg
|
|
235
|
+
border border-border;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.card-hover {
|
|
239
|
+
@apply bg-card text-card-foreground rounded-lg border border-border hover:shadow-sm transition-all duration-200 ease-soft;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* Sections */
|
|
243
|
+
.section {
|
|
244
|
+
@apply py-16 md:py-24;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.section-tight {
|
|
248
|
+
@apply py-12 md:py-16;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.section-loose {
|
|
252
|
+
@apply py-20 md:py-32;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.section-title {
|
|
256
|
+
@apply font-serif text-h1 text-foreground mb-4 text-balance;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.section-subtitle {
|
|
260
|
+
@apply text-body-lg text-muted-foreground max-w-2xl text-pretty;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.eyebrow {
|
|
264
|
+
@apply inline-block text-caption font-semibold uppercase tracking-wider text-muted-foreground mb-4;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* Badges — pill */
|
|
268
|
+
.badge {
|
|
269
|
+
@apply inline-flex items-center px-3 py-1 rounded-full text-caption font-medium;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.badge-primary {
|
|
273
|
+
@apply inline-flex items-center px-3 py-1 rounded-full text-caption font-medium bg-cobalt-container text-cobalt-on-container;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.badge-success {
|
|
277
|
+
@apply inline-flex items-center px-3 py-1 rounded-full text-caption font-medium bg-malachite-container text-malachite-on-container;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.badge-warning {
|
|
281
|
+
@apply inline-flex items-center px-3 py-1 rounded-full text-caption font-medium bg-gold-container text-gold-on-container;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.badge-info {
|
|
285
|
+
@apply inline-flex items-center px-3 py-1 rounded-full text-caption font-medium bg-cobalt-container text-cobalt-on-container;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.badge-accent {
|
|
289
|
+
@apply inline-flex items-center px-3 py-1 rounded-full text-caption font-medium bg-terracotta-container text-terracotta-on-container;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.badge-premium {
|
|
293
|
+
@apply inline-flex items-center px-3 py-1 rounded-full text-caption font-medium bg-tanzanite-container text-tanzanite-on-container;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* Ubuntu pull-quote */
|
|
297
|
+
.ubuntu-quote {
|
|
298
|
+
@apply relative py-8 px-6
|
|
299
|
+
bg-malachite-container text-malachite-on-container
|
|
300
|
+
border-l-4 border-malachite rounded-r-lg;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/* Inputs */
|
|
304
|
+
.input {
|
|
305
|
+
@apply w-full h-12 px-4 rounded-full
|
|
306
|
+
bg-input text-foreground border border-border
|
|
307
|
+
placeholder:text-muted-foreground
|
|
308
|
+
transition-colors duration-200
|
|
309
|
+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background
|
|
310
|
+
disabled:opacity-50;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* Editorial divider */
|
|
314
|
+
.rule {
|
|
315
|
+
@apply h-px w-full bg-border my-12;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
@layer utilities {
|
|
320
|
+
.text-balance {
|
|
321
|
+
text-wrap: balance;
|
|
322
|
+
}
|
|
323
|
+
.text-pretty {
|
|
324
|
+
text-wrap: pretty;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/* Anthropic-style soft hero gradient — cream → muted */
|
|
328
|
+
.gradient-hero {
|
|
329
|
+
background: linear-gradient(
|
|
330
|
+
135deg,
|
|
331
|
+
var(--canvas) 0%,
|
|
332
|
+
var(--secondary) 100%
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* Subtle mineral wash — for brand accent sections */
|
|
337
|
+
.wash-gold {
|
|
338
|
+
background: linear-gradient(
|
|
339
|
+
180deg,
|
|
340
|
+
transparent 0%,
|
|
341
|
+
var(--color-gold-container) 100%
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
.wash-cobalt {
|
|
345
|
+
background: linear-gradient(
|
|
346
|
+
180deg,
|
|
347
|
+
transparent 0%,
|
|
348
|
+
var(--color-cobalt-container) 100%
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
.wash-malachite {
|
|
352
|
+
background: linear-gradient(
|
|
353
|
+
180deg,
|
|
354
|
+
transparent 0%,
|
|
355
|
+
var(--color-malachite-container) 100%
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bundu/ui — Tailwind preset.
|
|
3
|
+
*
|
|
4
|
+
* Maps the seven-African-mineral palette and the semantic tokens onto
|
|
5
|
+
* the CSS custom properties defined in `styles/globals.css` (+ the
|
|
6
|
+
* `brand-*.css` overlays), and ships the Nyuchi Design System type
|
|
7
|
+
* scale, radius scale, and timing functions. Consumers get every
|
|
8
|
+
* `bg-cobalt` / `text-h2` / `rounded-xl` utility by adding this one
|
|
9
|
+
* preset — the values still resolve through the CSS vars, so nothing
|
|
10
|
+
* here is a raw hex.
|
|
11
|
+
*
|
|
12
|
+
* Usage (tailwind.config.mjs):
|
|
13
|
+
* import preset from "@bundu/ui/tailwind-preset";
|
|
14
|
+
* export default {
|
|
15
|
+
* presets: [preset],
|
|
16
|
+
* content: ["./src/**\/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
|
|
17
|
+
* };
|
|
18
|
+
*
|
|
19
|
+
* NOTE: mineral utility classes are often composed from data
|
|
20
|
+
* (e.g. `bg-${mineral}`); safelist them in your own config if you build
|
|
21
|
+
* class names dynamically.
|
|
22
|
+
*
|
|
23
|
+
* @type {import('tailwindcss').Config}
|
|
24
|
+
*/
|
|
25
|
+
export default {
|
|
26
|
+
darkMode: "class",
|
|
27
|
+
theme: {
|
|
28
|
+
extend: {
|
|
29
|
+
colors: {
|
|
30
|
+
// === Seven African Minerals (canonical Nyuchi Design System palette) ===
|
|
31
|
+
cobalt: {
|
|
32
|
+
DEFAULT: "var(--color-cobalt)",
|
|
33
|
+
container: "var(--color-cobalt-container)",
|
|
34
|
+
"on-container": "var(--color-cobalt-on-container)",
|
|
35
|
+
},
|
|
36
|
+
tanzanite: {
|
|
37
|
+
DEFAULT: "var(--color-tanzanite)",
|
|
38
|
+
container: "var(--color-tanzanite-container)",
|
|
39
|
+
"on-container": "var(--color-tanzanite-on-container)",
|
|
40
|
+
},
|
|
41
|
+
malachite: {
|
|
42
|
+
DEFAULT: "var(--color-malachite)",
|
|
43
|
+
container: "var(--color-malachite-container)",
|
|
44
|
+
"on-container": "var(--color-malachite-on-container)",
|
|
45
|
+
},
|
|
46
|
+
gold: {
|
|
47
|
+
DEFAULT: "var(--color-gold)",
|
|
48
|
+
container: "var(--color-gold-container)",
|
|
49
|
+
"on-container": "var(--color-gold-on-container)",
|
|
50
|
+
},
|
|
51
|
+
terracotta: {
|
|
52
|
+
DEFAULT: "var(--color-terracotta)",
|
|
53
|
+
container: "var(--color-terracotta-container)",
|
|
54
|
+
"on-container": "var(--color-terracotta-on-container)",
|
|
55
|
+
},
|
|
56
|
+
sodalite: {
|
|
57
|
+
DEFAULT: "var(--color-sodalite)",
|
|
58
|
+
container: "var(--color-sodalite-container)",
|
|
59
|
+
"on-container": "var(--color-sodalite-on-container)",
|
|
60
|
+
},
|
|
61
|
+
copper: {
|
|
62
|
+
DEFAULT: "var(--color-copper)",
|
|
63
|
+
container: "var(--color-copper-container)",
|
|
64
|
+
"on-container": "var(--color-copper-on-container)",
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
// === Semantic tokens ===
|
|
68
|
+
background: "var(--background)",
|
|
69
|
+
foreground: "var(--foreground)",
|
|
70
|
+
canvas: "var(--canvas)",
|
|
71
|
+
ink: "var(--ink)",
|
|
72
|
+
primary: {
|
|
73
|
+
DEFAULT: "var(--primary)",
|
|
74
|
+
foreground: "var(--primary-foreground)",
|
|
75
|
+
},
|
|
76
|
+
secondary: {
|
|
77
|
+
DEFAULT: "var(--secondary)",
|
|
78
|
+
foreground: "var(--secondary-foreground)",
|
|
79
|
+
},
|
|
80
|
+
muted: {
|
|
81
|
+
DEFAULT: "var(--muted)",
|
|
82
|
+
foreground: "var(--muted-foreground)",
|
|
83
|
+
},
|
|
84
|
+
accent: {
|
|
85
|
+
DEFAULT: "var(--accent)",
|
|
86
|
+
foreground: "var(--accent-foreground)",
|
|
87
|
+
},
|
|
88
|
+
destructive: {
|
|
89
|
+
DEFAULT: "var(--destructive)",
|
|
90
|
+
foreground: "var(--destructive-foreground)",
|
|
91
|
+
},
|
|
92
|
+
card: {
|
|
93
|
+
DEFAULT: "var(--card)",
|
|
94
|
+
foreground: "var(--card-foreground)",
|
|
95
|
+
},
|
|
96
|
+
popover: {
|
|
97
|
+
DEFAULT: "var(--popover)",
|
|
98
|
+
foreground: "var(--popover-foreground)",
|
|
99
|
+
},
|
|
100
|
+
border: "var(--border)",
|
|
101
|
+
input: "var(--input)",
|
|
102
|
+
ring: "var(--ring)",
|
|
103
|
+
success: "var(--success)",
|
|
104
|
+
warning: "var(--warning)",
|
|
105
|
+
error: "var(--error)",
|
|
106
|
+
info: "var(--info)",
|
|
107
|
+
},
|
|
108
|
+
fontFamily: {
|
|
109
|
+
sans: ['"Noto Sans"', "system-ui", "sans-serif"],
|
|
110
|
+
serif: ['"Noto Serif"', "Georgia", "serif"],
|
|
111
|
+
mono: ['"JetBrains Mono"', "ui-monospace", "monospace"],
|
|
112
|
+
},
|
|
113
|
+
fontSize: {
|
|
114
|
+
// Fluid type scale from the Nyuchi Design System
|
|
115
|
+
display: [
|
|
116
|
+
"clamp(2rem, 4vw + 0.75rem, 3.5rem)",
|
|
117
|
+
{ lineHeight: "1.1", letterSpacing: "-0.025em", fontWeight: "700" },
|
|
118
|
+
],
|
|
119
|
+
"display-sm": [
|
|
120
|
+
"clamp(1.875rem, 3vw + 0.75rem, 3rem)",
|
|
121
|
+
{ lineHeight: "1.1", letterSpacing: "-0.02em", fontWeight: "700" },
|
|
122
|
+
],
|
|
123
|
+
h1: [
|
|
124
|
+
"clamp(2rem, 3vw + 1rem, 3rem)",
|
|
125
|
+
{ lineHeight: "1.15", letterSpacing: "-0.02em", fontWeight: "700" },
|
|
126
|
+
],
|
|
127
|
+
h2: [
|
|
128
|
+
"clamp(1.75rem, 2vw + 1rem, 2.25rem)",
|
|
129
|
+
{ lineHeight: "1.2", letterSpacing: "-0.015em", fontWeight: "600" },
|
|
130
|
+
],
|
|
131
|
+
h3: [
|
|
132
|
+
"clamp(1.5rem, 1.5vw + 1rem, 1.875rem)",
|
|
133
|
+
{ lineHeight: "1.25", letterSpacing: "-0.01em", fontWeight: "600" },
|
|
134
|
+
],
|
|
135
|
+
h4: [
|
|
136
|
+
"1.5rem",
|
|
137
|
+
{ lineHeight: "1.3", letterSpacing: "-0.005em", fontWeight: "600" },
|
|
138
|
+
],
|
|
139
|
+
h5: ["1.25rem", { lineHeight: "1.4", fontWeight: "600" }],
|
|
140
|
+
h6: ["1rem", { lineHeight: "1.4", fontWeight: "600" }],
|
|
141
|
+
"body-lg": ["1.125rem", { lineHeight: "1.6" }],
|
|
142
|
+
body: ["1rem", { lineHeight: "1.6" }],
|
|
143
|
+
"body-sm": ["0.875rem", { lineHeight: "1.5" }],
|
|
144
|
+
caption: ["0.75rem", { lineHeight: "1.5", letterSpacing: "0.02em" }],
|
|
145
|
+
},
|
|
146
|
+
spacing: {
|
|
147
|
+
xxs: "0.125rem",
|
|
148
|
+
"xs-plus": "0.375rem",
|
|
149
|
+
"sm-plus": "0.625rem",
|
|
150
|
+
"base-plus": "1.25rem",
|
|
151
|
+
"xl-plus": "2.5rem",
|
|
152
|
+
"2xl-plus": "3.5rem",
|
|
153
|
+
18: "4.5rem",
|
|
154
|
+
88: "22rem",
|
|
155
|
+
},
|
|
156
|
+
borderRadius: {
|
|
157
|
+
sm: "7px",
|
|
158
|
+
md: "12px",
|
|
159
|
+
lg: "14px",
|
|
160
|
+
xl: "17px",
|
|
161
|
+
"2xl": "17px",
|
|
162
|
+
full: "9999px",
|
|
163
|
+
pill: "9999px",
|
|
164
|
+
},
|
|
165
|
+
maxWidth: {
|
|
166
|
+
prose: "65ch",
|
|
167
|
+
narrow: "42rem",
|
|
168
|
+
},
|
|
169
|
+
ringWidth: {
|
|
170
|
+
DEFAULT: "2px",
|
|
171
|
+
},
|
|
172
|
+
transitionTimingFunction: {
|
|
173
|
+
soft: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
};
|