@dynamic-labs-sdk/droplet 0.27.2 → 0.27.3

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
@@ -1,38 +1,30 @@
1
1
  # `@dynamic-labs-sdk/droplet`
2
2
 
3
- Dynamic Labs' internal React component library. Built on Radix UI primitives, Tailwind CSS v4, and an OKLCH-based design token system. Ships with 35+ components, animated icons, dark mode, and a live Storybook.
3
+ Dynamic Labs' React component library. Built on Radix UI primitives, Tailwind CSS v4, and an OKLCH-based design token system. Ships with 35+ components, animated icons, dark mode, and a live Storybook.
4
4
 
5
5
  ---
6
6
 
7
7
  ## Installation
8
8
 
9
- This package is internal to the `dynamic-js-sdk` monorepo. Add it as a workspace dependency:
10
-
11
- ```json
12
- {
13
- "dependencies": {
14
- "@dynamic-labs-sdk/droplet": "workspace:*"
15
- }
16
- }
9
+ ```bash
10
+ npm install @dynamic-labs-sdk/droplet
11
+ # or
12
+ pnpm add @dynamic-labs-sdk/droplet
13
+ # or
14
+ yarn add @dynamic-labs-sdk/droplet
17
15
  ```
18
16
 
17
+ **Peer dependencies:** `react >= 18`, `react-dom >= 18`, `tailwindcss >= 4`. Next.js (`>= 14`) is optional.
18
+
19
19
  ---
20
20
 
21
21
  ## Setup
22
22
 
23
- Import the design tokens stylesheet **once at your app root**. This provides the full color, shadow, radius, and typography token system and is required for all components to render correctly.
23
+ Droplet ships Tailwind CSS v4 utility classes baked into its compiled output. You must (1) have Tailwind v4 configured in your app, (2) import the design tokens stylesheet, and (3) tell Tailwind to scan droplet's `dist/` so its utility classes are emitted into your stylesheet.
24
24
 
25
- ```ts
26
- import '@dynamic-labs-sdk/droplet/styles/globals.css';
27
- ```
25
+ ### 1. Install and configure Tailwind v4
28
26
 
29
- If you're consuming from a source workspace (not the built `dist/`), use the relative path instead:
30
-
31
- ```ts
32
- import '../../packages/droplet/src/styles/globals.css';
33
- ```
34
-
35
- Your bundler must support Tailwind CSS v4 via the `@tailwindcss/vite` plugin:
27
+ If you don't already have Tailwind v4, follow [the Tailwind v4 install guide](https://tailwindcss.com/docs/installation). The `@tailwindcss/vite` plugin is the simplest path for Vite/Next:
36
28
 
37
29
  ```ts
38
30
  // vite.config.ts
@@ -44,6 +36,26 @@ export default defineConfig({
44
36
  });
