Tailwind CSS
CPR uses Tailwind CSS as its primary styling tool, integrated through the @nuxtjs/tailwindcss Nuxt module with default configuration.
Configuration
CPR runs Tailwind with no custom tailwind.config.* file. The @nuxtjs/tailwindcss module auto-detects content paths from the Nuxt project structure. Any Tailwind customization (such as extending the color palette) would be done by adding a tailwind.config.ts at the project root.
The CSS entry point is app/assets/css/main.css, which loads the three Tailwind directives:
@tailwind base;
@tailwind components;
@tailwind utilities;Utility Patterns Used in CPR
Layout
Flexbox and grid are the primary layout tools:
<!-- Flex row with spacing -->
<div class="flex items-center gap-3">
<!-- Flex column, full height -->
<div class="flex flex-col h-full">
<!-- Centered content -->
<div class="flex items-center justify-center min-h-screen">
<!-- Grid with responsive columns -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">Spacing conventions
CPR uses Tailwind's default spacing scale. Common values:
| Use case | Classes |
|---|---|
| Card padding | p-6 |
| Section gaps | gap-4 or gap-6 |
| Inline element spacing | gap-2 or gap-3 |
| Page margins | px-4 sm:px-6 |
| Vertical section spacing | space-y-4 or space-y-6 |
Responsive Design Patterns
CPR uses two primary breakpoints:
| Breakpoint | Prefix | Purpose |
|---|---|---|
| 640px | sm: | Compact info display, form layouts |
| 1024px | lg: | Desktop navigation, sidebar visibility |
Mobile-first examples
<!-- Navigation: hidden on mobile, visible on desktop -->
<nav class="hidden lg:flex items-center gap-1">
<!-- Sidebar: slides in on mobile, always visible on desktop -->
<aside class="fixed inset-y-0 left-0 z-40 w-64 lg:static lg:translate-x-0">
<!-- Info row: stacked on mobile, inline on tablet+ -->
<div class="flex flex-col sm:flex-row sm:items-center gap-2">
<!-- Grid: 1 column on mobile, 2 on tablet, 3 on desktop -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">Common Class Combinations
Cards
<!-- Standard card -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<!-- Card with hover effect (clickable) -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-4
hover:shadow-md transition-shadow cursor-pointer">
<!-- Compact card -->
<div class="bg-white rounded-lg shadow-sm p-4">Tables
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<table class="w-full">
<thead>
<tr class="border-b border-gray-100">
<th class="text-left px-4 py-3 text-xs font-semibold text-gray-500
uppercase tracking-wide">
Column Header
</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-gray-50 hover:bg-gray-50 transition-colors">
<td class="px-4 py-3 text-sm text-gray-700">Cell content</td>
</tr>
</tbody>
</table>
</div>Buttons
<!-- Primary button -->
<button class="bg-[#117ea7] text-white px-4 py-2 rounded-lg
hover:bg-[#0e6b8f] transition-colors
disabled:opacity-50 disabled:cursor-not-allowed">
Save
</button>
<!-- Secondary / ghost button -->
<button class="bg-gray-200 text-gray-800 px-4 py-2 rounded-lg
hover:bg-gray-300 transition-colors">
Cancel
</button>
<!-- Small icon button -->
<button class="p-1.5 rounded-lg hover:bg-gray-100 transition-colors text-gray-500">
<XMarkIcon class="w-5 h-5" />
</button>Form inputs
<!-- Standard text input -->
<input
type="text"
class="w-full px-4 py-2 border border-gray-300 rounded-lg
focus:ring-2 focus:ring-[#117ea7]/50 focus:border-transparent
outline-none transition-all text-sm"
/>
<!-- Input with error state -->
<input
type="text"
class="w-full px-4 py-2 border rounded-lg outline-none transition-all text-sm
border-red-500 focus:ring-2 focus:ring-red-500/50"
/>
<!-- Select dropdown -->
<select class="w-full px-4 py-2 border border-gray-300 rounded-lg
focus:ring-2 focus:ring-[#117ea7]/50 outline-none text-sm">
<option>Option</option>
</select>Modals
<!-- Modal backdrop -->
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<!-- Modal container -->
<div class="bg-white rounded-3xl shadow-xl w-full max-w-lg mx-4 overflow-hidden">
<!-- Branded header -->
<div class="bg-[#117ea7] text-white px-6 py-4 flex items-center justify-between">
<h2 class="text-lg font-semibold">Modal Title</h2>
<button class="text-white/80 hover:text-white">
<XMarkIcon class="w-5 h-5" />
</button>
</div>
<!-- Body -->
<div class="p-6">
<!-- content -->
</div>
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-100 flex justify-end gap-3">
<button class="btn-secondary">Cancel</button>
<button class="btn-primary">Confirm</button>
</div>
</div>
</div>Arbitrary Values
When the Tailwind default palette does not include the exact value needed, use arbitrary value syntax:
<!-- Brand color backgrounds -->
<div class="bg-[#117ea7]">
<div class="hover:bg-[#0e6b8f]">
<!-- Fixed header height -->
<header class="h-[52px]">
<!-- Focus ring with brand color and opacity -->
<input class="focus:ring-[#117ea7]/50">Use arbitrary values sparingly. If you find yourself repeating the same arbitrary value in many places, consider whether it should be added to a Tailwind config extension or extracted into a @layer components class.
Transition Patterns
CPR applies transitions to interactive elements for a polished feel:
<!-- Color transitions (buttons, links) -->
<button class="transition-colors">
<!-- All-property transitions (inputs with ring + border changes) -->
<input class="transition-all">
<!-- Shadow transitions (hoverable cards) -->
<div class="transition-shadow hover:shadow-md">
<!-- Transform transitions (sidebar slide) -->
<aside class="transition-transform duration-300">The default transition-colors is the most commonly used. Avoid transition-all on elements where only one property changes, as it is less performant.
Purge / Content Detection
The @nuxtjs/tailwindcss module automatically scans all .vue, .ts, and .js files in the Nuxt project for class usage. You do not need to configure content paths manually. However, be aware of two implications:
- Dynamic class names must be complete -- Tailwind cannot detect classes built from string concatenation. Write
text-red-500not`text-${color}-500`. - Classes in
main.cssare always included -- Anything inside@layer componentsor@layer baseusing@applyis resolved at build time and included in the output regardless of template usage.
