Skip to content

Commit fdf7e38

Browse files
authored
Merge pull request #51 from pras75299/feat/compatibility-matrix-and-e2e
Feat/compatibility matrix and e2e
2 parents 2f30934 + ddf0a65 commit fdf7e38

16 files changed

Lines changed: 929 additions & 17 deletions

File tree

.github/workflows/ci.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,55 @@ jobs:
6262
run: |
6363
pnpm build:registry
6464
git diff --exit-code -- registry.json apps/www/public/registry.json apps/www/public/registry apps/www/components/ui apps/www/config/components.ts apps/www/config/docs-scenarios.ts apps/www/config/demos.tsx || (echo "" && echo "ERROR: generated registry/docs artifacts are out of sync." && echo "Run 'pnpm build:registry' locally and commit the updated generated files." && exit 1)
65+
66+
- name: Validate generated registry artifacts (schema)
67+
run: pnpm registry:validate
68+
69+
e2e-smoke:
70+
name: Docs site smoke (Playwright)
71+
runs-on: ubuntu-latest
72+
needs: test-and-lint
73+
74+
steps:
75+
- uses: actions/checkout@v4.3.0
76+
77+
- uses: pnpm/action-setup@v5.0.0
78+
with:
79+
version: 10.33.0
80+
81+
- uses: actions/setup-node@v4.0.4
82+
with:
83+
node-version: "24"
84+
cache: "pnpm"
85+
86+
- name: Install dependencies
87+
run: pnpm install --frozen-lockfile
88+
89+
- name: Cache Playwright browsers
90+
uses: actions/cache@v4
91+
id: playwright-cache
92+
with:
93+
path: ~/.cache/ms-playwright
94+
key: playwright-${{ runner.os }}-${{ hashFiles('apps/www/package.json') }}
95+
96+
- name: Install Playwright browsers
97+
if: steps.playwright-cache.outputs.cache-hit != 'true'
98+
run: pnpm --dir apps/www test:e2e:install
99+
100+
- name: Install Playwright OS deps
101+
if: steps.playwright-cache.outputs.cache-hit == 'true'
102+
run: pnpm --dir apps/www exec playwright install-deps chromium
103+
104+
- name: Build docs site
105+
run: pnpm --dir apps/www build
106+
107+
- name: Run Playwright smoke
108+
run: pnpm --dir apps/www test:e2e
109+
110+
- name: Upload Playwright report on failure
111+
if: failure()
112+
uses: actions/upload-artifact@v4
113+
with:
114+
name: playwright-report
115+
path: apps/www/playwright-report
116+
retention-days: 7

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
<p align="center">
1111
<a href="https://www.npmjs.com/package/uniqueui-cli"><img src="https://img.shields.io/npm/v/uniqueui-cli.svg" alt="npm version" /></a>
12+
<a href="https://www.npmjs.com/package/uniqueui-cli"><img src="https://img.shields.io/npm/dt/uniqueui-cli.svg" alt="npm total downloads" /></a>
13+
<a href="https://www.npmjs.com/package/uniqueui-cli"><img src="https://img.shields.io/npm/dm/uniqueui-cli.svg" alt="npm monthly downloads" /></a>
1214
<a href="https://github.com/pras75299/uniqueui/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/uniqueui-cli.svg" alt="license" /></a>
1315
<a href="https://nodejs.org"><img src="https://img.shields.io/node/v/uniqueui-cli.svg" alt="node" /></a>
1416
</p>
@@ -97,11 +99,11 @@ If you are exploring or contributing to the repository, these are the main place
9799

98100
### Requirements
99101

100-
| Requirement | Version |
101-
|---|---|
102-
| **Node.js** | ≥ 18 |
103-
| **React** | ≥ 18 |
104-
| **Tailwind CSS** | ≥ 3 |
102+
| Requirement | Version | Notes |
103+
|---|---|---|
104+
| **Node.js (CLI users)** | ≥ 18 | 20 LTS / 22 LTS recommended. The monorepo itself uses Node 24 — that's for contributors only and does not affect end users of `uniqueui-cli`. |
105+
| **React** | 18 or 19 | Components are written to run on both. |
106+
| **Tailwind CSS** | 3.4+ or 4.x | The CLI auto-merges `theme.extend` into a `tailwind.config.*` file (v3 model). On v4 (CSS-first `@theme`) copy keyframes/animation from the component's registry JSON into your `@theme` block manually — see the [compatibility matrix](https://uniqueui-platform.vercel.app/docs/compatibility). |
105107

