@facetui/react 1.0.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/THEMING.md ADDED
@@ -0,0 +1,168 @@
1
+ # Theming FacetUI
2
+
3
+ FacetUI ships **one** theme and a fixed token contract. A theme is nothing more
4
+ than an override of ~16 CSS custom properties — there are no per-component theme
5
+ props, no config file, no build step. If your app already uses a
6
+ [shadcn/ui **v4**](https://ui.shadcn.com) theme, FacetUI is themed already; the
7
+ tokens are identical.
8
+
9
+ - [The token contract](#the-token-contract)
10
+ - [Using FacetUI's default theme](#using-facetuis-default-theme)
11
+ - [Dark mode](#dark-mode)
12
+ - [Building your own theme (≈15 min)](#building-your-own-theme-15-min)
13
+ - [Multiple presets](#multiple-presets)
14
+ - [FAQ](#faq)
15
+
16
+ ---
17
+
18
+ ## The token contract
19
+
20
+ Every FacetUI component is styled exclusively against these semantic tokens.
21
+ Values are [OKLCH](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch).
22
+ Each `--token` is exposed to Tailwind as a `*-token` utility (`bg-primary`,
23
+ `text-muted-foreground`, `border-border`, `ring-ring`, …).
24
+
25
+ | Token | Drives | Notes |
26
+ |---|---|---|
27
+ | `--background` / `--foreground` | Page + default text | The surface the table sits on |
28
+ | `--primary` / `--primary-foreground` | Selected rows, checked checkboxes, spinner, primary emphasis | |
29
+ | `--muted` / `--muted-foreground` | Header row fill, hover rows, secondary text, result counts | |
30
+ | `--accent` / `--accent-foreground` | Focused / hovered menu & option rows | |
31
+ | `--popover` / `--popover-foreground` | Column-visibility menu, page-size dropdown surface | |
32
+ | `--destructive` / `--destructive-foreground` | Reserved for destructive states in composed layouts | |
33
+ | `--border` | Every divider and cell border | `* { border-color: var(--border) }` is set in base |
34
+ | `--input` | Search field & control borders | Usually equal to `--border` |
35
+ | `--ring` | `focus-visible` ring on every interactive control | Keep it visibly distinct from `--background` |
36
+
37
+ That's the whole surface. Adding a component later means styling it against
38
+ **these** tokens — not introducing new ones unless a genuinely new role appears.
39
+
40
+ ---
41
+
42
+ ## Using FacetUI's default theme
43
+
44
+ Copy the three blocks from [`src/index.css`](./src/index.css) into your global
45
+ stylesheet:
46
+
47
+ 1. `@custom-variant dark (&:is(.dark *));`
48
+ 2. the `:root { … }` and `.dark { … }` token blocks
49
+ 3. the `@theme inline { … }` mapping (this is what makes `bg-primary` etc. resolve)
50
+
51
+ No `tailwind.config` changes are needed on Tailwind v4 — utility classes are
52
+ auto-detected from your source.
53
+
54
+ > **shadcn/ui v4 users:** you already have all of the above. Skip this section.
55
+
56
+ ---
57
+
58
+ ## Dark mode
59
+
60
+ The contract is driven by a `.dark` **class**, matched anywhere up the tree
61
+ (`&:is(.dark *)`). Put the class on `<html>` (or any wrapper) and every FacetUI
62
+ surface below it re-themes instantly — no rebuild, because `@theme inline` keeps
63
+ the utilities pointing at the live custom properties.
64
+
65
+ ```tsx
66
+ // e.g. next-themes, or your own toggle
67
+ <html className={isDark ? "dark" : undefined}>
68
+ ```
69
+
70
+ Storybook has a **Theme** toolbar toggle (light / dark) wired to the same class —
71
+ use it to check both halves of the contract while developing.
72
+
73
+ ---
74
+
75
+ ## Building your own theme (≈15 min)
76
+
77
+ A theme override only needs to restate the tokens you want to change. Anything you
78
+ omit falls back to the default.
79
+
80
+ **1. Pick your brand color in OKLCH.** Use any OKLCH picker
81
+ (`oklch(L C H)` — lightness `0–1`, chroma `0–0.4`, hue `0–360`). Say your brand is
82
+ a blue: `oklch(0.55 0.20 255)`.
83
+
84
+ **2. Derive the ramp.** You typically only need the accent pair plus the ring:
85
+
86
+ ```css
87
+ /* brand-theme.css — layer this AFTER FacetUI's :root block */
88
+ :root {
89
+ --primary: oklch(0.55 0.20 255);
90
+ --primary-foreground: oklch(0.985 0 0); /* white text on the brand fill */
91
+ --ring: oklch(0.55 0.20 255 / 0.6); /* brand, translucent */
92
+ }
93
+
94
+ .dark {
95
+ --primary: oklch(0.72 0.16 255); /* lift L, drop C for dark surfaces */
96
+ --primary-foreground: oklch(0.18 0.02 255);
97
+ --ring: oklch(0.72 0.16 255 / 0.55);
98
+ }
99
+ ```
100
+
101
+ **3. (Optional) shift the neutrals.** The defaults are zinc (hue ≈ 286). To warm
102
+ them, nudge every neutral token's hue toward ~60–90 and add a little chroma:
103
+
104
+ ```css
105
+ :root {
106
+ --background: oklch(1 0 0);
107
+ --foreground: oklch(0.14 0.01 75);
108
+ --muted: oklch(0.96 0.008 75);
109
+ --muted-foreground: oklch(0.55 0.02 75);
110
+ --accent: oklch(0.96 0.008 75);
111
+ --accent-foreground: oklch(0.21 0.01 75);
112
+ --border: oklch(0.90 0.01 75);
113
+ --input: oklch(0.90 0.01 75);
114
+ }
115
+ ```
116
+
117
+ **4. Check contrast.** `--foreground` on `--background`, `--primary-foreground` on
118
+ `--primary`, and `--muted-foreground` on `--background` all need to clear WCAG AA
119
+ (4.5:1 for body text). OKLCH lightness (`L`) is close to perceptual — a gap of
120
+ ~0.4 between text and its surface is a safe starting point; verify with a
121
+ contrast checker.
122
+
123
+ **5. Load it after the base tokens** so your values win the cascade. Done —
124
+ `<DataTable>` needs no props.
125
+
126
+ ---
127
+
128
+ ## Multiple presets
129
+
130
+ FacetUI intentionally ships a single theme. When you need more than one, make
131
+ each preset a **class** that restates the tokens, mirroring the `.dark` pattern:
132
+
133
+ ```css
134
+ .theme-ocean { --primary: oklch(0.55 0.13 220); --ring: oklch(0.55 0.13 220 / .6); /* … */ }
135
+ .theme-ocean.dark,
136
+ .dark .theme-ocean { --primary: oklch(0.72 0.11 220); /* … */ }
137
+ ```
138
+
139
+ ```tsx
140
+ <div className="theme-ocean">
141
+ <DataTable … />
142
+ </div>
143
+ ```
144
+
145
+ Because the tokens are semantic, one class swap re-themes the entire component —
146
+ including its menus and popovers — with no FacetUI API surface involved.
147
+
148
+ ---
149
+
150
+ ## FAQ
151
+
152
+ **Can I change padding / density / border radius per theme?**
153
+ Density is a runtime prop (`density="compact" | "default" | "comfortable"`), not a
154
+ token. Radius is currently a fixed Tailwind scale (`rounded-xs` / `rounded-sm` /
155
+ `rounded-md`) in the components; if you need it tokenized, that's a component
156
+ change, not a theme override.
157
+
158
+ **Why OKLCH and not HSL / hex?**
159
+ Perceptually uniform lightness makes deriving accessible ramps predictable, and it
160
+ matches shadcn/ui v4 so their themes drop in unchanged. Opacity modifiers
161
+ (`bg-muted/50`) still work — Tailwind compiles them to `color-mix()`.
162
+
163
+ **I'm on Tailwind v3.**
164
+ Add the token `:root` / `.dark` blocks as plain CSS, register the colors in
165
+ `tailwind.config` under `theme.extend.colors` (e.g.
166
+ `primary: "var(--primary)"`), and add
167
+ `"./node_modules/@facetui/react/dist/**/*.js"` (or your copied folder path) to
168
+ `content`.