Skip to content

Last updated:

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:

css
@tailwind base;
@tailwind components;
@tailwind utilities;

Utility Patterns Used in CPR

Layout

Flexbox and grid are the primary layout tools:

html
<!-- 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 caseClasses
Card paddingp-6
Section gapsgap-4 or gap-6
Inline element spacinggap-2 or gap-3
Page marginspx-4 sm:px-6
Vertical section spacingspace-y-4 or space-y-6

Responsive Design Patterns

CPR uses two primary breakpoints:

BreakpointPrefixPurpose
640pxsm:Compact info display, form layouts
1024pxlg:Desktop navigation, sidebar visibility

Mobile-first examples

html
<!-- 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

html
<!-- 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

html
<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

html
<!-- 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

html
<!-- 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

html
<!-- 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:

html
<!-- 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:

html
<!-- 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:

  1. Dynamic class names must be complete -- Tailwind cannot detect classes built from string concatenation. Write text-red-500 not `text-${color}-500`.
  2. Classes in main.css are always included -- Anything inside @layer components or @layer base using @apply is resolved at build time and included in the output regardless of template usage.

CPR - Clinical Patient Records