FROM PREVIEW TO YOUR PROJECT
Your first block.
LEDGER is a collection of source files. Copy the blocks you need and edit them in your app. This guide uses Next.js App Router, TypeScript, and Tailwind CSS v4.
1. Prepare your app
Already using this stack? Skip the create command. For a new app, keep the default @/* import alias.
npx create-next-app@latest my-app --ts --tailwind --app
cd my-appInstall the icon package and the two utilities used by blocks that import cn. Each block’s Code tab lists any other dependencies.
npm install lucide-react clsx tailwind-mergeimport { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}2. Bring the design with you
Choose a preset in the header or the design system. Copy its stylesheet into your global CSS file. In an existing app, merge the theme definitions and styles with your own CSS, keeping only one Tailwind import.
Current preset: Ledger. Both light and dark colors are included. Add the dark class to your root element to enable dark mode.
View and copy theme CSS
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
@theme inline {
--color-ledger-paper: var(--ledger-paper);
--color-ledger-ink: var(--ledger-ink);
--color-ledger-muted: var(--ledger-muted);
--color-ledger-rule: var(--ledger-rule);
--color-ledger-signal: var(--ledger-signal);
--font-sans: var(--design-font);
--font-mono: var(--font-geist-mono, ui-monospace), monospace;
}
:root {
--ledger-paper: #f4f1ea;
--ledger-ink: #171717;
--ledger-muted: #6b645b;
--ledger-rule: #d9d1c3;
--ledger-signal: #c43e1c;
--design-font: var(--font-geist-sans, ui-sans-serif), sans-serif;
--spacing: 0.25rem;
color-scheme: light;
}
.dark {
--ledger-paper: #12110f;
--ledger-ink: #ede8de;
--ledger-muted: #9e9689;
--ledger-rule: #2a2722;
--ledger-signal: #e76b49;
color-scheme: dark;
}
body {
background: var(--ledger-paper);
color: var(--ledger-ink);
font-family: var(--design-font);
-webkit-font-smoothing: antialiased;
}
* { border-radius: 0; box-shadow: none; }
:is(button, input, select, textarea, [class~="border"]) { border-radius: 0px; }
::selection { background: var(--ledger-signal); color: var(--ledger-paper); }
:focus-visible { outline: 2px solid var(--ledger-signal); outline-offset: 3px; }
.ledger-link { position: relative; display: inline-block; color: var(--ledger-ink); text-decoration: none; }
.ledger-link::after { content: ""; position: absolute; bottom: -2px; left: 0; width: 40px; height: 2px; background: var(--ledger-signal); transition: width 120ms ease-out; }
.ledger-link:hover::after, .ledger-link:focus-visible::after { width: 100%; }
.bg-grid-pattern { background-image: radial-gradient(var(--ledger-ink) 1px, transparent 1px); background-size: 24px 24px; }
For the original typography, load Geist in your root layout. The exported CSS also includes system-font fallbacks.
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const sans = Geist({ subsets: ["latin"], variable: "--font-geist-sans" });
const mono = Geist_Mono({ subsets: ["latin"], variable: "--font-geist-mono" });
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={`${sans.variable} ${mono.variable}`}>
<body>{children}</body>
</html>
);
}3. Copy a block and use it
Open Login, select Copy code, and save the source as components/blocks/login.tsx. Keep any "use client" directive in the source. Then import the named export:
import { Login01 } from "@/components/blocks/login";
export default function Page() {
return <Login01 />;
}Blocks demonstrate UI with sample data. Connect forms, links, authentication, and actions to your own application before shipping. The optional state prop lets you preview the states supported by each block.