106108
> All components use [Motion](https://motion.dev) (formerly Framer Motion). The CLI installs `motion` automatically.
107109

apps/www/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,9 @@ yarn-error.log*
3939
# typescript
4040
*.tsbuildinfo
4141
next-env.d.ts
42+
43+
# playwright
44+
/playwright-report/
45+
/test-results/
46+
/blob-report/
47+
/playwright/.cache/
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
import Link from "next/link";
2+
import { ArrowLeft, CheckCircle2, AlertTriangle, Info } from "lucide-react";
3+
4+
export const metadata = {
5+
title: "Compatibility — UniqueUI",
6+
description:
7+
"What we test against in CI vs. what we treat as best-effort. Framework, runtime, and Tailwind support for UniqueUI components and CLI.",
8+
};
9+
10+
type Status = "tested" | "best-effort" | "manual" | "unsupported";
11+
12+
type Row = {
13+
label: string;
14+
version: string;
15+
status: Status;
16+
notes: string;
17+
};
18+
19+
const runtime: Row[] = [
20+
{
21+
label: "Node.js (CLI users)",
22+
version: "≥ 18",
23+
status: "tested",
24+
notes: "Minimum supported by `uniqueui-cli`. Use 20 LTS or 22 LTS for the smoothest experience.",
25+
},
26+
{
27+
label: "Node.js (this repo contributors)",
28+
version: "24.x",
29+
status: "tested",
30+
notes: "Matches `package.json#engines` for monorepo work; mismatch is fine for end users of the CLI.",
31+
},
32+
{
33+
label: "pnpm",
34+
version: "10.33.x",
35+
status: "tested",
36+
notes: "Pinned via `packageManager`. Other PMs work for end users (`pnpm`, `npm`, `yarn`, `bun` all detected).",
37+
},
38+
];
39+
40+
const frameworks: Row[] = [
41+
{
42+
label: "Next.js (App Router)",
43+
version: "15 – 16",
44+
status: "tested",
45+
notes: "The docs site itself runs Next 16. Components use `\"use client\"` where they touch state or motion.",
46+
},
47+
{
48+
label: "Next.js (Pages Router)",
49+
version: "13+",
50+
status: "best-effort",
51+
notes: "Components are framework-agnostic React. No `next/*` imports in registry code.",
52+
},
53+
{
54+
label: "Vite + React",
55+
version: "≥ 5",
56+
status: "best-effort",
57+
notes: "After `npx uniqueui init`, edit `components.json` paths if your project uses `src/` or `app/` differently.",
58+
},
59+
{
60+
label: "Remix / React Router 7",
61+
version: "≥ 2",
62+
status: "best-effort",
63+
notes: "Same caveat as Vite. The CLI does not auto-detect framework yet.",
64+
},
65+
{
66+
label: "Astro (React island)",
67+
version: "≥ 4",
68+
status: "best-effort",
69+
notes: "Wrap each UniqueUI component in `client:load` or `client:visible`.",
70+
},
71+
];
72+
73+
const stack: Row[] = [
74+
{
75+
label: "React",
76+
version: "18 / 19",
77+
status: "tested",
78+
notes: "The docs site runs React 19; components are written to also work on 18.",
79+
},
80+
{
81+
label: "React Server Components",
82+
version: "—",
83+
status: "manual",
84+
notes:
85+
"UniqueUI components are client components by default (`\"use client\"`). Import them from a Server Component normally.",
86+
},
87+
{
88+
label: "TypeScript",
89+
version: "≥ 5",
90+
status: "tested",
91+
notes: "Each registry file is strict-mode safe. The CLI itself ships types.",
92+
},
93+
{
94+
label: "Tailwind CSS v3",
95+
version: "≥ 3.4",
96+
status: "tested",
97+
notes: "The CLI merges component `tailwindConfig.theme.extend` directly into your `tailwind.config.{js,ts}`.",
98+
},
99+
{
100+
label: "Tailwind CSS v4",
101+
version: "≥ 4.0",
102+
status: "manual",
103+
notes:
104+
"The docs site uses v4. The CLI's auto-merge still targets a v3-style config file. If your project is v4 CSS-first, copy the component's keyframes / theme tokens into your `@theme` block manually. We are tracking a first-class v4 path in the backlog.",
105+
},
106+
{
107+
label: "Motion (formerly Framer Motion)",
108+
version: "≥ 12",
109+
status: "tested",
110+
notes: "Components import from `motion/react`. The CLI installs `motion` for you.",
111+
},
112+
];
113+
114+
const cliFlows: Row[] = [
115+
{
116+
label: "`uniqueui init` — Next.js (App Router)",
117+
version: "—",
118+
status: "tested",
119+
notes: "Smoke-tested locally and in CI.",
120+
},
121+
{
122+
label: "`uniqueui add <slug>` — every registry component",
123+
version: "—",
124+
status: "tested",
125+
notes: "`scripts/test-all-components.ts` installs each component into a fresh Next app and builds.",
126+
},
127+
{
128+
label: "shadcn registry — `npx shadcn add .../r/<slug>.json`",
129+
version: "—",
130+
status: "tested",
131+
notes: "`pnpm test:e2e:shadcn` adds every item with the shadcn CLI and runs `next build`.",
132+
},
133+
{
134+
label: "Offline / airgapped install",
135+
version: "—",
136+
status: "manual",
137+
notes: "Pass `--url file://path/to/built/registry` to point the CLI at a local checkout.",
138+
},
139+
];
140+
141+
const STATUS_META: Record<Status, { label: string; bg: string; text: string; icon: React.ComponentType<{ className?: string }> }> = {
142+
tested: {
143+
label: "Tested in CI",
144+
bg: "bg-emerald-50 dark:bg-emerald-500/10 border-emerald-200 dark:border-emerald-500/30",
145+
text: "text-emerald-700 dark:text-emerald-300",
146+
icon: CheckCircle2,
147+
},
148+
"best-effort": {
149+
label: "Best effort",
150+
bg: "bg-amber-50 dark:bg-amber-500/10 border-amber-200 dark:border-amber-500/30",
151+
text: "text-amber-700 dark:text-amber-300",
152+
icon: Info,
153+
},
154+
manual: {
155+
label: "Manual step",
156+
bg: "bg-blue-50 dark:bg-blue-500/10 border-blue-200 dark:border-blue-500/30",
157+
text: "text-blue-700 dark:text-blue-300",
158+
icon: AlertTriangle,
159+
},
160+
unsupported: {
161+
label: "Not supported",
162+
bg: "bg-rose-50 dark:bg-rose-500/10 border-rose-200 dark:border-rose-500/30",
163+
text: "text-rose-700 dark:text-rose-300",
164+
icon: AlertTriangle,
165+
},
166+
};
167+
168+
function StatusBadge({ status }: { status: Status }) {
169+
const meta = STATUS_META[status];
170+
const Icon = meta.icon;
171+
return (
172+
<span
173+
className={`inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-[11px] font-medium ${meta.bg} ${meta.text}`}
174+
>
175+
<Icon className="h-3 w-3" aria-hidden />
176+
{meta.label}
177+
</span>
178+
);
179+
}
180+
181+
function Matrix({ title, rows }: { title: string; rows: Row[] }) {
182+
return (
183+
<section className="space-y-4">
184+
<h2 className="text-2xl font-bold text-neutral-900 dark:text-white">{title}</h2>
185+
<div className="overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-800">
186+
<table className="w-full text-sm">
187+
<thead className="bg-neutral-50 dark:bg-neutral-900/40 text-neutral-500 dark:text-neutral-400">
188+
<tr>
189+
<th scope="col" className="px-4 py-2 text-left font-medium">Target</th>
190+
<th scope="col" className="px-4 py-2 text-left font-medium">Version</th>
191+
<th scope="col" className="px-4 py-2 text-left font-medium">Status</th>
192+
<th scope="col" className="px-4 py-2 text-left font-medium">Notes</th>
193+
</tr>
194+
</thead>
195+
<tbody className="divide-y divide-neutral-200 dark:divide-neutral-800">
196+
{rows.map((row) => (
197+
<tr key={row.label} className="bg-white dark:bg-neutral-950/40 align-top">
198+
<td className="px-4 py-3 font-medium text-neutral-900 dark:text-white">{row.label}</td>
199+
<td className="px-4 py-3 font-mono text-xs text-neutral-700 dark:text-neutral-300">{row.version}</td>
200+
<td className="px-4 py-3"><StatusBadge status={row.status} /></td>
201+
<td className="px-4 py-3 text-neutral-600 dark:text-neutral-400">{row.notes}</td>
202+
</tr>
203+
))}
204+
</tbody>
205+
</table>
206+
</div>
207+
</section>
208+
);
209+
}
210+
211+
export default function CompatibilityPage() {
212+
return (
213+
<div className="space-y-12">
214+
<div className="space-y-4">
215+
<Link
216+
href="/docs"
217+
className="inline-flex items-center gap-1.5 text-sm text-neutral-500 hover:text-neutral-900 dark:hover:text-white transition-colors"
218+
>
219+
<ArrowLeft className="h-3.5 w-3.5" aria-hidden />
220+
Back to docs
221+
</Link>
222+
<h1 className="text-4xl font-bold tracking-tight text-neutral-900 dark:text-white sm:text-5xl">
223+
Compatibility
224+
</h1>
225+
<p className="max-w-2xl text-lg leading-relaxed text-neutral-600 dark:text-neutral-400">
226+
What we verify on every push, versus what we treat as best-effort.
227+
If your stack lands in a row marked <strong>Manual step</strong>, the docs note tells you exactly what to do.
228+
</p>
229+
</div>
230+
231+
<section className="rounded-xl border border-neutral-200 bg-neutral-50 p-5 text-sm leading-relaxed text-neutral-700 dark:border-neutral-800 dark:bg-neutral-900/40 dark:text-neutral-300">
232+
<p className="mb-3 font-semibold text-neutral-900 dark:text-white">Legend</p>
233+
<ul className="space-y-2">
234+
<li className="flex flex-wrap items-center gap-2">
235+
<StatusBadge status="tested" />
236+
<span>Exercised on every CI run (unit, E2E smoke, registry validation, or per-component build).</span>
237+
</li>
238+
<li className="flex flex-wrap items-center gap-2">
239+
<StatusBadge status="best-effort" />
240+
<span>We expect it to work because the code is framework-agnostic, but it isn&apos;t in our test matrix yet.</span>
241+
</li>
242+
<li className="flex flex-wrap items-center gap-2">
243+
<StatusBadge status="manual" />
244+
<span>Works, but requires a one-time setup step documented in the notes column.</span>
245+
</li>
246+
</ul>
247+
</section>
248+
249+
<Matrix title="Runtime" rows={runtime} />
250+
<Matrix title="React stack" rows={stack} />
251+
<Matrix title="Frameworks" rows={frameworks} />
252+
<Matrix title="CLI flows" rows={cliFlows} />
253+
254+
<section className="space-y-3 rounded-xl border border-amber-300/60 bg-amber-50 p-5 text-sm leading-relaxed text-amber-800 dark:border-amber-500/20 dark:bg-amber-500/5 dark:text-amber-200">
255+
<p className="font-semibold">Tailwind v4 — what to do today</p>
256+
<p>
257+
A v4 project&apos;s tokens live in CSS (<code className="font-mono text-xs">@theme</code> in your global stylesheet),
258+
not in a JS <code className="font-mono text-xs">tailwind.config</code>. When a UniqueUI component declares
259+
custom keyframes (e.g. <code className="font-mono text-xs">moving-border</code>, <code className="font-mono text-xs">aurora-background</code>),
260+
the CLI&apos;s automatic merge won&apos;t find a config object to write into, so you need to copy the
261+
<code className="font-mono text-xs"> theme.extend.keyframes</code> and
262+
<code className="font-mono text-xs"> theme.extend.animation</code> entries from the component&apos;s registry JSON
263+
(<code className="font-mono text-xs">https://uniqueui-platform.vercel.app/registry/&lt;slug&gt;.json</code>) into your
264+
<code className="font-mono text-xs"> @theme</code> block. A first-class v4 emission path is on the roadmap.
265+
</p>
266+
</section>
267+
268+
<section className="space-y-2 text-sm text-neutral-500 dark:text-neutral-400">
269+
<p>
270+
Found a gap? Open an issue at{" "}
271+
<a
272+
className="text-purple-600 underline underline-offset-2 dark:text-purple-400"
273+
href="https://github.com/pras75299/uniqueui/issues/new"
274+
>
275+
github.com/pras75299/uniqueui/issues
276+
</a>{" "}
277+
with your stack versions and the error you hit.
278+
</p>
279+
</section>
280+
</div>
281+
);
282+
}

apps/www/app/docs/page.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,23 @@ export default function Hero() {
289289
</div>
290290
</section>
291291

292+
{/* ── Compatibility CTA ── */}
293+
<div className="animate-fade-in-up animate-delay-325 rounded-2xl border border-neutral-200 dark:border-neutral-800 bg-neutral-50 dark:bg-neutral-900/40 p-8 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6">
294+
<div className="space-y-1">
295+
<p className="font-semibold text-neutral-900 dark:text-white">Compatibility matrix</p>
296+
<p className="text-sm text-neutral-500 dark:text-neutral-400">
297+
Which Node, React, Next, and Tailwind versions we verify in CI — and what to do on Tailwind v4.
298+
</p>
299+
</div>
300+
<Link
301+
href="/docs/compatibility"
302+
className="shrink-0 flex items-center gap-2 px-5 py-2.5 rounded-xl font-medium text-sm border border-neutral-200 bg-white text-neutral-900 hover:bg-neutral-100 dark:border-neutral-800 dark:bg-neutral-900 dark:text-white dark:hover:bg-neutral-800 transition-colors"
303+
>
304+
View matrix
305+
<ArrowRight className="w-4 h-4" />
306+
</Link>
307+
</div>
308+
292309
{/* ── CTA ── */}
293310
<div className="animate-fade-in-up animate-delay-350 rounded-2xl border border-neutral-200 dark:border-neutral-800 bg-neutral-50 dark:bg-neutral-900/40 p-8 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6">
294311
<div className="space-y-1">

0 commit comments

Comments
 (0)