@plainauth/ui
v0.1.2
Published
shadcn/ui drop-in sign-in and account components for plainauth.
Readme
@plainauth/ui
Installation
Install the UI package and its SDK dependencies from npm:
pnpm add @plainauth/ui @plainauth/nextjsSource and issue reporting: github.com/uripg/plainauth.
Import the stylesheet, and Tailwind will scan this package
These components ship Tailwind class names (bg-primary, bg-popover,
text-destructive, …). Tailwind v4 scans the files under your CSS entry point's own
project and nothing else, so a build that has never been told about this package produces
none of those classes: the drop-ins arrive structurally correct and visually blank — a
<SignIn /> with no background beside a <SignUp variant="outline" /> that looks fine,
because border is a base utility and bg-primary is not.
One import fixes it:
/* app/globals.css */
@import "tailwindcss";
@import "@plainauth/ui/styles.css";@plainauth/ui/styles.css carries the @source declaration pointing at this package's own
sources — resolved relative to itself, so it is correct from your node_modules without you
writing a path — and the shadcn @theme mappings the components render against
(--color-primary, --color-popover, …).
It deliberately does not ship the token values. Those are your theme: --primary,
--popover, --destructive, --accent, --muted, --border, --input, --ring on
:root. If you have run npx shadcn@latest init you already have them, and these
components inherit your brand rather than overriding it.
Why this is an import and not a paragraph telling you to write @source yourself
It was that paragraph, once. The first fix was a hand-written @source path in our own
dashboard plus a note here. That fixed our app and left yours exactly as broken, because the
path existed only in our repository.
The release check mounts every component in real Chromium and asserts its computed styles
differ from the browser's defaults. A mutation control removes the @source declaration and
proves the check fails. DOM-presence assertions are blind to styling — every one of them
passed while these components were completely unstyled.
Drop them into a server component — the boundaries are already where they belong
Nothing here asks you to add "use client". <UserButton /> carries the directive itself,
because it holds session state and runs sign-out; <SignIn /> and <SignUp /> do not, and
must not — they render one <a>, so they stay in your server graph and cost your bundle
nothing. That is the drop-in promise kept literally: you import the tag and render it,
from a server component or a client one, and it works.
// app/page.tsx — a SERVER component, no directive of your own
import { SignIn, UserButton } from "@plainauth/ui";The one thing this implies for you: because <UserButton /> is a client component, its
client prop has to come from createNextBrowserClient() / createBrowserClient() — a
publishable key — and not from anything server-only. That was already true; the directive
just makes React enforce it at the right place.
Why this is a directive and not a paragraph telling you to add a boundary
Same story as the stylesheet above, one layer out. This package shipped with no directive
anywhere and every test, every screenshot and the whole gate were green — because the only
consumer in this repo is our own dashboard, whose page is already a client component. We
were supplying a boundary you do not have. A README line saying "wrap it in a client
component" would have been an admission that "drop-in" means "drop-in once you know one more
thing", and the thing you would have learned it from is a TypeError in production.
The release check installs the packed tarball into a scratch app outside our repository
and imports every published component the way an App Router server component does — under
Node's react-server condition, where React genuinely has no useState. Removing the
directive and repacking takes that check from green to red, naming the module.
