panda-css-styling

Guide for styling components with Panda CSS in the SuperTool project. Use this when styling tool pages, creating layouts, or implementing the glassmorphic design system.

$ Installer

git clone https://github.com/ferryhinardi/supertool /tmp/supertool && cp -r /tmp/supertool/.github/skills/panda-css-styling ~/.claude/skills/supertool

// tip: Run this command in your terminal to install the skill


name: panda-css-styling description: Guide for styling components with Panda CSS in the SuperTool project. Use this when styling tool pages, creating layouts, or implementing the glassmorphic design system. license: MIT

Panda CSS Styling Guide

This skill teaches you how to style components using Panda CSS following the SuperTool design system.

CRITICAL: Tool Pages Use Panda CSS, NOT Tailwind

IMPORTANT: Tool pages (app/tools/**/page.tsx) MUST use Panda CSS css() function.

  • Tailwind CSS v4 is used for app layouts and general pages
  • Tool pages require Panda CSS for type-safe, recipe-based styling

Import Pattern

import { css } from '@/styled-system/css'
// For UI components
import { Button } from '@/components/ui/button'

Design System

Color Palette

The project uses a dark theme with purple/pink/blue gradients:

// CSS variables from globals.css
--color-bg-primary: rgb(10, 10, 15)
--color-bg-secondary: rgb(20, 20, 30)
--color-accent-purple: rgb(168, 85, 247)
--color-accent-pink: rgb(236, 72, 153)
--color-accent-blue: rgb(59, 130, 246)

Glassmorphism Effect

Use this pattern consistently:

className={css({
  bg: 'rgba(255, 255, 255, 0.05)',
  backdropFilter: 'blur(10px)',
  borderRadius: 'xl',
  border: '1px solid',
  borderColor: 'rgba(255, 255, 255, 0.1)',
  boxShadow: '0 8px 32px 0 rgba(0, 0, 0, 0.37)'
})}

Or use the .glass utility class from Tailwind for quick application.

Standard Page Layout

Every tool page should follow this structure:

'use client'

import { css } from '@/styled-system/css'

export default function ToolPage() {
  return (
    <main className={css({
      mx: 'auto',
      maxW: '7xl',
      w: 'full',
      px: { base: '4', sm: '6', md: '8' },
      py: { base: '6', sm: '8', md: '10' },
      spaceY: { base: '6', sm: '8', md: '10' }
    })}>
      {/* Tool Header */}
      <div className={css({ textAlign: 'center', spaceY: '4' })}>
        <h1 className={css({
          fontSize: { base: '3xl', sm: '4xl', md: '5xl' },
          fontWeight: 'bold',
          bgGradient: 'to-r',
          gradientFrom: 'purple.400',
          gradientTo: 'pink.600',
          bgClip: 'text'
        })}>
          Tool Title
        </h1>
        <p className={css({
          fontSize: { base: 'md', sm: 'lg' },
          color: 'gray.400'
        })}>
          Tool description
        </p>
      </div>

      {/* Tool Content */}
      <div className={css({
        bg: 'rgba(255, 255, 255, 0.05)',
        backdropFilter: 'blur(10px)',
        borderRadius: 'xl',
        border: '1px solid',
        borderColor: 'rgba(255, 255, 255, 0.1)',
        p: { base: '6', sm: '8' },
        spaceY: '6'
      })}>
        {/* Your content here */}
      </div>
    </main>
  )
}

Responsive Design Pattern

ALWAYS use responsive values with base, sm, md, lg, xl breakpoints:

// ✅ CORRECT
className={css({
  fontSize: { base: 'sm', sm: 'md', md: 'lg' },
  padding: { base: '4', sm: '6', md: '8' },
  gridTemplateColumns: { base: '1fr', sm: 'repeat(2, 1fr)', lg: 'repeat(3, 1fr)' }
})}

// ❌ WRONG - No responsive values
className={css({
  fontSize: 'lg',
  padding: '8'
})}

Grid Layouts

CRITICAL: Use valid grid template column values:

// ✅ CORRECT
className={css({
  display: 'grid',
  gridTemplateColumns: { base: '1fr', sm: 'repeat(2, 1fr)', lg: 'repeat(3, 1fr)' },
  gap: { base: '4', sm: '6' },
  w: 'full' // IMPORTANT: Always add w: 'full'
})}

