← TypeScript 12 / 18 Next: Docker β†’

Tailwind CSS

Module 4 · Frontend · ⏱ ~20 min
Why this matters: Every style in the JongYang frontend comes from Tailwind utility classes. Instead of writing CSS files, you apply pre-built classes directly in JSX. Once you learn the naming pattern, you can read any component's styling instantly.

Utility-First CSS: The Mental Model Shift

Traditional CSS: you write a class name, then style it in a separate CSS file:

css β€” traditional approach
/* In a .css file somewhere */
.booking-card {
  background: white;
  border-radius: 16px;
  border: 1px solid #e2e8f0;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
  overflow: hidden;
}
tsx β€” Tailwind approach (no CSS file needed)
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm overflow-hidden">
  {/* same result, all inline */}
</div>

Every class is one CSS rule. You compose them like Lego bricks. At build time, Tailwind generates a CSS file with only the classes you actually use β€” no unused styles shipped.

The Naming System

ClassCSS equivalent
bg-whitebackground-color: white
bg-teal-600background-color: #0d9488
text-slate-900color: #0f172a
text-smfont-size: 0.875rem
font-boldfont-weight: 700
rounded-xlborder-radius: 0.75rem
rounded-2xlborder-radius: 1rem
p-4padding: 1rem (16px)
px-4 py-3padding: 0.75rem 1rem
flex gap-3display: flex; gap: 0.75rem
w-fullwidth: 100%
border border-slate-200border: 1px solid #e2e8f0
shadow-smbox-shadow: small shadow
transition-colorstransition: color, background-color, ... 150ms
animate-spinanimation: spin 1s linear infinite

The Spacing Scale

Tailwind uses a consistent spacing scale where 1 unit = 4px:

State Variants: hover:, disabled:, focus:

Add a prefix to apply a class only in a certain state:

tsx β€” state variants from the admin confirm button
<button
  onClick={() => handleManualConfirm(b.id)}
  disabled={confirming === b.id}
  className="
    text-xs                    bg-amber-100    text-amber-800  font-medium
    hover:bg-amber-200         /* darker on hover */
    disabled:opacity-50        /* semi-transparent when disabled */
    px-3 py-1.5 rounded-lg transition-colors whitespace-nowrap
  "
>
  {confirming === b.id ? 'Confirming…' : 'Confirm (Demo)'}
</button>

Responsive Prefixes: Mobile-First

Tailwind is mobile-first. Classes apply at all sizes; prefixes apply at breakpoints and above:

tsx β€” responsive layout
<div className="
  grid
  grid-cols-1      /* 1 column on mobile */
  md:grid-cols-2   /* 2 columns on medium screens (768px+) */
  lg:grid-cols-3   /* 3 columns on large screens (1024px+) */
  gap-4
">

The Project's Color Palette

JongYang uses a consistent color system:

ColorClass prefixUsed for
Tealteal-Primary CTA buttons, active states, brand color
Slate-900slate-900Dark backgrounds, admin panel
Slate-500slate-500Secondary text, labels
Slate-100slate-100Light borders, table hover backgrounds
Amberamber-Warnings, demo-only labels, pending_payment badges
Greengreen-confirmed status badges, LINE connected indicator
Redred-Error messages, cancelled status badges

The Booking Status Badge Pattern

In the admin bookings page, status badges use a lookup object:

tsx β€” status badge from admin/bookings/page.tsx
const STATUS_BADGE: Record<string, string> = {
  confirmed:        'bg-green-100 text-green-800',
  pending_payment:  'bg-amber-100 text-amber-800',
  cancelled:        'bg-red-100 text-red-800',
}

// In the table row:
<span className={`inline-block px-2 py-0.5 rounded-full text-xs font-medium
  ${STATUS_BADGE[b.status] ?? 'bg-slate-100 text-slate-600'}`}>
  {b.status.replace('_', ' ')}
</span>

The bg-green-100 text-green-800 pattern is standard Tailwind for colored badges β€” a light background color with a darker text of the same hue.

🧠 Self-Check Quiz

1. What CSS does className="px-4 py-3" produce?

padding-left: 1rem; padding-right: 1rem; padding-top: 0.75rem; padding-bottom: 0.75rem; β€” that's 16px horizontal and 12px vertical. px- sets left+right padding; py- sets top+bottom. The number is the spacing scale (4 = 16px, 3 = 12px).

2. What does hover:bg-amber-200 do and when does it apply?

It sets background-color to amber-200 (#fde68a) only when the user hovers over the element. The hover: prefix is a state variant β€” without it, bg-amber-200 would apply at all times. In the demo confirm button, the background darkens from amber-100 to amber-200 on hover to give visual feedback.

3. Tailwind is "mobile-first". What does md:grid-cols-2 mean?

On screens 768px wide or wider (the "md" breakpoint), apply grid-template-columns: repeat(2, minmax(0, 1fr)). On smaller screens (mobile), there's no effect from this class β€” whatever the base (non-prefixed) class says takes effect. Mobile-first means you design for small screens first, then add breakpoint prefixes for larger screens.

4. What is the status badge pattern and why does it use bg-green-100 text-green-800 instead of just bg-green-800?

The pattern uses a very light background (100 = lightest shade) with dark text of the same color family (800 = dark). A pure dark background (bg-green-800) with white text is harder to read at small sizes and looks harsh. The light background + dark text combination is more readable and visually softer β€” standard for status pills/chips in admin UIs.

5. How does Tailwind know which classes to include in the final CSS file? Does it include all possible classes?

No β€” Tailwind scans your source files at build time and only generates CSS for classes it finds. If bg-pink-500 appears nowhere in your code, it won't be in the output CSS. This keeps the CSS file tiny. The build scans all .tsx, .ts, and .html files matching the content paths in the Tailwind config.