shadcn screens
StableFive screens in the shadcn/ui style: a hero, an inset sidebar shell, a login card, a site header and a loading screen.
Made from raw elements in shadcn/ui’s shapes and neutral theme (Card, Badge, Sidebar with the inset variant, Navigation Menu, Progress, Skeleton), so they drop into a shadcn project where you can swap in the real components. Hero and Navbar take no props.
shadcn hero
import { ShadcnHero } from '@rchursin/screens'
/** * A hero in the shadcn/ui style: its own header, the mark on a card, a badge link, the * headline and two buttons. The page wraps it in a <main>. Bracketed copy is yours to replace. */export default function ShadcnHeroPage() { return ( <main className="min-h-dvh bg-white dark:bg-neutral-950"> <ShadcnHero /> </main> )}Usage
import { ShadcnAppShell, ShadcnHero, ShadcnLoading, ShadcnNavbar, ShadcnSignIn,} from '@rchursin/screens'<ShadcnAppShell> <h1 className="text-2xl font-semibold">Overview</h1> {/* your page */}</ShadcnAppShell>Examples
shadcn app shell
The inset sidebar with a workspace switcher, a nav group and the user menu, around a sample overview.
import { SampleBadge } from '@rchursin/product/shell/layout'import { ShadcnAppShell } from '@rchursin/screens'
/** * The inset sidebar layout (workspace switcher, a nav group, the user menu) with a page inside. * The shell has a <main> and a breadcrumb but no heading, so the content starts with the * page's <h1>. The sidebar shows from 768px up. */const cards = [ { label: 'Agents in use', value: '3 of 12', note: 'Nine have not run in 30 days' }, { label: 'Context per turn', value: '~8.1k', note: 'Tokens of skill descriptions' }, { label: 'Overlaps', value: '6', note: 'Pairs of skills doing one job' },]
export default function ShadcnAppShellPage() { return ( <ShadcnAppShell> <div className="flex flex-wrap items-center justify-between gap-3 pt-2"> <h1 className="text-2xl font-semibold tracking-tight">Overview</h1> <SampleBadge /> </div> <div className="grid grid-cols-1 gap-4 sm:grid-cols-3"> {cards.map((c) => ( <div key={c.label} className="flex flex-col gap-1.5 rounded-[14px] bg-neutral-100 p-5 dark:bg-neutral-900"> <p className="text-sm text-neutral-600 dark:text-neutral-400">{c.label}</p> <p className="text-3xl font-semibold tracking-tight tabular-nums">{c.value}</p> <p className="text-xs text-neutral-600 dark:text-neutral-400">{c.note}</p> </div> ))} </div> <div className="flex min-h-40 flex-1 items-center justify-center rounded-[14px] border border-dashed border-neutral-300 text-sm text-neutral-600 dark:border-white/15 dark:text-neutral-400"> Your chart or table </div> </ShadcnAppShell> )}shadcn sign-in
The login block: logo, SSO, a divider, email and password, and the terms underneath.
import { ShadcnSignIn } from '@rchursin/screens'
/** * The login block: the logo above a card with SSO, a divider, email and password. Its title * is an <h2>, so the page adds a <main> and a visually hidden <h1>. Connect your own submit. */export default function ShadcnSignInPage() { return ( <main> <h1 className="sr-only">Sign in to RChursin_</h1> <ShadcnSignIn /> </main> )}shadcn navbar
Navigation menu, a search button with ⌘K and two buttons, over an empty page.
import { ShadcnNavbar } from '@rchursin/screens'
/** * The site header: logo, a navigation menu, a search button with ⌘K and two buttons. The * "Product" trigger and the search button are placeholders with no menu or dialog yet. */export default function ShadcnNavbarPage() { return ( <div className="min-h-dvh bg-white text-neutral-950 dark:bg-neutral-950 dark:text-neutral-50"> <ShadcnNavbar /> <main className="mx-auto flex max-w-5xl flex-col gap-4 px-6 pt-14 pb-24"> <h1 className="text-4xl font-semibold tracking-tight text-balance">[Your page title]</h1> <p className="max-w-xl text-lg text-pretty text-neutral-600 dark:text-neutral-400">[One line on what this page is for.]</p> <div className="mt-4 flex min-h-80 items-center justify-center rounded-[14px] border border-dashed border-neutral-300 text-sm text-neutral-600 dark:border-white/15 dark:text-neutral-400"> Page content </div> </main> </div> )}shadcn loading screen
A progress bar with its value, a spinner and skeleton rows.
import { ShadcnLoading } from '@rchursin/screens'
/** * A loading screen with a real progress bar (role="progressbar" with its value), a spinner and * skeleton rows. Pass value from 0 to 100. The page adds a <main> and a hidden <h1>. */export default function ShadcnLoadingPage() { return ( <main> <h1 className="sr-only">Loading your workspace</h1> <ShadcnLoading value={62} /> </main> )}API
ShadcnAppShell props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | – | The page inside <main>. Without it, three placeholder tiles and a panel. Start it with the page’s <h1>: the shell has none. |
className | string | 'min-h-dvh' | Classes on the root. Sets the height: pass your own (e.g. "h-[640px]") to fit a frame. |
ShadcnSignIn props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | 'min-h-dvh' | Classes on the root. Sets the height: pass your own (e.g. "h-[640px]") to fit a frame. |
ShadcnLoading props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | 'min-h-dvh' | Classes on the root. Sets the height: pass your own (e.g. "h-[640px]") to fit a frame. |
value | number | 62 | Progress from 0 to 100: the bar’s width and its aria-valuenow. |
Accessibility
Keyboard
| Key | What it does |
|---|---|
| Tab | Moves through the links, fields and buttons in reading order. Every control is a native element. |
| Enter | In a sign-in form: submits it. The form only calls preventDefault(), so connect your own handler. |
- The sidebar’s nav is labelled by its group title (“Platform”); the header has a “Toggle sidebar” button and a breadcrumb with aria-current="page".
- The progress bar is role="progressbar" with a name and its value from 0 to 100; the whole loading screen is a role="status" region.
- Placeholders that open nothing yet: the workspace and user buttons, “Toggle sidebar”, the Navbar’s “Product” trigger and its search button.
- The sidebar is hidden below 768 pixels with no phone menu. The spinner and the skeleton only move when motion is allowed (motion-safe:animate-spin, motion-safe:animate-pulse).
- Sign-in fields have labels and autocomplete (email, current-password); the title is an <h2>, so give the page an <h1>.
- The header’s “Sign in” and “Get started” buttons never wrap or shrink, so at phone widths their labels stay on one line.
Guidelines
Do
- Copy the screen into your app and swap its placeholder links, [bracketed] copy and handlers for your own.
- Wrap a screen without a <main> in one, and give a sign-in or loading page an <h1> (hidden is fine), as the examples do.
- Keep the shadcn/ui look within a screen: its spacing, radius and round-capped icons belong together.
Don’t
- Don’t ship the placeholder buttons that open nothing (menu triggers, search) without wiring them up.
- Don’t mix screens from two styles on one page.
- Don’t pass a height class that cuts off the content on a phone; min-h-dvh is the default for a reason.
In use
Related
- Tailwind Plus screensFive screens in the Tailwind Plus style: a centred hero, a sidebar app shell, a sign-in form, a header and a loading screen.
- Magic UI screensFive screens with Magic UI’s effects: retro grid, border beam, shiny and gradient text, shimmer buttons, ripples and a dock.
- Aceternity screensFive screens in the Aceternity style: a spotlight hero, a borderless sidebar, a signup-style form, a floating navbar and a lamp.
- Screen iconsFifteen small round-capped icons used by the library-style screens, each its own component.
- SpinnerThe loader icon turning once every 900 ms, only when motion is allowed. Decorative next to words; give it a label when it stands alone.
- Auth layoutThe frame for every sign-in page: a dark brand panel when there is room, one calm form column, the logo on top and legal links below.
Source
The component’s own files, exactly as they run on this site.
/** * shadcn/ui style · Sidebar (inset variant) with workspace switcher, nav group, * user menu, and a header with sidebar trigger + breadcrumb. * In a shadcn project use <SidebarProvider>, <Sidebar variant="inset">, <SidebarInset>. * Pass `children` for page content and `className` to control height (defaults to full viewport). */import { useId, type ComponentType, type ReactNode, type SVGProps } from "react"import { Logo } from "../../brand/Logo"import { cx } from "../../components/cx"import { ActivityIcon, ChevronRightIcon, ChevronsUpDownIcon, FolderIcon, HomeIcon, PanelLeftIcon, SlidersIcon,} from "../../components/icons"
type NavItem = { label: string; icon: ComponentType<SVGProps<SVGSVGElement>>; current?: boolean }