Magic UI screens
StableFive screens with Magic UI’s effects: retro grid, border beam, shiny and gradient text, shimmer buttons, ripples and a dock.
The effects are recreated in plain CSS (styles/effects.css), so there is nothing to install: each light passes once or twice on arrival and then rests, and nothing moves with reduced motion. The app shell is a browser window with a dock under it. Hero takes no props; Navbar wraps your page.
Magic UI hero
import { MagicUiHero } from '@rchursin/screens'
/** * A hero in the Magic UI style: a retro grid floor, the mark in a tile with a running border * beam, a shiny announcement pill, a gradient headline and a shimmer button. Each light passes * once or twice on arrival, then rests; nothing moves with reduced motion. The page wraps it * in a <main>. */export default function MagicUiHeroPage() { return ( <main className="min-h-dvh bg-white dark:bg-ink"> <MagicUiHero /> </main> )}Usage
import { MagicUiAppShell, MagicUiHero, MagicUiLoading, MagicUiNavbar, MagicUiSignIn,} from '@rchursin/screens'<MagicUiNavbar className="min-h-dvh"> <main>{/* your page */}</main></MagicUiNavbar>Examples
Magic UI app shell
The app in a browser window with a dock below, holding a sample list of the most used skills.
import { SampleBadge } from '@rchursin/product/shell/layout'import { MagicUiAppShell } from '@rchursin/screens'
/** * The app inside a browser window, with a dock below it, over a dot pattern. The window holds * a <main>; the page's <h1> goes at the top of your content. */const skills = [ { name: 'pr-review', runs: 212 }, { name: 'release-notes', runs: 48 }, { name: 'code-explorer', runs: 41 }, { name: 'commit-msg', runs: 36 },]
export default function MagicUiAppShellPage() { return ( <MagicUiAppShell> <div className="flex flex-wrap items-center justify-between gap-3"> <h1 className="text-xl font-semibold tracking-tight">Most used skills</h1> <SampleBadge /> </div> <ol className="flex flex-col gap-2"> {skills.map((s, i) => ( <li key={s.name} className="flex items-center gap-3 rounded-xl bg-neutral-50 px-4 py-3 text-sm ring-1 ring-neutral-100 ring-inset dark:bg-white/4 dark:ring-white/6"> <span className="w-4 font-mono text-xs text-fg-3 tabular-nums">{i + 1}</span> <span className="min-w-0 flex-1 truncate font-mono">{s.name}</span> <span className="text-fg-3 tabular-nums">{s.runs} runs</span> </li> ))} </ol> </MagicUiAppShell> )}Magic UI sign-in
A card with a shining border, a shimmer submit button and SSO, over a dot pattern.
import { MagicUiSignIn } from '@rchursin/screens'
/** * Sign in on a card with a shining border, a shimmer submit button and SSO. Its title is an * <h2>, so the page adds a <main> and a visually hidden <h1>. Connect your own submit. */export default function MagicUiSignInPage() { return ( <main> <h1 className="sr-only">Sign in to RChursin_</h1> <MagicUiSignIn /> </main> )}Magic UI navbar
A sticky, translucent header with a shiny pill and a shimmer button, over a dot pattern.
import { MagicUiNavbar } from '@rchursin/screens'
/** * A sticky, translucent header over a dot pattern, with a shiny announcement pill and a * shimmer button. Your page goes in as children, under the bar. */export default function MagicUiNavbarPage() { return ( <MagicUiNavbar className="min-h-dvh"> <main className="mx-auto flex max-w-3xl flex-col items-center gap-5 px-6 pt-24 pb-32 text-center"> <h1 className="text-4xl font-medium tracking-[-0.03em] text-balance sm:text-5xl">[Your page title]</h1> <p className="max-w-xl text-lg text-pretty text-stone dark:text-fog">[One line on what this page is for.]</p> <div className="mt-6 flex min-h-72 w-full items-center justify-center rounded-2xl border border-dashed border-neutral-900/15 bg-white/60 text-sm text-stone dark:border-white/15 dark:bg-ink/60 dark:text-fog"> Page content </div> </main> </MagicUiNavbar> )}Magic UI loading screen
Ripple rings behind the mark and a line of shiny text.
import { MagicUiLoading } from '@rchursin/screens'
/** * Ripple rings behind the mark and a line of shiny text, inside a role="status" region. The * rings keep rippling while it shows (it is a loading indicator) and stay still with reduced * motion. The page adds a <main> and a hidden <h1>. */export default function MagicUiLoadingPage() { return ( <main> <h1 className="sr-only">Loading your workspace</h1> <MagicUiLoading message="Loading workspace" /> </main> )}API
MagicUiAppShell props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | – | The page inside the window’s <main>. Without it, placeholder tiles. Start it with the page’s <h1>. |
className | string | 'min-h-dvh' | Classes on the root. Sets the height: pass your own (e.g. "h-[640px]") to fit a frame. |
MagicUiNavbar props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | – | Your page, under the sticky bar and over the dot pattern. Put your <main> here. |
className | string | – | Classes on the wrapper. It has no height of its own: pass min-h-dvh for a full page. |
MagicUiSignIn 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. |
MagicUiLoading 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. |
message | string | 'Loading workspace' | The shiny line under the mark, inside the status region. |
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. |
- Every effect layer (grid, dots, beams, ripples) is aria-hidden. With reduced motion the beams, shine and ripples stop; the content never depends on them.
- The dock is a navigation named “Dock”; each icon link carries its name (“Home”, “Search”…), and the first one is named “RChursin_”.
- The loading screen is a role="status" region. Its ripple runs for as long as it is shown, as a loading indicator should, and stops with reduced motion.
- The shimmer submit button is a real <button type="submit">; sign-in fields have labels and autocomplete. The title is an <h2>, so give the page an <h1>.
- The Navbar hides its links below 768 pixels and its announcement pill below 640, with no phone menu.
- The hero’s own header shows its three links from 640 pixels up; below that only the logo remains, so nothing runs into it.
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 Magic 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
- Effect buttonsA shimmer button, a shiny announcement pill and a glowing border, from the launch-page screens.
- Animated accentsShiny and gradient text, a beam round a border, a shine border, ripples, lines that type in and a scanning column.
- BackgroundsDot and grid patterns, masks and vignettes to fade them, a retro floor grid, a spotlight, a lamp and a softly lit card.
- 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.
- shadcn screensFive screens in the shadcn/ui style: a hero, an inset sidebar shell, a login card, a site header and a loading screen.
Source
The component’s own files, exactly as they run on this site.
/** * Magic UI style · App shell: the app inside a Safari-style window, with a Dock below, over a Dot Pattern. * Pass `children` for page content and `className` to control height (defaults to full viewport). */import type { ComponentType, ReactNode, SVGProps } from "react"import { Logo } from "../../brand/Logo"import { cx } from "../../components/cx"import { ActivityIcon, FolderIcon, HomeIcon, SearchIcon, SlidersIcon } from "../../components/icons"
const dock: { label: string; icon: ComponentType<SVGProps<SVGSVGElement>> }[] = [ { label: "Home", icon: HomeIcon }, { label: "Search", icon: SearchIcon }, { label: "Projects", icon: FolderIcon }, { label: "Activity", icon: ActivityIcon }, { label: "Settings", icon: SlidersIcon },]
export function MagicUiAppShell({ children, className,