// ❌ WRONG - Invalid grid values
className={css({
  display: 'grid',
  gridTemplateColumns: { base: 1, sm: 2, lg: 3 } // Numbers not valid!
})}

Common Patterns

1. Card Component

<div className={css({
  bg: 'rgba(255, 255, 255, 0.05)',
  backdropFilter: 'blur(10px)',
  borderRadius: 'xl',
  border: '1px solid',
  borderColor: 'rgba(255, 255, 255, 0.1)',
  p: { base: '4', sm: '6' },
  transition: 'all 0.3s',
  cursor: 'pointer',
  _hover: {
    bg: 'rgba(255, 255, 255, 0.08)',
    borderColor: 'rgba(168, 85, 247, 0.5)',
    transform: 'translateY(-2px)',
  }
})}>
  Card content
</div>

2. Button (Using UI Component)

import { Button } from '@/components/ui/button'

<Button 
  onClick={handleClick}
  className={css({
    w: 'full',
    bg: 'linear-gradient(to right, rgb(168, 85, 247), rgb(236, 72, 153))',
    _hover: {
      opacity: 0.9
    }
  })}
>
  Click Me
</Button>

3. Input Field

<input
  type="text"
  className={css({
    w: 'full',
    px: '4',
    py: '3',
    bg: 'rgba(255, 255, 255, 0.05)',
    border: '1px solid',
    borderColor: 'rgba(255, 255, 255, 0.1)',
    borderRadius: 'lg',
    color: 'white',
    fontSize: { base: 'sm', sm: 'md' },
    outline: 'none',
    transition: 'all 0.2s',
    _focus: {
      borderColor: 'purple.400',
      bg: 'rgba(255, 255, 255, 0.08)'
    },
    _placeholder: {
      color: 'gray.500'
    }
  })}
  placeholder="Enter text..."
/>

4. Flex Layouts

// Row layout
<div className={css({
  display: 'flex',
  flexDirection: { base: 'column', sm: 'row' }, // Stack on mobile
  alignItems: 'center',
  justifyContent: 'space-between',
  gap: { base: '4', sm: '6' }
})}>
  <div>Item 1</div>
  <div>Item 2</div>
</div>

// Column layout
<div className={css({
  display: 'flex',
  flexDirection: 'column',
  gap: '4',
  alignItems: 'stretch'
})}>
  <div>Item 1</div>
  <div>Item 2</div>
</div>

5. Section Divider

<div className={css({
  h: '1px',
  w: 'full',
  bg: 'linear-gradient(to right, transparent, rgba(168, 85, 247, 0.5), transparent)',
  my: { base: '6', sm: '8' }
})} />

6. Gradient Text

<h2 className={css({
  fontSize: { base: '2xl', sm: '3xl', md: '4xl' },
  fontWeight: 'bold',
  bgGradient: 'to-r',
  gradientFrom: 'purple.400',
  gradientVia: 'pink.500',
  gradientTo: 'blue.500',
  bgClip: 'text',
  lineHeight: '1.2'
})}>
  Gradient Heading
</h2>

7. Loading Spinner

<div className={css({
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  gap: '3',
  py: '8'
})}>
  <div className={css({
    w: '8',
    h: '8',
    border: '3px solid',
    borderColor: 'rgba(168, 85, 247, 0.2)',
    borderTopColor: 'purple.400',
    borderRadius: 'full',
    animation: 'spin 1s linear infinite'
  })} />
  <span className={css({ color: 'gray.400' })}>Loading...</span>
</div>

8. Result Display

<div className={css({
  mt: '6',
  p: { base: '4', sm: '6' },
  bg: 'rgba(34, 197, 94, 0.1)',
  border: '1px solid',
  borderColor: 'rgba(34, 197, 94, 0.3)',
  borderRadius: 'lg'
})}>
  <h3 className={css({
    fontSize: { base: 'lg', sm: 'xl' },
    fontWeight: 'semibold',
    color: 'green.400',
    mb: '3'
  })}>
    Result
  </h3>
  <p className={css({ color: 'gray.300' })}>
    Result content here
  </p>
</div>

9. Error State

