@ekanos/ui 0.1.1 → 0.1.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
@@ -21,6 +21,8 @@ One subpath per component, mirroring the `@kit/ui` names:
21
21
  | `@ekanos/ui/select` | `Select`, `SelectTrigger`, `SelectValue`, `SelectContent`, `SelectItem`, … |
22
22
  | `@ekanos/ui/form` | `Form`, `FormField`, `FormItem`, `FormLabel`, `FormControl`, `FormDescription`, `FormMessage`, `useFormField` |
23
23
  | `@ekanos/ui/input` | `Input` |
24
+ | `@ekanos/ui/textarea` | `Textarea` |
25
+ | `@ekanos/ui/switch` | `Switch` |
24
26
  | `@ekanos/ui/dropdown-menu` | `DropdownMenu`, `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, … |
25
27
  | `@ekanos/ui/label` | `Label` |
26
28
  | `@ekanos/ui/skeleton` | `Skeleton` |
@@ -31,37 +33,170 @@ One subpath per component, mirroring the `@kit/ui` names:
31
33
  | `@ekanos/ui/spinner` | `Spinner` |
32
34
  | `@ekanos/ui/ai-prompt-input` | `PromptInput` compound components for AI ask surfaces |
33
35
 
34
- Most source files are a thin `export * from '@kit/ui/<name>'` — for those,
35
- `@kit/ui` stays the single source of truth. At build time the `@kit/ui` code is
36
- inlined into `dist/` so the published artifact has no workspace dependencies
37
- (`scripts/pack-test.mjs` fails the build if any `@kit/` specifier leaks).
36
+ Most source files are a thin re-export of the internal `@kit/ui` library — for
37
+ those, `@kit/ui` stays the single source of truth. At build time that code is
38
+ inlined into `dist/` so the published artifact has no workspace dependencies;
39
+ `pnpm --filter @ekanos/ui pack:test` fails the build if any `@kit/` specifier
40
+ leaks into the tarball.
38
41
 
39
42
  Three subpaths — `if`, `trans`, and `spinner` — are instead implemented
40
- natively in `src/_impl/`. Their `@kit/ui` counterparts live in
41
- `packages/ui/src/makerkit/`, which is Makerkit-authored code from the
42
- commercial Turbo starter this monorepo was built on; publishing it under this
43
+ natively here. Their internal counterparts are Makerkit-authored code from the
44
+ commercial Turbo starter this monorepo was built on; publishing that under this
43
45
  package's MIT license would purport to grant reuse rights we do not hold. The
44
- implementations here are original, and `pack:test` fails the build if any
45
- `packages/ui/src/makerkit` source is ever inlined into `dist/`. Inside the
46
- monorepo, first-party code keeps using `@kit/ui` as before.
46
+ implementations shipped here are original, and `pack:test` fails the build if
47
+ any Makerkit-authored source is ever inlined into `dist/`.
47
48
 
48
- ## Styling: this package ships NO CSS
49
+ ## Styling
49
50
 
50
- Components emit Tailwind class strings referencing Fusion's **semantic design
51
- tokens** (`bg-primary`, `text-muted-foreground`, `border-border`, …). They
52
- render correctly only inside a host that owns that CSS — the Fusion app or the
53
- integration dev harness. Nothing here reads or requires the host's
54
- `theme-tokens.ts` runtime; the contract is purely "the host page loads
55
- Fusion's stylesheet".
51
+ Every component emits Tailwind class strings bound to Fusion's semantic design
52
+ tokens (`bg-primary`, `text-muted-foreground`, `border-border`, `shadow-card`,
53
+ …), so something has to define those. This package ships that CSS.
56
54
 
57
- Two host-provided assets to know about:
55
+ Pick **one** of the two paths. Loading both means two copies of every utility.
58
56
 
59
- - **Tailwind tokens**: the host defines the semantic token variables and
60
- utilities. Without them the components render unstyled HTML.
61
- - **Icons are Font Awesome**: `Icon` renders `<i class="fa-solid fa-house">`
62
- styled by the Font Awesome Pro web-font CSS the host loads. Legacy PascalCase
63
- Lucide names are translated to FA equivalents via a bundled map; there is no
64
- `lucide-react` at runtime.
57
+ ### You do NOT use Tailwind import the compiled stylesheet
58
+
59
+ ```ts
60
+ import '@ekanos/ui/styles.css';
61
+ ```
62
+
63
+ Self-contained: Tailwind's Preflight, the design tokens, light and dark, and
64
+ every utility class the components in this package emit — all pre-compiled.
65
+ Nothing else to install, no build step, no config. Works in Next.js, Vite, CRA,
66
+ a plain `<link rel="stylesheet">`, anything.
67
+
68
+ ### You DO use Tailwind v4 — import the preset
69
+
70
+ ```css
71
+ @import 'tailwindcss';
72
+ @import '@ekanos/ui/theme.css';
73
+ ```
74
+
75
+ That registers the token map (`--color-primary` → `--primary`, the `--radius-*`
76
+ scale, `shadow-card`), the token values for both themes, the base rules, the
77
+ `dark:` variant, and an `@source` pointing at this package's bundles so your
78
+ Tailwind build generates the components' utilities itself. You then get the
79
+ Fusion tokens in **your own** class strings too.
80
+
81
+ Also published, if you want the pieces separately: `@ekanos/ui/tokens.css`
82
+ (just the custom properties, no Tailwind at-rules) and `@ekanos/ui/base.css`
83
+ (the base rules).
84
+
85
+ ### What `styles.css` does and does not contain
86
+
87
+ It contains the utilities **this package's components** emit — derived by
88
+ compiling every class string found in the built bundles. It is not a copy of
89
+ Tailwind.
90
+
91
+ So `<Button>` and `<Card>` render correctly, but a Tailwind class *you* write in
92
+ *your* component (`p-8`, `grid-cols-3`, `h-5 w-5`) has no rule in it and will do
93
+ nothing. That is inherent: a pre-compiled stylesheet cannot know class names
94
+ that don't exist yet. Two options, both fine:
95
+
96
+ - style your own markup with plain CSS or inline styles, or
97
+ - take the Tailwind path above, which is how Fusion itself works.
98
+
99
+ `styles.css` includes Tailwind's Preflight, which is a global reset — it will
100
+ normalise margins, headings and form controls across your whole page. If that
101
+ is unwelcome, use the Tailwind path and control layering yourself.
102
+
103
+ ### Page background and dark mode
104
+
105
+ The stylesheet will not repaint your page uninvited. To adopt Fusion's page
106
+ surface — which is what makes dark mode look right rather than dark cards
107
+ floating on a white page — add the opt-in class:
108
+
109
+ ```html
110
+ <body class="ekanos-root">
111
+ ```
112
+
113
+ Dark mode resolves in three states, in this order:
114
+
115
+ | State | Result |
116
+ | ----------------------------------------- | ---------------------------- |
117
+ | nothing set | light |
118
+ | `@media (prefers-color-scheme: dark)` | dark |
119
+ | `.dark` or `[data-theme='dark']` ancestor | dark, beating the media query |
120
+ | `.light` or `[data-theme='light']` on `:root` | light, beating the media query |
121
+
122
+ So a partner app with no theme toggle at all still follows the OS, and one with
123
+ a toggle (next-themes, or your own) works by setting the class. Fusion itself is
124
+ class-only; the media query is added here for apps that have no switcher.
125
+
126
+ ### Tokens: exactly the ones the components use
127
+
128
+ `tokens.css` is not a copy of Fusion's `shadcn-ui.css`. It carries the 25
129
+ semantic colour tokens the published components actually reference, plus their
130
+ paired foregrounds, `--radius`, and the one elevation level any of them uses
131
+ (`--el-card`, behind `shadow-card`). Fusion's chart, sidebar and assistant
132
+ tokens, and nine of its ten elevation levels, are deliberately absent.
133
+
134
+ The build enforces this in both directions: it re-compiles the same class
135
+ strings against Fusion's **full** token map and fails if a component has started
136
+ using a token this package doesn't ship (which would otherwise emit no CSS at
137
+ all, silently). The CSS build enforces it.
138
+
139
+ ### Fonts
140
+
141
+ Fusion pairs **Figtree** (body) with **Bricolage Grotesque** (headings) and
142
+ loads them via `next/font`. This package only names them in `--font-sans` /
143
+ `--font-heading`, followed by a system fallback stack — no webfont is bundled or
144
+ fetched. For exact typographic parity, load them yourself:
145
+
146
+ ```html
147
+ <link
148
+ rel="stylesheet"
149
+ href="https://fonts.googleapis.com/css2?family=Figtree:wght@300..700&family=Bricolage+Grotesque:wght@200..800&display=swap"
150
+ />
151
+ ```
152
+
153
+ Without them, everything still renders — in your platform's UI font.
154
+
155
+ ### Icons: you must supply Font Awesome
156
+
157
+ `Icon` renders Font Awesome markup (`<i class="fa-solid fa-house">`) and this
158
+ package ships **no icon font**, because Fusion uses **Font Awesome Pro** (7.2.0,
159
+ © Fonticons, Inc.) under a commercial licence. Redistributing it inside an MIT
160
+ package would hand reuse rights we do not hold to everyone who installs this;
161
+ `pack:test` fails the build if any font data ever reaches `dist/`.
162
+
163
+ **If you hold a Font Awesome Pro licence**, load your own Pro CSS (or kit) and
164
+ every icon renders exactly as it does in Fusion.
165
+
166
+ **If you do not**, use Font Awesome Free — but read this, because the fit is
167
+ imperfect:
168
+
169
+ ```sh
170
+ npm install @fortawesome/fontawesome-free
171
+ ```
172
+
173
+ ```ts
174
+ import '@fortawesome/fontawesome-free/css/all.min.css';
175
+ ```
176
+
177
+ Free ships `fa-solid`, `fa-regular` and `fa-brands`. It has **no `fa-light` or
178
+ `fa-duotone` family**, and it is missing many Pro glyphs — and `fa-light` is
179
+ exactly what `@kit/ui`'s resolver falls back to for an icon name it doesn't
180
+ recognise. Force everything into a family Free actually has:
181
+
182
+ ```tsx
183
+ import { IconStyleOverrideProvider } from '@ekanos/ui/icon';
184
+
185
+ <IconStyleOverrideProvider value="solid">{children}</IconStyleOverrideProvider>;
186
+ ```
187
+
188
+ Any glyph Free still doesn't have falls back to a question mark rather than
189
+ vanishing (`--ekanos-icon-fa-fallback`, default `'\f059'`).
190
+
191
+ **If you load no Font Awesome at all**, icons do not disappear silently: this
192
+ package's CSS draws a placeholder box (`□`) in their place, so "I forgot the
193
+ icon font" is visibly different from "my code is broken". Change or disable it
194
+ with `--ekanos-icon-fallback`.
195
+
196
+ Note that FA glyphs are sized by `font-size`, not box dimensions, and `Icon`
197
+ derives that from the first `h-*` / `w-*` / `size-*` token in `className` in JS
198
+ — so `<Icon className="h-5 w-5" />` is the right *visual* size even on the
199
+ no-Tailwind path where those utilities have no CSS rule.
65
200
 
66
201
  ## Runtime notes
67
202
 
@@ -110,14 +245,37 @@ runtime is a declared dependency of this package.
110
245
  ## Building / verifying
111
246
 
112
247
  ```bash