45
37
  ```
46
38
 
39
+ ### 2. Import the design tokens stylesheet
40
+
41
+ Import once at your app root. This provides the full color, shadow, radius, and typography token system and is required for all components to render correctly.
42
+
43
+ ```ts
44
+ import '@dynamic-labs-sdk/droplet/styles/globals.css';
45
+ ```
46
+
47
+ ### 3. Add droplet to Tailwind's `@source` paths
48
+
49
+ Tailwind v4 only scans the consuming project by default, so it will miss the utility classes inside droplet's compiled JS. Add an `@source` directive to your app's main stylesheet:
50
+
51
+ ```css
52
+ /* app.css */
53
+ @import 'tailwindcss';
54
+ @source "../node_modules/@dynamic-labs-sdk/droplet/dist";
55
+ ```
56
+
57
+ Adjust the relative path to match where your stylesheet lives. Without this step, components will render unstyled.
58
+
47
59
  ---
48
60
 
49
61
  ## Usage
@@ -194,6 +206,90 @@ import {
194
206
 
195
207
  ---
196
208
 
209
+ ## Theming
210
+
211
+ Droplet's theme is a layer of CSS custom properties. Tailwind utility classes resolve through them (`bg-brand-default` → `var(--brand-default)`), and every component reads from the same vars — so you can fully rebrand by **redefining the vars in your own stylesheet**. No Tailwind config changes, no rebuild, no fork.
212
+
213
+ ### Quick start: change the brand color
214
+
215
+ ```css
216
+ /* your-theme.css — imported AFTER @dynamic-labs-sdk/droplet/styles/globals.css */
217
+
218
+ :root {
219
+ --brand-default: oklch(0.67 0.21 30); /* your light-mode brand */
220
+ --brand-hover: oklch(0.72 0.19 30);
221
+ --brand-accented: oklch(0.58 0.22 30);
222
+ --brand-light: oklch(0.97 0.03 30);
223
+ --ring: oklch(0.67 0.21 30);
224
+ --action: oklch(0.67 0.21 30);
225
+ }
226
+
227
+ .dark {
228
+ --brand-default: oklch(0.6 0.22 30); /* your dark-mode brand */
229
+ --brand-hover: oklch(0.68 0.19 30);
230
+ --brand-accented: oklch(0.52 0.23 30);
231
+ --brand-light: oklch(0.27 0.07 30);
232
+ --ring: oklch(0.6 0.22 30);
233
+ --action: oklch(0.6 0.22 30);
234
+ }
235
+ ```
236
+
237
+ ```ts
238
+ // in your app entry, AFTER the droplet import
239
+ import '@dynamic-labs-sdk/droplet/styles/globals.css';
240
+ import './your-theme.css';
241
+ ```
242
+
243
+ That's it. Buttons, links, focus rings, text-selection highlights, sidebars — all switch to the new brand.
244
+
245
+ ### Cascade order matters
246
+
247
+ Your theme stylesheet **must load after** `@dynamic-labs-sdk/droplet/styles/globals.css`, or the same selectors (`:root`, `.dark`) in droplet's stylesheet will win and your overrides will silently do nothing. If you `@import` in a single CSS file, put droplet's import **first**:
248
+
249
+ ```css
250
+ @import '@dynamic-labs-sdk/droplet/styles/globals.css';
251
+ @import './your-theme.css';
252
+ ```
253
+
254
+ ### What's themeable
255
+
256
+ Anything in the [Design tokens](#design-tokens) table below — colors, shadows, radii. The most common things to override:
257
+
258
+ | What you want to change | Tokens |
259
+ | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
260
+ | Brand color (buttons, focus rings, links, selection) | `--brand-default`, `--brand-hover`, `--brand-accented`, `--brand-light`, `--ring`, `--action` |
261
+ | Surfaces (page bg, cards, sidebar) | `--background`, `--card`, `--bg-bottom`, `--bg-default`, `--sidebar` |
262
+ | Text colors | `--foreground`, `--muted-foreground`, `--text-link` |
263
+ | Radius scale | `--radius-sm` through `--radius-4xl` |
264
+ | Selection highlight | `--selection-bg` |
265
+ | Solid button top-highlight | `--button-highlight` |
266
+ | Checkbox / radio hover fills | `--checkbox-hover-fill`, `--radio-hover-fill` |
267
+
268
+ For semantic colors (success/danger/warning/process), override the matching `--<state>-default`, `--<state>-light`, `--text-<state>` triplet.
269
+
270
+ ### Scoping a theme to part of your app
271
+
272
+ If you only want droplet themed inside a subtree (e.g., a marketing site reusing one component), put the overrides on a wrapper class instead of `:root`:
273
+
274
+ ```css
275
+ .my-theme {
276
+ --brand-default: oklch(0.67 0.21 30);
277
+ /* ... */
278
+ }
279
+ ```
280
+
281
+ ```tsx
282
+ <div className="my-theme">
283
+ <Button>Themed</Button>
284
+ </div>
285
+ ```
286
+
287
+ ### Color space
288
+
289
+ Droplet uses [OKLCH](https://oklch.com/) throughout. If your design system is in hex/rgb, tools like [oklch.com](https://oklch.com) or [oklch.fyi](https://oklch.fyi) will convert. You don't *have* to use OKLCH in your overrides — `oklch()`, `hex`, `rgb()`, and `hsl()` all interoperate fine — but staying in OKLCH gives you perceptually uniform lightness ramps when you tune hover/accented variants.
290
+
291
+ ---
292
+
197
293
  ## Design tokens
198
294
 
199
295
  All tokens are CSS custom properties defined in `globals.css`. Tailwind utility classes map directly to them — `bg-brand-default` resolves to `var(--brand-default)`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs-sdk/droplet",
3
- "version": "0.27.2",
3
+ "version": "0.27.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.esm.js",
@@ -23,6 +23,9 @@
23
23
  "dist",
24
24
  "src/styles"
25
25
  ],
26
+ "sideEffects": [
27
+ "**/*.css"
28
+ ],
26
29
  "dependencies": {
27
30
  "@hugeicons/core-free-icons": "4.1.1",
28
31
  "@hugeicons/react": "1.1.6",
@@ -76,7 +79,13 @@
76
79
  "peerDependencies": {
77
80
  "next": ">=14",
78
81
  "react": ">=18",
79
- "react-dom": ">=18"
82
+ "react-dom": ">=18",
83
+ "tailwindcss": ">=4"
84
+ },
85
+ "peerDependenciesMeta": {
86
+ "next": {
87
+ "optional": true
88
+ }
80
89
  },
81
90
  "scripts": {
82
91
  "build": "rm -rf dist && tsdown && pnpm exec tsc --emitDeclarationOnly -p ./tsconfig.lib.json",
@@ -227,6 +227,12 @@
227
227
  --shadow-field-hover: 0 0 0 1px oklch(0.278 0.03 256.85 / 0.18), 0px 0px 1px 0px oklch(0.278 0.03 256.85 / 0.10);
228
228
  --shadow-elevated: 0 0 0 1px oklch(0.278 0.03 256.85 / 0.10), 0px 5px 15px 0px oklch(0.278 0.03 256.85 / 0.08), 0px 15px 35px -5px oklch(0.278 0.03 256.85 / 0.20);
229
229
  --shadow-card-content: 0px 0px 2px 0px oklch(0.278 0.03 256.85 / 0.08), 0px 1px 2px 0px oklch(0.278 0.03 256.85 / 0.06);
230
+
231
+ /* ——— Component-specific overlays (themeable) ——— */
232
+ --selection-bg: oklch(0.574 0.207 257.4 / 0.15);
233
+ --checkbox-hover-fill: oklch(0.278 0.03 256.85 / 0.08);
234
+ --radio-hover-fill: oklch(0.278 0.03 256.85 / 0.12);
235
+ --button-highlight: oklch(1 0 0);
230
236
  }
231
237
 
232
238
  /* ═══════════════════════════════════════════════════════════
@@ -354,6 +360,12 @@
354
360
  --shadow-field-hover: 0 0 0 1px oklch(1 0 0 / 0.18), 0px 0px 1px 0px oklch(0 0 0 / 0.10);
355
361
  --shadow-elevated: 0 0 0 1px oklch(1 0 0 / 0.06), 0px 5px 15px 0px oklch(0 0 0 / 0.25), 0px 15px 35px -5px oklch(0 0 0 / 0.45);
356
362
  --shadow-card-content: 0px 0px 2px 0px oklch(0 0 0 / 0.08), 0px 1px 2px 0px oklch(0 0 0 / 0.06);
363
+
364
+ /* ——— Component-specific overlays (themeable) ——— */
365
+ --selection-bg: oklch(0.510 0.205 257 / 0.18);
366
+ --checkbox-hover-fill: oklch(1 0 0 / 0.10);
367
+ --radio-hover-fill: oklch(1 0 0 / 0.15);
368
+ --button-highlight: oklch(1 0 0);
357
369
  }
358
370
 
359
371
  @layer base {
@@ -384,7 +396,7 @@
384
396
  }
385
397
  /* Branded text selection */
386
398
  ::selection {
387
- background-color: oklch(0.574 0.207 257.4 / 0.15);
399
+ background-color: var(--selection-bg);
388
400
  color: inherit;
389
401
  }
390
402
  /* Slashed zero + tabular nums for monospace contexts */
@@ -448,16 +460,12 @@
448
460
  position: absolute;
449
461
  inset: 2px;
450
462
  border-radius: 2px;
451
- background: oklch(0.278 0.03 256.85 / 0.08);
463
+ background: var(--checkbox-hover-fill);
452
464
  opacity: 0;
453
465
  transition: opacity 150ms ease-out;
454
466
  pointer-events: none;
455
467
  }
456
468
 
457
- .dark [data-slot='checkbox'][data-state='unchecked']::before {
458
- background: oklch(1 0 0 / 0.10);
459
- }
460
-
461
469
  [data-slot='checkbox'][data-state='unchecked']:not(:disabled):hover::before {
462
470
  opacity: 1;
463
471
  }
@@ -477,16 +485,12 @@
477
485
  top: 50%;
478
486
  left: 50%;
479
487
  transform: translate(-50%, -50%);
480
- background: oklch(0.278 0.03 256.85 / 0.12);
488
+ background: var(--radio-hover-fill);
481
489
  opacity: 0;
482
490
  transition: opacity 150ms ease-out;
483
491
  pointer-events: none;
484
492
  }
485
493
 
486
- .dark [data-slot='radio-group-item'][data-state='unchecked']::before {
487
- background: oklch(1 0 0 / 0.15);
488
- }
489
-
490
494
  [data-slot='radio-group-item'][data-state='unchecked']:not(:disabled):hover::before {
491
495
  opacity: 1;
492
496
  }
@@ -524,7 +528,7 @@
524
528
  position: absolute;
525
529
  inset: 0;
526
530
  border-radius: inherit;
527
- background: linear-gradient(180deg, oklch(1 0 0 / 1), oklch(1 0 0 / 0));
531
+ background: linear-gradient(180deg, var(--button-highlight), transparent);
528
532
  opacity: 0.1;
529
533
  transition: opacity 0.5s;
530
534
  pointer-events: none;