Styling

Catmint supports multiple styling approaches out of the box, including plain CSS, CSS Modules, Tailwind CSS, SCSS, and CSS-in-JS libraries. Styles are processed through Vite and benefit from hot module replacement during development.

Global CSS

Import global CSS files in your root layout to apply styles across the entire application. The import is processed by Vite and included in the final bundle.

/* app/globals.css */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: system-ui, -apple-system, sans-serif;
  line-height: 1.6;
  color: #1a1a1a;
}

a {
  color: inherit;
  text-decoration: none;
}
// app/layout.tsx
import React from 'react'
import './globals.css'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
      </head>
      <body>{children}</body>
    </html>
  )
}

You can also import CSS files in page or component files. Vite will deduplicate and bundle them correctly.

CSS Modules

CSS Modules scope class names to the component that imports them, preventing name collisions. Use the .module.css extension to enable CSS Module behavior.

/* app/components/Card.module.css */
.card {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 1.5rem;
  background: #ffffff;
}

.title {
  font-size: 1.25rem;
  font-weight: 600;
  margin-bottom: 0.5rem;
}

.body {
  color: #6b7280;
  line-height: 1.6;
}
// app/components/Card.tsx
import styles from './Card.module.css'

export function Card({ title, children }: {
  title: string
  children: React.ReactNode
}) {
  return (
    <div className={styles.card}>
      <h2 className={styles.title}>{title}</h2>
      <div className={styles.body}>{children}</div>
    </div>
  )
}

At build time, class names are transformed to unique identifiers (e.g., Card_card_1a2b3), ensuring no conflicts between components.

Tailwind CSS

Tailwind CSS is a utility-first CSS framework that works well with Catmint. Install it and create the configuration file:

Installation

pnpm add -D tailwindcss @tailwindcss/vite

Vite Plugin

Add the Tailwind CSS Vite plugin to your vite.config.ts:

// vite.config.ts
import { defineConfig } from 'vite'
import catmint from '@catmint/vite'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [catmint(), tailwindcss()],
})

CSS Entry Point

Import Tailwind in your global CSS file:

/* app/globals.css */
@import "tailwindcss";

Usage

Apply Tailwind utility classes directly in your components:

// app/page.tsx
export default function HomePage() {
  return (
    <div className="max-w-2xl mx-auto px-4 py-12">
      <h1 className="text-4xl font-bold text-gray-900 mb-4">
        Welcome to My App
      </h1>
      <p className="text-lg text-gray-600 mb-8">
        Built with Catmint and Tailwind CSS.
      </p>
      <a
        href="/docs"
        className="inline-block bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
      >
        Read the Docs
      </a>
    </div>
  )
}

SCSS

Catmint supports SCSS through Vite's built-in preprocessor support. Install sass as a dev dependency:

pnpm add -D sass

Then use .scss files anywhere you would use .css files. SCSS Modules are also supported with the .module.scss extension.

/* app/components/Button.module.scss */
$primary: #3b82f6;
$primary-hover: #2563eb;

.button {
  display: inline-flex;
  align-items: center;
  padding: 0.5rem 1rem;
  border-radius: 0.375rem;
  font-weight: 500;
  transition: background-color 150ms;

  &.primary {
    background-color: $primary;
    color: white;

    &:hover {
      background-color: $primary-hover;
    }
  }

  &.secondary {
    background-color: transparent;
    border: 1px solid $primary;
    color: $primary;
  }
}
// app/components/Button.tsx
import styles from './Button.module.scss'

export function Button({
  variant = 'primary',
  children,
}: {
  variant?: 'primary' | 'secondary'
  children: React.ReactNode
}) {
  return (
    <button className={`${styles.button} ${styles[variant]}`}>
      {children}
    </button>
  )
}

CSS-in-JS

CSS-in-JS libraries that support server-side rendering work with Catmint. Libraries that extract styles at build time (such as vanilla-extract or Panda CSS) are preferred because they avoid runtime overhead.

For runtime CSS-in-JS libraries (such as styled-components or Emotion), you need to configure SSR style extraction to avoid a flash of unstyled content. Consult the library's documentation for SSR setup instructions.

// Example: vanilla-extract (build-time CSS-in-JS)
// app/components/Card.css.ts
import { style } from '@vanilla-extract/css'

export const card = style({
  border: '1px solid #e5e7eb',
  borderRadius: '8px',
  padding: '1.5rem',
})

export const title = style({
  fontSize: '1.25rem',
  fontWeight: 600,
})
// app/components/Card.tsx
import * as styles from './Card.css'

export function Card({ title, children }: {
  title: string
  children: React.ReactNode
}) {
  return (
    <div className={styles.card}>
      <h2 className={styles.title}>{title}</h2>
      {children}
    </div>
  )
}

Static Assets

Static files such as images, fonts, and icons should be placed in the app/public/ directory. Files in this directory are served as-is at the root URL path without processing.

app/
  public/
    favicon.ico          -> /favicon.ico
    robots.txt           -> /robots.txt
    images/
      logo.svg           -> /images/logo.svg
    fonts/
      inter.woff2        -> /fonts/inter.woff2

Reference static assets in your components and CSS using absolute paths:

// In a component
<img src="/images/logo.svg" alt="Logo" />

// In CSS
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}

Importing Assets in Components

For assets that should be processed by Vite (hashed filenames, optimization), import them directly in your component files. Vite will handle bundling and cache-busting:

// app/components/Header.tsx
import logoUrl from '../assets/logo.svg'

export function Header() {
  return (
    <header>
      <img src={logoUrl} alt="Logo" />
    </header>
  )
}
// logoUrl resolves to something like /assets/logo-a1b2c3.svg

Summary

ApproachFile ExtensionScopingSetup
Global CSS.cssGlobalNone
CSS Modules.module.cssComponentNone
Tailwind CSS.cssUtility classespnpm add -D tailwindcss @tailwindcss/vite
SCSS.scssGlobal or Modulepnpm add -D sass
SCSS Modules.module.scssComponentpnpm add -D sass
CSS-in-JS.css.tsComponentLibrary-specific