@pihanga2/shadcn 0.2.7 → 0.2.8
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/AGENT.building-cards.md +118 -0
- package/package.json +4 -1
- package/theme.css +102 -0
package/AGENT.building-cards.md
CHANGED
|
@@ -703,3 +703,121 @@ Rules:
|
|
|
703
703
|
- `src/cards/BUILDING_CARDS_HOWTO.md` — human-oriented narrative version of this guide
|
|
704
704
|
- `scripts/gen-card-dependencies.mjs` — dependency scanner
|
|
705
705
|
- `scripts/gen-registry.mjs` — registry builder
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
709
|
+
## Theme-aware cards — working with CSS variables
|
|
710
|
+
|
|
711
|
+
shadcn/ui components (and therefore pihanga-shadcn cards built on top of them)
|
|
712
|
+
use **CSS custom properties** for all semantic colours, radii, and shadows.
|
|
713
|
+
Understanding the token system is essential for cards that carry their own
|
|
714
|
+
visual chrome (borders, backgrounds, elevation).
|
|
715
|
+
|
|
716
|
+
### How the token system works
|
|
717
|
+
|
|
718
|
+
`src/theme.css` (shipped as `@pihanga2/shadcn/theme.css`) defines three layers:
|
|
719
|
+
|
|
720
|
+
| Layer | Purpose | Example |
|
|
721
|
+
|---|---|---|
|
|
722
|
+
| `:root { --card: oklch(...) }` | **Palette** — raw colour values | `--card`, `--border`, `--radius` |
|
|
723
|
+
| `@theme inline { --color-card: var(--card) }` | **Tailwind mapping** — wires vars to utility classes | `bg-card`, `border-border`, `rounded-xl` |
|
|
724
|
+
| `.dark { --card: oklch(...) }` | **Dark-mode overrides** | Same tokens, different values |
|
|
725
|
+
|
|
726
|
+
A consumer who overrides `:root { --card: oklch(0.9 0 0); }` in their own CSS
|
|
727
|
+
automatically rethemes every card that uses `bg-card` — no card code changes
|
|
728
|
+
needed.
|
|
729
|
+
|
|
730
|
+
### Using theme tokens in a new card
|
|
731
|
+
|
|
732
|
+
**Prefer shadcn semantic tokens over hardcoded colours.** This makes the card
|
|
733
|
+
rethemable by the consumer:
|
|
734
|
+
|
|
735
|
+
```tsx
|
|
736
|
+
// ✅ Rethemable — uses CSS variable tokens
|
|
737
|
+
<div className="bg-card text-card-foreground border border-border rounded-xl shadow-sm">
|
|
738
|
+
|
|
739
|
+
// ❌ Hard-wired — ignores consumer theme
|
|
740
|
+
<div className="bg-white text-gray-900 border border-gray-200 rounded-xl shadow-sm">
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
Standard shadcn tokens available as Tailwind utilities:
|
|
744
|
+
|
|
745
|
+
| Tailwind class | CSS variable | Default (light) |
|
|
746
|
+
|---|---|---|
|
|
747
|
+
| `bg-card` | `--card` | white |
|
|
748
|
+
| `text-card-foreground` | `--card-foreground` | near-black |
|
|
749
|
+
| `bg-background` | `--background` | white |
|
|
750
|
+
| `text-foreground` | `--foreground` | near-black |
|
|
751
|
+
| `bg-muted` | `--muted` | light gray |
|
|
752
|
+
| `text-muted-foreground` | `--muted-foreground` | medium gray |
|
|
753
|
+
| `bg-primary` | `--primary` | dark |
|
|
754
|
+
| `text-primary-foreground` | `--primary-foreground` | near-white |
|
|
755
|
+
| `border-border` | `--border` | light gray |
|
|
756
|
+
| `rounded-xl` | `--radius-xl` = `--radius + 4px` | ~14px |
|
|
757
|
+
| `rounded-lg` | `--radius-lg` = `--radius` | 10px |
|
|
758
|
+
| `shadow-sm` | Tailwind built-in | subtle shadow |
|
|
759
|
+
|
|
760
|
+
### Adding a new theme token for a card
|
|
761
|
+
|
|
762
|
+
When a card needs a design token consumers can override (e.g. a brand-specific
|
|
763
|
+
colour or radius), add it to **`src/theme.css`** following the same pattern:
|
|
764
|
+
|
|
765
|
+
```css
|
|
766
|
+
/* In src/theme.css — @theme inline block */
|
|
767
|
+
@theme inline {
|
|
768
|
+
/* … existing tokens … */
|
|
769
|
+
--color-my-card-accent: var(--my-card-accent);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/* default in :root */
|
|
773
|
+
:root {
|
|
774
|
+
--my-card-accent: oklch(0.5 0.2 260); /* a blue */
|
|
775
|
+
}
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
Then use `bg-my-card-accent` / `text-my-card-accent` in your component.
|
|
779
|
+
Consumers override by setting `--my-card-accent` in their own `:root`.
|
|
780
|
+
|
|
781
|
+
> **Do not** add tokens that are only useful for a single card to the global
|
|
782
|
+
> `theme.css`. For card-local defaults, prefer explicit Tailwind colour classes
|
|
783
|
+
> or a `className` prop.
|
|
784
|
+
|
|
785
|
+
### The `@container` gotcha with shadcn composites
|
|
786
|
+
|
|
787
|
+
shadcn's `<CardHeader>` applies `@container/card-header`
|
|
788
|
+
(`container-type: inline-size`). CSS containment makes the element's
|
|
789
|
+
intrinsic inline size appear as **0** to its parent for `min-width` / `max-content`
|
|
790
|
+
calculations — your card will collapse to a tiny width if it relies on `min-w-max`.
|
|
791
|
+
|
|
792
|
+
**Fix:** replace `<CardHeader>` with a plain `<div data-slot="card-header">` that
|
|
793
|
+
carries the same Tailwind classes but without the `@container` prefix:
|
|
794
|
+
|
|
795
|
+
```tsx
|
|
796
|
+
// ❌ @container breaks min-w-max on the parent Card
|
|
797
|
+
<CardHeader className={headerClassName}>
|
|
798
|
+
|
|
799
|
+
// ✅ Same visual styles, no CSS containment
|
|
800
|
+
<div
|
|
801
|
+
data-slot="card-header"
|
|
802
|
+
className={cn(
|
|
803
|
+
"grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6",
|
|
804
|
+
"has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
|
805
|
+
headerClassName,
|
|
806
|
+
)}
|
|
807
|
+
>
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
This is safe because the `has-data-[slot=card-action]` selector is a CSS `:has()`
|
|
811
|
+
rule, not a container query — it doesn't require `@container` to function.
|
|
812
|
+
|
|
813
|
+
### Consumer setup (reminder)
|
|
814
|
+
|
|
815
|
+
Cards in this library render correctly only when the consuming app's CSS
|
|
816
|
+
includes `@import "@pihanga2/shadcn/theme.css"`. That single import:
|
|
817
|
+
|
|
818
|
+
1. Defines the CSS custom property palette (`:root`, `.dark`)
|
|
819
|
+
2. Maps them to Tailwind v4 utility tokens (`@theme inline`)
|
|
820
|
+
3. Tells Tailwind to scan the library's compiled files (`@source "."`)
|
|
821
|
+
|
|
822
|
+
Apps that already use shadcn/ui with their own theme setup do not need the
|
|
823
|
+
import — their `:root` variables cascade over the defaults.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pihanga2/shadcn",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Pihanga core card components built on shadcn/ui and Radix UI — the npm distribution of pihanga-shadcn.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -179,6 +179,9 @@
|
|
|
179
179
|
"./lib/utils": {
|
|
180
180
|
"import": "./lib/utils.js",
|
|
181
181
|
"types": "./lib/utils.d.ts"
|
|
182
|
+
},
|
|
183
|
+
"./theme": {
|
|
184
|
+
"import": "./theme.css"
|
|
182
185
|
}
|
|
183
186
|
},
|
|
184
187
|
"sideEffects": [
|
package/theme.css
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* pihanga-shadcn — theme.css
|
|
3
|
+
*
|
|
4
|
+
* Add ONE line to your app's main CSS (processed by Tailwind v4):
|
|
5
|
+
*
|
|
6
|
+
* @import "tailwindcss";
|
|
7
|
+
* @import "@pihanga2/shadcn/theme.css";
|
|
8
|
+
*
|
|
9
|
+
* The @source directive below tells Tailwind to scan this package's
|
|
10
|
+
* compiled JS files for utility class names, so you don't need a
|
|
11
|
+
* separate @source line in your own CSS.
|
|
12
|
+
*
|
|
13
|
+
* Skip the import if your app already has a shadcn/ui theme — your
|
|
14
|
+
* :root variables will take precedence over the defaults below.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/* Scan this package's compiled files for Tailwind utility classes */
|
|
18
|
+
@source ".";
|
|
19
|
+
|
|
20
|
+
/* Map shadcn CSS vars to Tailwind v4 utility tokens */
|
|
21
|
+
@theme inline {
|
|
22
|
+
--color-background: var(--background);
|
|
23
|
+
--color-foreground: var(--foreground);
|
|
24
|
+
--color-card: var(--card);
|
|
25
|
+
--color-card-foreground: var(--card-foreground);
|
|
26
|
+
--color-popover: var(--popover);
|
|
27
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
28
|
+
--color-primary: var(--primary);
|
|
29
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
30
|
+
--color-secondary: var(--secondary);
|
|
31
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
32
|
+
--color-muted: var(--muted);
|
|
33
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
34
|
+
--color-accent: var(--accent);
|
|
35
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
36
|
+
--color-destructive: var(--destructive);
|
|
37
|
+
--color-border: var(--border);
|
|
38
|
+
--color-input: var(--input);
|
|
39
|
+
--color-ring: var(--ring);
|
|
40
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
41
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
42
|
+
--radius-lg: var(--radius);
|
|
43
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
44
|
+
--color-btn-brand: var(--primary);
|
|
45
|
+
--color-btn-brand-foreground: var(--primary-foreground);
|
|
46
|
+
--radius-btn-brand: var(--radius-md);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Light theme (shadcn neutral / new-york) */
|
|
50
|
+
:root {
|
|
51
|
+
--background: oklch(1 0 0);
|
|
52
|
+
--foreground: oklch(0.145 0 0);
|
|
53
|
+
--card: oklch(1 0 0);
|
|
54
|
+
--card-foreground: oklch(0.145 0 0);
|
|
55
|
+
--popover: oklch(1 0 0);
|
|
56
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
57
|
+
--primary: oklch(0.205 0 0);
|
|
58
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
59
|
+
--secondary: oklch(0.97 0 0);
|
|
60
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
61
|
+
--muted: oklch(0.97 0 0);
|
|
62
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
63
|
+
--accent: oklch(0.97 0 0);
|
|
64
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
65
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
66
|
+
--border: oklch(0.922 0 0);
|
|
67
|
+
--input: oklch(0.922 0 0);
|
|
68
|
+
--ring: oklch(0.708 0 0);
|
|
69
|
+
--radius: 0.625rem;
|
|
70
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
71
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
72
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
73
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
74
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Dark theme */
|
|
78
|
+
.dark {
|
|
79
|
+
--background: oklch(0.145 0 0);
|
|
80
|
+
--foreground: oklch(0.985 0 0);
|
|
81
|
+
--card: oklch(0.205 0 0);
|
|
82
|
+
--card-foreground: oklch(0.985 0 0);
|
|
83
|
+
--popover: oklch(0.205 0 0);
|
|
84
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
85
|
+
--primary: oklch(0.922 0 0);
|
|
86
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
87
|
+
--secondary: oklch(0.269 0 0);
|
|
88
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
89
|
+
--muted: oklch(0.269 0 0);
|
|
90
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
91
|
+
--accent: oklch(0.269 0 0);
|
|
92
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
93
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
94
|
+
--border: oklch(1 0 0 / 10%);
|
|
95
|
+
--input: oklch(1 0 0 / 15%);
|
|
96
|
+
--ring: oklch(0.556 0 0);
|
|
97
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
98
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
99
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
100
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
101
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
102
|
+
}
|