<div className={css({
  p: { base: '4', sm: '6' },
  bg: 'rgba(239, 68, 68, 0.1)',
  border: '1px solid',
  borderColor: 'rgba(239, 68, 68, 0.3)',
  borderRadius: 'lg'
})}>
  <p className={css({ color: 'red.400' })}>
    Error message here
  </p>
</div>

10. Badge/Tag

<span className={css({
  display: 'inline-flex',
  alignItems: 'center',
  px: '3',
  py: '1',
  bg: 'rgba(168, 85, 247, 0.2)',
  border: '1px solid',
  borderColor: 'rgba(168, 85, 247, 0.4)',
  borderRadius: 'full',
  fontSize: 'xs',
  fontWeight: 'medium',
  color: 'purple.300'
})}>
  New
</span>

Mobile-First Requirements

Touch Targets

MINIMUM 44px for all interactive elements:

// ✅ CORRECT
<button className={css({
  minH: '44px',
  minW: '44px',
  px: '6',
  py: '3'
})}>
  Button
</button>

// ❌ WRONG - Too small
<button className={css({ p: '1' })}>Small</button>

Typography Scale

// Heading sizes
h1: { base: '3xl', sm: '4xl', md: '5xl' }
h2: { base: '2xl', sm: '3xl', md: '4xl' }
h3: { base: 'xl', sm: '2xl', md: '3xl' }

// Body text
body: { base: 'sm', sm: 'md' }
caption: { base: 'xs', sm: 'sm' }

Spacing Scale

// Section spacing
spaceY: { base: '6', sm: '8', md: '10' }

// Component spacing
gap: { base: '4', sm: '6', md: '8' }

// Padding
p: { base: '4', sm: '6', md: '8' }

State Modifiers

Panda CSS supports pseudo-classes with _ prefix:

className={css({
  bg: 'purple.500',
  _hover: { bg: 'purple.600' },
  _focus: { outline: '2px solid', outlineColor: 'purple.400' },
  _active: { transform: 'scale(0.98)' },
  _disabled: { opacity: 0.5, cursor: 'not-allowed' },
  _placeholder: { color: 'gray.500' }
})}

Animations

Use Tailwind animation utilities:

className={css({
  animation: 'spin 1s linear infinite' // Spinner
})}

className={css({
  animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite' // Pulse
})}

className={css({
  animation: 'bounce 1s infinite' // Bounce
})}

Common Mistakes to Avoid

❌ WRONG: Using Tailwind classes on tool pages

// Don't do this on tool pages
<div className="bg-gray-800 p-4 rounded-lg">

✅ CORRECT: Using Panda CSS

<div className={css({
  bg: 'gray.800',
  p: '4',
  borderRadius: 'lg'
})}>

❌ WRONG: No responsive values

<div className={css({ fontSize: 'lg' })}>

✅ CORRECT: Responsive values

<div className={css({ fontSize: { base: 'md', sm: 'lg' } })}>

❌ WRONG: Invalid grid template

gridTemplateColumns: { base: 1, sm: 2 }

✅ CORRECT: Valid grid template

gridTemplateColumns: { base: '1fr', sm: 'repeat(2, 1fr)' }

❌ WRONG: Missing width on grid

<div className={css({
  display: 'grid',
  gridTemplateColumns: 'repeat(3, 1fr)'
})}>

✅ CORRECT: Width specified

<div className={css({
  display: 'grid',
  gridTemplateColumns: 'repeat(3, 1fr)',
  w: 'full'
})}>

Reference Example

Canonical implementation: app/tools/unit-converter/page.tsx

This file demonstrates:

  • Correct Panda CSS usage
  • Responsive design patterns
  • Glassmorphism styling
  • Mobile-first approach
  • Proper grid layouts

UI Component Library

Use pre-built components from components/ui/:

import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { Select } from '@/components/ui/select'
import { Checkbox } from '@/components/ui/checkbox'

These components already follow the design system and are accessible.

Checklist

  • Using Panda CSS css() function (not Tailwind utilities)
  • Glassmorphism effect applied to cards
  • Responsive values for all sizing properties
  • Mobile-first breakpoints (base → sm → md → lg)
  • Touch targets >= 44px
  • Grid layouts use valid template values + w: 'full'
  • Gradient text for headings
  • Proper spacing (spaceY, gap, padding)
  • State modifiers for interactivity (_hover, _focus)
  • Dark theme colors used throughout
  • Tested on mobile viewport