Skip to main content
COSMOS RSC comes with Tailwind CSS v4 pre-configured, allowing you to style your application using utility classes.

Tailwind CSS setup

Tailwind is already integrated into the build system. No additional configuration is needed.

Import Tailwind

Import Tailwind in your app/globals.css:
app/globals.css
@import 'tailwindcss';
This single line imports all of Tailwind’s utilities, base styles, and components. Reference the compiled CSS in your root layout:
app/root-layout.js
export default function RootLayout({ children }) {
  return (
    <html lang='en'>
      <head>
        <meta charSet='utf-8' />
        <meta name='viewport' content='width=device-width, initial-scale=1' />
        <title>My App</title>
        <link rel='stylesheet' href='/style.css' />
      </head>
      <body>{children}</body>
    </html>
  );
}
The build system compiles globals.css to style.css in the .cosmos-rsc/ directory.

Using Tailwind classes

Apply utility classes directly to your components:
export default function HomePage() {
  return (
    <div className='mx-auto max-w-4xl px-4 py-12'>
      <h1 className='mb-8 text-4xl font-bold'>Welcome</h1>
      <p className='text-gray-700 leading-relaxed'>
        Build modern web applications with React Server Components.
      </p>
      <button className='mt-4 rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700'>
        Get Started
      </button>
    </div>
  );
}

Common patterns from the demo

Here are styling patterns used in the COSMOS RSC demo:

Layout containers

<div className='mx-auto max-w-4xl px-4 py-12'>
  {/* Content centered with max width */}
</div>

Card components

<div className='rounded bg-white p-4 shadow'>
  <h3 className='mb-2 text-lg font-medium'>Card Title</h3>
  <p className='text-gray-600'>Card content</p>
</div>

Grid layouts

<div className='grid gap-6 md:grid-cols-2'>
  <div>Column 1</div>
  <div>Column 2</div>
</div>

Form inputs

<input
  type='text'
  className='mt-1 block w-full rounded-md border-gray-300 px-4 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500'
/>

Buttons

<button className='rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:outline-none'>
  Click me
</button>
<a href='/about' className='text-blue-600 hover:underline'>
  Learn more
</a>

Custom CSS and animations

Add custom styles and animations in globals.css:
app/globals.css
@import 'tailwindcss';

/* Custom animations */
@keyframes enter-slide-right {
  0% {
    opacity: 0;
    translate: -200px 0;
  }
  100% {
    opacity: 1;
    translate: 0 0;
  }
}

@keyframes exit-slide-left {
  0% {
    opacity: 1;
    translate: 0 0;
  }
  100% {
    opacity: 0;
    translate: -200px 0;
  }
}

/* View transition animations */
::view-transition-new(.navigation-back) {
  animation: enter-slide-right ease-in 0.25s;
}

::view-transition-old(.navigation-back) {
  animation: exit-slide-left ease-in 0.25s;
}

::view-transition-new(.navigation-forward) {
  animation: enter-slide-left ease-in 0.25s;
}

::view-transition-old(.navigation-forward) {
  animation: exit-slide-right ease-in 0.25s;
}
These custom animations work with the NavigationTransition component for smooth page transitions.

Loading states

Create loading skeletons with Tailwind’s animation utilities:
function LoadingCard() {
  return (
    <div className='animate-pulse rounded bg-gray-50 p-4 shadow'>
      <div className='mb-2 h-4 w-1/4 rounded bg-gray-200'></div>
      <div className='h-4 w-3/4 rounded bg-gray-200'></div>
    </div>
  );
}

Responsive design

Use Tailwind’s responsive prefixes:
<div className='grid gap-4 md:grid-cols-2 lg:grid-cols-3'>
  {/* 1 column on mobile, 2 on tablet, 3 on desktop */}
</div>
Common breakpoints:
  • sm: - 640px and up
  • md: - 768px and up
  • lg: - 1024px and up
  • xl: - 1280px and up

Dark mode

Tailwind supports dark mode with the dark: prefix:
<div className='bg-white dark:bg-gray-900 text-gray-900 dark:text-white'>
  Adapts to system color scheme
</div>

Build process

The Webpack configuration handles CSS compilation:
core/build/webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const tailwindcss = require('@tailwindcss/postcss');

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [tailwindcss],
              },
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'style.css',
    }),
  ],
};
This configuration:
  1. Processes CSS files with PostCSS
  2. Runs Tailwind CSS to generate utilities
  3. Extracts the final CSS to style.css

Best practices

Group related utilities together for readability:
// Good
<button className='rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700'>

// Less readable
<button className='bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md'>
If you’re using the same class combinations frequently, create a component:
export function Card({ children }) {
  return (
    <div className='rounded bg-white p-4 shadow'>
      {children}
    </div>
  );
}
Stick to Tailwind’s spacing scale (4, 8, 12, 16, etc.) for consistency:
<div className='space-y-4'> {/* 16px gap */}
  <div className='p-4'>Item 1</div>
  <div className='p-4'>Item 2</div>
</div>
Use Tailwind’s color scale for visual hierarchy:
<h1 className='text-gray-900'>Primary heading</h1>
<p className='text-gray-700'>Body text</p>
<span className='text-gray-500'>Muted text</span>

Resources

Build docs developers (and LLMs) love