GitHubnpm

CSS and Styling

Juice handles CSS through Vite. Import stylesheets directly in your components. Global CSS, CSS Modules, and preprocessors all work with zero configuration.

Global CSS

Import a .css file in your root layout to apply styles globally. The Vite plugin bundles it into the final output automatically.

// app/routes/layout.tsx
import React from 'react';
import './global.css';

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

CSS Modules

Name your file with .module.css and import it as an object. Class names are scoped automatically.

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

.title {
  font-weight: 600;
  margin-bottom: 0.5rem;
}
// 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}>
      <h3 className={styles.title}>{title}</h3>
      {children}
    </div>
  );
}

Preprocessors

Vite supports Sass, Less, and Stylus natively. Install the preprocessor and import the file. No additional configuration needed.

bun add -d sass
// Then import .scss files directly
import './styles.scss';

How It Works

Development

In dev mode, Vite injects CSS via <link> tags with hot module replacement. Edits to stylesheets are reflected instantly without a full page reload.

Production

On build, the Juice Vite plugin emits CSS into the flight manifest under the css key, keyed by route pattern. Global CSS (imported by layouts) appears under '*'. The runtime injects <link rel="stylesheet"> tags into the HTML and uses bootstrapScriptContent to ensure styles load before the page paints.

ModeCSS DeliveryHMR
DevelopmentVite dev server link injectionYes
ProductionManifest CSS + link tags in HTMLNo