113
- pnpm --filter @ekanos/ui build # tsup → dist/ (ESM + .d.ts per subpath)
114
- pnpm --filter @ekanos/ui pack:test # clean-room publish gate (see scripts/pack-test.mjs)
248
+ pnpm --filter @ekanos/ui build # tsup → dist/, then the CSS build → the stylesheets
249
+ pnpm --filter @ekanos/ui pack:test # clean-room publish gate
115
250
  ```
116
251
 
252
+ The build runs in two stages, in this order for a reason: `tsup` bundles the
253
+ components, then the CSS build scans the *built* `dist/*.js` for class
254
+ strings and compiles exactly those through Tailwind. Deriving the utility set
255
+ from the build output rather than a hand-kept list is what makes it impossible
256
+ to add a component whose classes have no CSS.
257
+
258
+ That script carries three fatal gates:
259
+
260
+ - **dangling** — every `var(--x)` the token map reaches for must be defined.
261
+ - **coverage** — the same class strings are re-compiled against Fusion's full
262
+ token map, and the build fails if a component has begun using a token this
263
+ package doesn't ship. Without this, such a utility would just silently emit
264
+ nothing.
265
+ - **lockstep** — `tokens.css` spells its dark values out twice (a media query
266
+ and an explicit selector, because CSS can't share a block between them); both
267
+ must stay in agreement.
268
+
117
269
  `pack:test` packs the tarball, asserts dist-only contents, fails on any
118
270
  `@kit/` leak, asserts `'use client'` preservation, then installs the tarball
119
271
  into a temp project outside the workspace and typechecks
120
272
  (`skipLibCheck: false`) + esbuild-bundles a consumer importing every subpath.
121
273
 
274
+ It also gates the stylesheets: all four must ship, clear a size floor, still
275
+ contain known utilities and both theme states, resolve through esbuild as
276
+ `@ekanos/ui/styles.css`, and carry **no** icon-font data — `@font-face`, a
277
+ webfont reference or a glyph table in `dist/` fails the build, for the same
278
+ licensing reason the Makerkit lineage check exists.
279
+
122
280
  `prepack` runs the build, so a publish can never ship a stale or missing
123
281
  `dist/`.