# Documentation

A technical overview of how GIN Design works — registry, architecture, component authoring, docs tooling, and AI integration.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. An agent skill is available at [/.well-known/agent-skills/site-skill.md](/.well-known/agent-skills/site-skill.md).



GIN Design is a component registry built on top of the [shadcn registry system](https://ui.shadcn.com/docs/registry). It is not an npm package — components are distributed as source files that are copied directly into the consuming project via the shadcn CLI. This page documents how the repository is structured, how components are authored and registered, and how the documentation site works.

## Architecture [#architecture]

The repository is a Next.js 16 App Router application that serves two purposes simultaneously: it hosts the documentation site and exposes the component registry as a public JSON API.

| Layer                | Technology                       |
| -------------------- | -------------------------------- |
| Framework            | Next.js 16, App Router, React 19 |
| Docs engine          | Fumadocs (MDX-based)             |
| Styling              | Tailwind CSS 4, CSS variables    |
| Component primitives | Radix UI (`@radix-ui/react-*`)   |
| State management     | Jotai (`atomWithStorage`)        |
| Animations           | Motion (Framer Motion)           |
| Syntax highlighting  | Shiki + rehype-pretty-code       |
| Notifications        | Sonner                           |
| Audio / haptics      | `@web-kits/audio`, `web-haptics` |
| Analytics            | Vercel Analytics                 |
| Deployment           | Vercel                           |

## Registry System [#registry-system]

The registry is defined in `registry.json` at the root of the repository. Each entry declares a component, its source file, its type, and its dependencies.

```json
{
  "name": "gin-design",
  "homepage": "https://gin-design.vercel.app",
  "items": [
    {
      "name": "button",
      "type": "registry:ui",
      "title": "Button",
      "description": "...",
      "dependencies": ["class-variance-authority", "@radix-ui/react-slot"],
      "files": [
        {
          "path": "registry/mint/button.tsx",
          "type": "registry:ui",
          "target": "components/ui/button.tsx"
        }
      ]
    }
  ]
}
```

When a user runs `npx shadcn add https://gin-design.vercel.app/r/button`, the CLI fetches the registry entry, resolves the file, and copies it to the `target` path inside their project. They own the file completely from that point.

The registry endpoint is served at `/r/[name].json` via `app/r/[name]/route.ts`, which reads `registry.json` and transforms the entries into the format the shadcn CLI expects.

## Registry Item Types [#registry-item-types]

Each item in the registry has a prefix that identifies its type. Components have no prefix; all other types use a short letter prefix followed by a dash.

| Prefix   | Type      | Example        | Status      |
| -------- | --------- | -------------- | ----------- |
| *(none)* | Component | `button`       | Available   |
| `e-`     | Element   | `e-data-table` | Available   |
| `b-`     | Block     | `b-featured`   | Available   |
| `c-`     | Chart     | `c-bar`        | Available   |
| `i-`     | Icons     | `i-social`     | Available   |
| `p-`     | Page      | `p-dashboard`  | Coming soon |

## Component Authoring [#component-authoring]

All registry components live in `registry/mint/` — the base/default brand theme, source of truth for every other theme. The directory name doesn't match the shadcn `style` setting (see below); it's just our default brand id.

A registry component is a plain TypeScript/React file with no `"use client"` directive unless it genuinely requires browser APIs or hooks. Components that only compose JSX render as Server Components and avoid hydration issues.

**Standard component structure:**

```tsx
import { cva } from "class-variance-authority";
import type { VariantProps } from "class-variance-authority";
import { Slot } from "@radix-ui/react-slot";
import { cn } from "@/lib/utils";

const componentVariants = cva("base-classes", {
  variants: { variant: { default: "..." } },
  defaultVariants: { variant: "default" },
});

interface ComponentProps
  extends React.ComponentProps<"element">,
    VariantProps<typeof componentVariants> {
  asChild?: boolean;
}

const Component = ({ className, variant, asChild = false, ...props }: ComponentProps) => {
  const Comp = asChild ? Slot : "element";
  return <Comp className={cn(componentVariants({ className, variant }))} {...props} />;
};

export { Component, componentVariants };
```

**Conventions:**

* `cn()` from `@/lib/utils` — merges Tailwind classes via `clsx` + `tailwind-merge`
* `cva()` from `class-variance-authority` — variant logic
* `Slot` from `@radix-ui/react-slot` — enables the `asChild` pattern (renders as any element)
* `data-slot`, `data-size`, `data-variant` — data attributes for external targeting via CSS

## shadcn Configuration [#shadcn-configuration]

The repository uses the `new-york` style with the following defaults:

```json
{
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "css": "app/globals.css",
    "baseColor": "neutral",
    "cssVariables": true
  },
  "aliases": {
    "components": "@/components",
    "ui": "@/components/ui",
    "utils": "@/lib/utils",
    "lib": "@/lib",
    "hooks": "@/hooks"
  },
  "iconLibrary": "lucide"
}
```

CSS design tokens are defined as CSS variables in `app/globals.css`, following the shadcn token convention. All tokens have light and dark mode values:

```css
:root {
  --background: ...;
  --foreground: ...;
  --primary: ...;
  --primary-foreground: ...;
  --secondary: ...;
  --secondary-foreground: ...;
  --accent: ...;
  --accent-foreground: ...;
  --destructive: ...;
  --border: ...;
  --input: ...;
  --ring: ...;
  --radius: ...;
}
```

## Documentation Site [#documentation-site]

Documentation pages are MDX files in `content/docs/`. The Fumadocs engine (`fumadocs-mdx`) processes them and exposes them via the `source` loader in `lib/source.ts`.

The sidebar structure is controlled by `meta.json` files alongside the MDX files:

```json
{ "title": "Get Started", "pages": ["index", "installation", "documentation"] }
```

Each MDX file has a YAML frontmatter block that declares `title`, `description`, and optionally `figma` (a Figma link for the component's design reference).

**MDX components** are registered in `mdx-components.tsx` and are available globally in every page without explicit imports — `ComponentPreview`, `ComponentSource`, `CommandBox`, `Accordion`, etc.

### ComponentPreview [#componentpreview]

```mdx
<ComponentPreview name="button">
  <Button>Button</Button>
</ComponentPreview>
```

The `name` prop resolves to `registry/mint/{name}.tsx`. The component renders the live preview alongside a source code tab that shows the registry file contents.

### CommandBox [#commandbox]

```mdx
<CommandBox componentName="button" className="mt-2" />
```

Renders the install command card with npm / pnpm / yarn / vite tabs. On the home and installation pages, `componentName` is omitted and the card animates through all registered component names. On component pages, `componentName` is passed explicitly and the name is shown statically.

## Markdown Endpoints [#markdown-endpoints]

Every documentation page is available as machine-readable markdown at its `.md` URL:

```
/docs/components/button    → HTML page
/docs/components/button.md → Raw markdown
```

The markdown route is served by `app/llms.md/docs/[[...slug]]/route.ts`. It uses `getLLMText()` from `lib/source.ts`, which extracts the processed markdown content from the MDX page data. This endpoint is used internally by the Copy Page and Open in AI features.

Two additional endpoints make the full library indexable by AI tools:

* `/llms.txt` — lists all documented pages (follows the [llms.txt](https://llmstxt.org) convention)
* `/llms-full.txt` — includes the full markdown content of every page in one file

## AI Tooling [#ai-tooling]

Each component, element, block, chart, and icon page has a **Copy Page** button in the header toolbar.

### Copy Page [#copy-page]

Fetches the `.md` endpoint for the current page and copies the raw markdown to the clipboard. Useful for pasting into AI tools that don't support URL prompts — local models, IDE chat panels, or any tool where you paste context manually.

### Open in AI tool [#open-in-ai-tool]

Opens the page directly in Claude or ChatGPT with a pre-built prompt constructed in `components/docs-copy-page.tsx` via `getPromptUrl()`:

```
I'm building with GIN Design — a custom component library (https://gin-design.vercel.app).

Active theme: **{themeLabel}**

Read the full component documentation here: {markdownUrl}

Focus on the **{themeLabel}** Specs section — it contains the exact design tokens,
border radius, colour palette, variant meanings, and conventions for this theme.

Based on that, help me use this component correctly in the {themeLabel} theme.
```

The model is directed to the `.md` URL of the page, which includes the full content — usage examples, variants, props, and the Specs block.

### Open in v0 [#open-in-v0]

Opens the component in v0's sandbox using the registry JSON endpoint (`/api/registry/{theme}/{component}`), with the theme's CSS variables pre-loaded so the preview renders in the correct brand colours.

### Dropdown actions [#dropdown-actions]

The Copy Page dropdown also provides:

* **View as Markdown** — opens the `.md` URL in a new tab
* **Open in JSON** — opens the raw registry entry (`/r/{name}.json`)
* **View in Figma** — appears only when the page declares a `figma` URL in its frontmatter

## Specs Block [#specs-block]

Every component page ends with a `## Specs` section — a fenced markdown block designed to be copied and pasted into any AI tool as structured context.

It contains:

* Component name, file path, install target, dependencies
* shadcn config (style, rsc, tsx, cssVariables, iconLibrary, aliases)
* All CSS design tokens used (with light/dark values)
* Tailwind CSS conventions (border-radius, spacing, font scale)
* Variant and size reference with their Tailwind classes
* Prop definitions with types and defaults
* Code conventions (`cn`, `cva`, `Slot`, focus ring, disabled state)

## Figma Integration [#figma-integration]

Component pages can declare a Figma reference URL in their frontmatter:

```mdx
---
title: Button
figma: https://www.figma.com/design/{fileId}/{name}?node-id={nodeId}
---
```

When a `figma` URL is present, a **View in Figma** item appears in the Copy Page dropdown, linking directly to the component's Figma frame or section.

The `figma` field is typed in `source.config.ts` as an optional URL via Zod:

```ts
schema: frontmatterSchema.extend({
  figma: z.string().url().optional(),
})
```

### Figma Diff [#figma-diff]

Beyond linking, the library exposes a Figma Diff API at `app/api/diff/figma/`. Connect a Figma file via access token and get a structured diff of what changed in your designs — useful for keeping code and design in step when the design evolves.

Configure via environment variable:

```bash
FIGMA_ACCESS_TOKEN=your_token_here
```

## Sharing [#sharing]

The share button (top right of each component, element, block, chart, or icon page) provides:

* **Copy link** — copies the absolute URL to clipboard
* **Copy JSON** — fetches and copies the raw registry JSON for the component
* **Share on Discord** — posts the component link directly to the configured Discord channel via webhook (requires `DISCORD_WEBHOOK_URL` env var)

Discord and X sharing are configured in `constants/links.ts`. Items only appear in the UI when their corresponding value is non-empty.

## Package Manager Support [#package-manager-support]

The docs support four package managers: **npm**, **pnpm**, **yarn**, and **vite**. The selected manager is persisted in `localStorage` via a Jotai atom (`atomWithStorage`) in `hooks/use-package-manager.ts`.

All code block commands render tabs for each manager. The `CommandBox` component reads the active manager to build the correct install command.

## File Structure [#file-structure]

```
├── app/
│   ├── (app)/docs/[[...slug]]/   # Docs page renderer
│   ├── r/[name]/                 # Registry JSON endpoint (/r/button.json)
│   ├── api/
│   │   ├── registry/             # Registry API for v0 and AI tools
│   │   ├── diff/figma/           # Figma diff endpoint
│   │   └── share/discord/        # Discord webhook endpoint
│   └── llms.md/docs/[[...slug]]/ # Markdown endpoint (.md URLs)
├── components/
│   ├── command-box.tsx           # Install command card
│   ├── docs-copy-page.tsx        # Copy Page / Open in AI button
│   ├── docs-share-menu.tsx       # Share button
│   ├── docs-toc-footer.tsx       # TOC sidebar footer links
│   └── ui/                       # App UI components (not registry)
├── content/docs/                 # MDX documentation pages
├── registry/mint/                 # Registry component source files
├── constants/
│   ├── links.ts                  # External URLs (GitHub, Discord, X)
│   └── site.ts                   # Site metadata, registry URL, tokens
├── lib/
│   ├── source.ts                 # Fumadocs source loader
│   ├── figma-diff.ts             # Figma diff logic
│   └── events.ts                 # Analytics event schema
├── registry.json                 # Registry manifest
└── source.config.ts              # Fumadocs MDX config + frontmatter schema
```
