# Getting started

> From an empty shadcn project to a correctly formatted figure on screen. The order matters: the theme goes in first, because every component compiles against its tokens and its type scale, and a component installed without it renders with the wrong sizes and no error.

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

---

# Getting started

From an empty shadcn project to a correctly formatted figure on screen. The order matters: the theme goes in first, because every component compiles against its tokens and its type scale, and a component installed without it renders with the wrong sizes and no error.

---

## 1. Have a shadcn project

The 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 do not 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 addressable as `@lopifi/<name>`, and each item's own `registryDependencies` pull in what it needs.

## 3. Install the theme first

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

This writes two files:

- `styles/ledger-tokens.css` — the tokens: colour (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, not in a JS config. Use the CSS form of the theme that ships in the `@lopifi/theme` item (an `@theme` block carrying the same scales as the preset) and import it from your main stylesheet after `@import "tailwindcss";`. Do not load both the preset and the `@theme` file — 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 it is worth knowing 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 files any other `text-*` class under text colour, 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. The decimals come from the currency, not from a hard-coded 2.

## 7. Verify

You are set up correctly 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 the value colours.

---

## Troubleshooting

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

The preset is not applied, or the tokens are not 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. A class Tailwind never saw is a class that was never generated.
- Tailwind 4: the `@theme` file is imported from the stylesheet that imports `tailwindcss`.

A 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 are not registered.

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

A component, or your own code, is calling shadcn's `cn` from `@/lib/utils` instead of `@/lib/ledger/cn`. See step 5. The symptom is that the element renders at the inherited size with the right colour.

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

Some interactive components (popovers, dialogs, checkboxes) are built on Base UI or on a shadcn primitive. The registry item lists the package under `dependencies` and the shadcn CLI installs it; if it did not (a lockfile conflict, an offline install, a monorepo where the CLI ran in the wrong package), install it yourself in the package that owns the component, or run the `add` again 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 things:

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

If the warning is about `data-theme` on `<html>`, it 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 are relying on. The repo's `CONTRIBUTING.md` has the full checklist.
