---
title: "Getting started"
description: "Go from an empty shadcn project to a correctly formatted figure on screen. Install the theme first. Every component compiles against its tokens and type scale, and without them a component renders at the wrong sizes with no error."
canonical: "https://lopifi.com/docs/guides/getting-started"
---

# Getting started

> Go from an empty shadcn project to a correctly formatted figure on screen. Install the theme first. Every component compiles against its tokens and type scale, and without them a component renders at the wrong sizes with no error.

Source: https://lopifi.com/docs/guides/getting-started
Part of: Lopifi — Planned: US GAAP books on autopilot, checked by a CPA (https://lopifi.com)

---

# Getting started

Go from an empty shadcn project to a correctly formatted figure on screen. Install the theme first. Every component compiles against its tokens and type scale, and without them a component renders at the wrong sizes with no error.

---

## 1. Have a shadcn project

Components install through the [shadcn CLI](https://ui.shadcn.com/docs/cli) into a React project that already has Tailwind and a `components.json`. If you don't have one yet:

```bash
npx shadcn@latest init
```

## 2. Add the registry to `components.json`

The `@lopifi` namespace is a [namespaced registry](https://ui.shadcn.com/docs/registry/namespace). Add it once:

```json
{
  "registries": {
    "@lopifi": "https://lopifi.com/r/{name}.json"
  }
}
```

Every item is then available as `@lopifi/<name>`, and its `registryDependencies` pull in whatever else it needs.

## 3. Install the theme first

```bash
npx shadcn@latest add @lopifi/theme
```

This writes two files:

- `styles/ledger-tokens.css` — the tokens: color (both themes), spacing, radius, elevation, motion, the two type families.
- `ledger/preset.ts` — the Tailwind preset: the same tokens as Tailwind scales, including the type scale (`text-body`, `text-figure-md`, …).

Import the tokens once, in your root layout or global stylesheet, before your own styles:

```ts
import "@/styles/ledger-tokens.css";
```

## 4. Wire Tailwind

**Tailwind 3**: add the preset to `tailwind.config.ts` and make sure `content` covers the folder the components were written to:

```ts
import type { Config } from "tailwindcss";
import ledger from "./ledger/preset";

export default {
  presets: [ledger],
  content: ["./src/**/*.{ts,tsx}"],
} satisfies Config;
```

**Tailwind 4**: v4 is configured in CSS. Use the CSS form of the theme that ships in `@lopifi/theme` (an `@theme` block with the same scales as the preset) and import it from your main stylesheet after `@import "tailwindcss";`. Don't load both the preset and the `@theme` file, since they declare the same scales. If your v4 project still uses a JS config through `@config`, the v3 preset works there too.

## 5. Install `@lopifi/cn`

Most components pull it in for you, but here's why it exists:

```bash
npx shadcn@latest add @lopifi/cn
```

It lands at `lib/ledger/cn.ts`. Components import this `cn`, not your project's `@/lib/utils`. shadcn's `cn` uses a `tailwind-merge` that only knows Tailwind's built-in `text-sm`…`text-9xl`. It treats any other `text-*` class as a text color, so `cn("text-body text-text-primary")` silently drops the size. The Lopifi `cn` registers the type scale with `tailwind-merge`. If you add a font size to the preset, add it to `TYPE_SCALE` in `cn.ts` in the same change.

## 6. Your first component

```bash
npx shadcn@latest add @lopifi/money
```

```tsx
import { Money } from "@/components/ledger/money";

export function Example() {
  return (
    <p>
      <Money value={{ amount: 124056, currency: "USD" }} />{" "}
      <Money accounting value={{ amount: -124056, currency: "USD" }} />{" "}
      <Money value={{ amount: 1200, currency: "JPY" }} />{" "}
      <Money value={null} />
    </p>
  );
}
```

The amount is an integer in **minor units** (`124056` is $1,240.56), and the currency travels with it. Decimals come from the currency, not a hard-coded 2.

## 7. Verify

You're set up when all of these are true:

- `$1,240.56` renders in the figure face (Geist Mono if your app loads it), with tabular digits.
- The accounting negative renders as `($1,240.56)`, and the parentheses do not shift the digits when stacked in a right-aligned column against a positive.
- JPY renders with no decimals.
- `null` renders as an explicit "not available" state, never `$0.00`.
- Setting `data-theme="dark"` on `<html>` switches the surfaces and value colors.

---

## Troubleshooting

### The components render, but sizes and colors are wrong

The preset isn't applied or the tokens aren't loaded. Check that:

- `styles/ledger-tokens.css` is imported once, globally, not only inside a page.
- Tailwind 3: `presets: [ledger]` is in the config Tailwind actually reads, and `content` includes the directory the components were written to. Tailwind only generates classes it has seen.
- Tailwind 4: the `@theme` file is imported from the stylesheet that imports `tailwindcss`.

Quick test: an element with `className="text-figure-md bg-canvas"` should get a font size and a background. If it gets neither, the scales aren't registered.

### `text-body` (or another type class) disappears next to a color class

Something is calling shadcn's `cn` from `@/lib/utils` instead of `@/lib/ledger/cn`. See step 5. The element renders at the inherited size with the right color.

### Module not found: `@base-ui-components/react`, or a shadcn wrapper

Some interactive components (popovers, dialogs, checkboxes) are built on Base UI or a shadcn primitive. The registry item lists the package under `dependencies` and the shadcn CLI installs it. If it didn't (a lockfile conflict, an offline install, a monorepo where the CLI ran in the wrong package), install it in the package that owns the component, or rerun `add` from that package's root. If the missing import is `@/components/ui/<name>`, add the shadcn component of that name (`npx shadcn@latest add <name>`).

### Hydration mismatch warnings

Usually one of two causes:

- **Locale or time zone.** A figure or date formatted in one locale on the server and another on the client produces different text. Pass an explicit `locale` to `Money`, and format dates with an explicit time zone, wherever server and browser may disagree.
- **Relative time.** "as of 2 minutes ago" depends on the current time, which differs between server and client. Render the absolute timestamp on the server, or render the relative form only after mount.

A warning about `data-theme` on `<html>` comes from setting the theme before React hydrates. That attribute is expected to differ, so add `suppressHydrationWarning` to the `<html>` element only.

### Anything else

Email [hello@lopifi.com](mailto:hello@lopifi.com) with the component, the smallest snippet that reproduces it, what you expected, and your React and Tailwind versions. A wrong accounting rule is as much a bug as a broken layout. Say which document states it and cite the authority you're relying on. The repo's `CONTRIBUTING.md` has the full checklist.
