Skip to content
UI Kit
Accent color

Build

How components work

Every component follows the same pattern. One file, a variant prop, your content through props and a few built-in texts in two languages.

On this page

One file per component#

A component is a single .tsx file in src/components/ui. It exports the component, its props type and the type of its variants. Its own texts sit in the file as TEXTS, in both languages.

src/components/ui/faq.tsx
import { cn, useTexts } from '@/lib/kit';
import { Button } from '@/components/ui/button';

export type FaqVariant = 'accordion' | 'two-column' | 'grid' | …;

const TEXTS = {
  en: { search: 'Search questions', … },
  de: { search: 'Fragen durchsuchen', … },
};

export interface FaqProps {
  variant?: FaqVariant;
  title?: React.ReactNode;
  items: FaqItem[];
  labels?: Partial<(typeof TEXTS)['en']>;
  className?: string;
}

export function Faq(props: FaqProps) { … }

Imports always go through the alias, @/components/ui/<name> for components and @/lib/kit for helpers such as cn, KitProvider and useInView.

Variants#

One component, many layouts. The variant prop picks one, the hero knows 17, the FAQ 7. Your content stays the same, you only swap the word.

<Hero variant="centered" title="…" />
<Hero variant="split" title="…" media={<img src="/shop.avif" alt="…" />} />
<Hero variant="with-stats" title="…" stats={[{ value: '1962', label: 'founded' }]} />

Every variant has a live preview on its page, for example under Hero.

Content through props#

Everything on the page comes through props. A component carries no brand and no fixed copy, it fits a law firm as well as an online shop.

Faq, variant accordionView in Components
Faq with your content
import { Faq } from '@/components/ui/faq';

<Faq
  variant="accordion"
  title="Good to know"
  defaultOpen={0}
  schema
  items={[
    { question: 'Can I pay in the shop?', answer: 'Yes, by card or in cash when you pick up.' },
    { question: 'How early should I order?', answer: 'By 6 pm the day before, two days ahead for cakes.' },
  ]}
/>

schema writes the structured data for search engines as well. The main props of the hero at a glance.

PropTypeDefaultWhat it does
variant'centered' | 'split' | 'left' | 'with-form' | …'centered'One of 17 layouts.
titleReactNode·The headline. Required.
descriptionReactNode·The text below the headline.
primaryAction{ label, href }·The main button.
secondaryAction{ label, href }·The second button, quieter.
mediaReactNode·Image, screenshot or drawing next to or below the text.
labelsPartial<Texts>·Your own wording for built-in texts.
classNamestring·Extra classes on the outer element.

Language and labels#

A few texts a component writes itself, such as Close, Next or form hints. KitProvider sets their language for everything below, English is the default. For a single place, labels overrides exactly the words you name.

<Faq
  variant="with-search"
  items={items}
  labels={{ search: 'Search the help', placeholder: 'For example shipping' }}
/>

Slots for images and media#

Images, logos, avatars and media are props of type ReactNode. Pass an <img>, a video, an SVG or a whole component, and the component places it in the right spot and frame.

<Hero
  variant="split"
  title="Fresh bread, every morning"
  media={<img src="/bakery.avif" alt="Counter of Lindner Bakery" width={1200} height={900} />}
  logos={[<Logo1 key="1" />, <Logo2 key="2" />]}
/>

For device pictures there are BrowserFrame and PhoneFrame, drawn motifs live under Visuals.

Interactive in Astro#

Components render fully on the server, their content is there as HTML. When a component has state, such as an accordion, a toggle or a form, it needs a client directive in Astro. client:visible loads it once it comes into view, client:load right away.

src/pages/index.astro
---
import { Faq } from '@/components/ui/faq';
import { Home } from '../components/Home';
---

<!-- No state, HTML only -->
<Faq variant="grid" items={items} />

<!-- With state, as an island -->
<Home client:visible />

Shared helpers#

Components of one family share a file of helpers, so forms check alike, charts read alike and scenes scroll alike. aiw add brings them along as a dependency, and you can import them yourself for your own parts.

HelperImportWhat it does
form-flow@/components/ui/form-flowFields as data, checks, error summary, focus on the first error, chips and the confirmation of every form section.
chart@/components/ui/chartScales, number formats, colors, keyboard and the data table behind every chart.
chart-figure@/components/ui/chart-figureThe frame of the newer charts with title, big number, change and source.
scroll-scene@/components/ui/scroll-sceneScroll progress as a CSS variable for the scroll stories and effects.
motion-scene@/components/ui/motion-scenePlayback of the animated visuals, paused out of view, still with reduced motion.
section@/components/ui/sectionSection frame, heading block and Eyebrow shared by most sections.

Guides with examples are under forms, charts and scroll stories.

Make it yours#

className goes to the outer element and wins on conflicts, so you adjust spacing or background without opening the file.

<Pricing variant="cards" plans={plans} className="py-32" />

For anything more, open the file. It is yours, change it like your own code.

Often searched

Jump to

Copied to the clipboard