Skip to content
WANCRAFT

Component Architecture

How a Wancraft component is structured and why.

One file, one component

Every component follows the same shape:

components/ui/example.tsx
"use client";

import * as React from "react";
import { motion, useReducedMotion } from "motion/react"; // only if needed

export interface ExampleProps extends React.ComponentPropsWithoutRef<"div"> {
  /** Every configurable behaviour is a typed prop with a sensible default. */
  strength?: number;
  accent?: string;
}

export function Example({ strength = 40, accent = "#FF7448", className = "", ...props }: ExampleProps) {
  const reduceMotion = useReducedMotion();
  // ...
}

Rules every file follows

  • Starts with "use client" and imports only react and, where needed, motion/react.
  • No imports from your project (no utilities, no aliases), so a copied file works anywhere.
  • Accepts className and forwards native attributes where it renders a single root element.
  • Prop defaults are the values you see in the inspector before changing anything.
  • Colours that define the brand of a component (accents) are props, applied through CSS custom properties, so Tailwind never needs to know your palette.

How the site uses the same file

Behind each component in the Wancraft repository are three files:

  • registry/ui/<slug>.tsx — the shipped source.
  • registry/meta/<slug>.ts — typed metadata: description, categories, the control schema, presets, props table and accessibility notes.
  • registry/demos/<slug>.tsx — a thin wrapper that renders the shipped component with the current control values.

A build step validates the metadata with Zod, checks that the dependencies declared in metadata exactly match the imports in the source, and then writes the registry JSON from the same file. If any of these drift, the build fails.