Skip to content

Study Guide: Tailwind CSS + shadcn/ui

tailwind.css LIVE DEMO
Traditional CSS

My Project

A short description of the project.

style.css: 15 lines
.card {
  background: white;
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0,0,0,.1);
  overflow: hidden;
}
.card-img {
  height: 80px;
  background: linear-gradient(135deg,#6366f1,#06b6d4);
}
.card-body { padding: 16px; }
.card-title {
  font-size: 16px;
  font-weight: 700;
  color: #111;
  margin: 0 0 6px;
}
.card-desc {
  font-size: 12px;
  color: #666;
  margin: 0 0 12px;
}
.card-btn {
  width: 100%;
  padding: 8px;
  background: #3b82f6;
  color: white;
  border: none;
  border-radius: 8px;
  font-weight: 600;
  cursor: pointer;
}
Tailwind CSS

My Project

A short description of the project.

class="...": 1 line
<div class="bg-white rounded-xl
  shadow-sm overflow-hidden">
  <div class="h-20
    bg-gradient-to-br
    from-indigo-500 to-cyan-500">
  </div>
  <div class="p-4">
    <h4 class="text-base font-bold
      text-gray-900 mb-1.5">...</h4>
    <p class="text-xs text-gray-500
      mb-3">...</p>
    <button class="w-full py-2
      bg-blue-500 text-white
      rounded-lg font-semibold">
      View
    </button>
  </div>
</div>

ผลลัพธ์เหมือนกัน แต่ Tailwind เขียนตรงใน HTML ไม่ต้องสลับไฟล์

Preview
The Box
Spacing
Colors
Rounded
Shadow
class=  

คลิก chip เพื่อเพิ่ม class — กดอีกทีเพื่อเอาออก

1
Card A
2
Card B
3
Card C
grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4

เปลี่ยน viewport เพื่อดูว่า breakpoint prefix ไหนทำงาน

Light Mode

Dashboard

Welcome back! You have 3 new notifications today.

3 new
Card: bg-white dark:bg-gray-900
Title: text-gray-900 dark:text-white
Text: text-gray-600 dark:text-gray-400
Button: bg-blue-600 dark:bg-blue-500
Badge: bg-blue-50 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300

สลับ toggle เพื่อดูว่า dark: prefix เปลี่ยน style อย่างไร

Tailwind คือ Utility-first CSS framework — แทนที่จะเขียน CSS แยกไฟล์ เราใส่ class ตรงใน HTML เลย

<!-- CSS ปกติ: เขียน class แล้วไป style ที่ style.css -->
<div class="card">
<h2 class="card-title">Hello</h2>
</div>
<!-- Tailwind: เขียน style ตรงใน class เลย -->
<div class="p-6 bg-white rounded-xl shadow-md">
<h2 class="text-xl font-bold text-gray-900">Hello</h2>
</div>

Class ที่ใช้บ่อยที่สุด

Section titled “Class ที่ใช้บ่อยที่สุด”
<!-- p = padding, m = margin -->
<!-- ตัวเลข: 1=4px, 2=8px, 3=12px, 4=16px, 6=24px, 8=32px -->
<div class="p-4">padding 16px ทุกด้าน</div>
<div class="px-6 py-3">padding X=24px, Y=12px</div>
<div class="mt-4">margin-top 16px</div>
<div class="mb-8">margin-bottom 32px</div>
<div class="mx-auto">จัดกลางแนวนอน</div>
<h1 class="text-3xl font-bold text-gray-900">Big Title</h1>
<p class="text-sm text-gray-500 leading-relaxed">
Small text with relaxed line height
</p>
<span class="text-xs font-medium text-blue-600 uppercase tracking-wider">
Label
</span>
Output
A
p-2 8px
B
p-4 16px
C
p-8 32px
<!-- text color -->
<p class="text-gray-900">เข้ม</p>
<p class="text-gray-500">กลาง</p>
<p class="text-blue-600">สีน้ำเงิน</p>
<!-- background -->
<div class="bg-white">พื้นขาว</div>
<div class="bg-blue-50">พื้นน้ำเงินอ่อน</div>
<div class="bg-gray-900">พื้นเข้ม</div>
<!-- opacity -->
<div class="bg-blue-500/20">น้ำเงินจาง 20%</div>
<!-- Flexbox -->
<div class="flex items-center justify-between gap-4">
<span>Left</span>
<span>Right</span>
</div>
<!-- จัดกลางจอ -->
<div class="flex items-center justify-center min-h-screen">
<h1>Centered</h1>
</div>
<!-- Grid 3 columns -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>Card 1</div>
<div>Card 2</div>
<div>Card 3</div>
</div>
Output
Flexbox flex items-center gap-4
A
Item 1
B
Item 2
C
Item 3
Grid grid grid-cols-3 gap-4
1
2
3
<div class="border border-gray-200 rounded-lg">มีขอบ</div>
<div class="rounded-xl shadow-md">มุมมนกับเงา</div>
<div class="rounded-full">วงกลม</div>
<div class="border-b border-gray-100">เส้นล่าง</div>

Tailwind ใช้ prefix สำหรับ breakpoint — mobile-first

Prefixขนาดจอตัวอย่าง
(ไม่ใส่)ทุกขนาด (mobile)text-sm
sm:640px+sm:text-base
md:768px+md:grid-cols-2
lg:1024px+lg:grid-cols-3
xl:1280px+xl:max-w-6xl
<!-- 1 col บน mobile, 2 col บน tablet, 3 col บน desktop -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="p-4 bg-white rounded-xl">Card</div>
<div class="p-4 bg-white rounded-xl">Card</div>
<div class="p-4 bg-white rounded-xl">Card</div>
</div>
<!-- ซ่อนบน mobile, แสดงบน desktop -->
<nav class="hidden md:flex gap-4">
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
Output
Card 1
Card 2
Card 3
grid-cols-1 md:grid-cols-2 lg:grid-cols-3

<!-- hover effect -->
<button class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg
transition-colors duration-200">
Click Me
</button>
<!-- card hover -->
<div class="p-6 bg-white rounded-xl border border-gray-200
hover:shadow-lg hover:border-blue-300
hover:scale-[1.02] transition-all duration-300">
<h3>Hover Card</h3>
</div>
<!-- focus ring on input -->
<input class="w-full px-4 py-2 border border-gray-300 rounded-lg
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Type here..." />

<!-- ใช้ dark: prefix -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
<h1 class="text-2xl font-bold">
Works in both themes!
</h1>
<p class="text-gray-600 dark:text-gray-400">
This text adapts automatically.
</p>
</div>
Output
Light Dark

Dashboard

Welcome back! You have 3 new notifications.

3 new
bg-white dark:bg-gray-900
text-gray-900 dark:text-gray-100
text-gray-600 dark:text-gray-400

shadcn/ui คือ component library ที่ให้ copy โค้ดมาใส่โปรเจกต์เรา — ไม่ใช่ npm package แต่เป็นโค้ดที่เราเป็นเจ้าของ 100%

// Button
<Button variant="default">Primary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
// Input
<Input placeholder="Email" type="email" />
// Card
<Card>
<CardHeader>
<CardTitle>Dashboard</CardTitle>
<CardDescription>Overview of your data</CardDescription>
</CardHeader>
<CardContent>
<p>Content here</p>
</CardContent>
</Card>
// Dialog (Modal)
<Dialog>
<DialogTrigger asChild>
<Button>Open Modal</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Edit Profile</DialogTitle>
</DialogHeader>
<p>Modal content here</p>
</DialogContent>
</Dialog>

เมื่อไหร่ควรใช้ shadcn/ui?

Section titled “เมื่อไหร่ควรใช้ shadcn/ui?”
  • ต้องการ UI สวยพร้อมใช้ เร็ว
  • ทำ Dashboard, Admin Panel, Form
  • ต้องการ components ที่ accessible (รองรับ keyboard, screen reader)
  • อยาก customize ได้เต็มที่ (เพราะโค้ดเป็นของเรา)

ตัวอย่าง: สร้าง Card ด้วย Tailwind

Section titled “ตัวอย่าง: สร้าง Card ด้วย Tailwind”
<div class="max-w-sm mx-auto">
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-lg overflow-hidden
hover:shadow-xl transition-shadow duration-300">
<!-- Image -->
<img src="project.jpg" alt="Project" class="w-full h-48 object-cover" />
<!-- Content -->
<div class="p-6">
<div class="flex items-center gap-2 mb-3">
<span class="text-xs font-medium text-blue-600 bg-blue-50
dark:text-blue-400 dark:bg-blue-900/30
px-2.5 py-1 rounded-full">
Design
</span>
<span class="text-xs text-gray-400">3 days ago</span>
</div>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-2">
My Project Title
</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4 line-clamp-2">
This is a short description of the project that might be long.
</p>
<button class="w-full py-2.5 bg-blue-600 hover:bg-blue-700
text-white text-sm font-medium rounded-xl
transition-colors duration-200">
View Project
</button>
</div>
</div>
</div>

Tailwind v4 ใช้ระบบใหม่ — ไม่มี tailwind.config.js แล้ว ใช้ @theme block ใน CSS แทน

app.css
@import "tailwindcss";
@theme {
/* Custom Colors */
--color-brand: #6366f1;
--color-brand-light: #a5b4fc;
--color-brand-dark: #4338ca;
--color-surface: #f8fafc;
--color-surface-dark: #1e293b;
/* Custom Fonts */
--font-heading: "Inter", sans-serif;
--font-body: "Noto Sans Thai", sans-serif;
/* Custom Spacing */
--spacing-18: 4.5rem; /* 72px */
--spacing-88: 22rem; /* 352px */
/* Custom Border Radius */
--radius-card: 1rem;
--radius-button: 0.625rem;
}
<!-- ใช้ custom colors จาก @theme -->
<div class="bg-brand text-white p-6 rounded-card">
<h2 class="font-heading text-2xl font-bold">Custom Theme</h2>
<p class="font-body text-brand-light">สวยงามตาม brand ของเรา</p>
</div>
<button class="bg-brand hover:bg-brand-dark text-white px-6 py-2 rounded-button
transition-colors duration-200">
Brand Button
</button>

เมื่อต้องการค่าที่ไม่มีใน theme — ใช้ วงเล็บก้ามปู []

<!-- arbitrary color -->
<div class="bg-[#1a1a2e] text-[#e94560]">Custom colors</div>
<!-- arbitrary size -->
<div class="w-[200px] h-[calc(100vh-64px)]">Custom sizing</div>
<!-- arbitrary font-size -->
<p class="text-[15px] leading-[1.8]">ขนาดเฉพาะเจาะจง</p>
<!-- arbitrary spacing -->
<div class="mt-[60px] p-[clamp(1rem,3vw,2rem)]">Responsive spacing</div>

Pattern สำเร็จรูปที่ใช้ในงานจริงบ่อยที่สุด

<div class="flex min-h-screen">
<!-- Sidebar: ซ่อนบน mobile, แสดงบน desktop -->
<aside class="hidden lg:flex lg:flex-col lg:w-64 bg-gray-900 text-white p-6">
<h2 class="text-lg font-bold mb-6">Dashboard</h2>
<nav class="flex flex-col gap-2">
<a href="#" class="px-3 py-2 rounded-lg bg-gray-800 text-white">Home</a>
<a href="#" class="px-3 py-2 rounded-lg hover:bg-gray-800 text-gray-400">Settings</a>
<a href="#" class="px-3 py-2 rounded-lg hover:bg-gray-800 text-gray-400">Profile</a>
</nav>
</aside>
<!-- Content -->
<main class="flex-1 p-6 lg:p-10 bg-gray-50">
<h1 class="text-2xl font-bold mb-6">Page Content</h1>
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
<div class="bg-white p-6 rounded-xl shadow-sm">Card 1</div>
<div class="bg-white p-6 rounded-xl shadow-sm">Card 2</div>
<div class="bg-white p-6 rounded-xl shadow-sm">Card 3</div>
</div>
</main>
</div>
<header class="sticky top-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-200">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<!-- Logo -->
<a href="/" class="text-xl font-bold text-gray-900">Logo</a>
<!-- Desktop Nav -->
<nav class="hidden md:flex items-center gap-6">
<a href="#" class="text-sm text-gray-600 hover:text-gray-900">Home</a>
<a href="#" class="text-sm text-gray-600 hover:text-gray-900">About</a>
<a href="#" class="text-sm text-gray-600 hover:text-gray-900">Contact</a>
<button class="bg-blue-600 hover:bg-blue-700 text-white text-sm px-4 py-2
rounded-lg transition-colors">
Sign Up
</button>
</nav>
<!-- Mobile Menu Button -->
<button class="md:hidden p-2 text-gray-600" id="menu-toggle">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"/>
</svg>
</button>
</div>
</div>
</header>
<!-- Overlay + Drawer -->
<div class="fixed inset-0 z-50 lg:hidden">
<!-- Backdrop -->
<div class="fixed inset-0 bg-black/50" id="drawer-backdrop"></div>
<!-- Drawer Panel -->
<div class="fixed inset-y-0 left-0 w-72 bg-white shadow-xl
transform transition-transform duration-300">
<div class="flex items-center justify-between p-4 border-b">
<span class="text-lg font-bold">Menu</span>
<button class="p-2 text-gray-500 hover:text-gray-700" id="drawer-close">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<nav class="flex flex-col p-4 gap-1">
<a href="#" class="px-3 py-2 rounded-lg text-gray-700 hover:bg-gray-100">Home</a>
<a href="#" class="px-3 py-2 rounded-lg text-gray-700 hover:bg-gray-100">About</a>
<a href="#" class="px-3 py-2 rounded-lg text-gray-700 hover:bg-gray-100">Contact</a>
</nav>
</div>
</div>
<!-- container จัดกลาง + responsive padding -->
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
<p>Content stays centered and readable</p>
</div>
<!-- max-w ควบคุมความกว้างสูงสุด -->
<div class="max-w-md mx-auto">Narrow (28rem)</div>
<div class="max-w-2xl mx-auto">Medium (42rem)</div>
<div class="max-w-5xl mx-auto">Wide (64rem)</div>
<div class="max-w-7xl mx-auto">Full layout (80rem)</div>

Reusable Patterns จาก Tailwind Classes

Section titled “Reusable Patterns จาก Tailwind Classes”

แทนที่จะ copy class ยาว ๆ ซ้ำ สร้างเป็น component ใน framework ของเรา

// ซ้ำ class ทุกที่
<button class="bg-blue-600 hover:bg-blue-700 text-white font-medium px-4 py-2 rounded-lg transition-colors">Save</button>
<button class="bg-blue-600 hover:bg-blue-700 text-white font-medium px-4 py-2 rounded-lg transition-colors">Submit</button>
// สร้างเป็น component แทน
function Button({ children, variant = "primary", size = "md" }) {
const base = "font-medium rounded-lg transition-colors";
const variants = {
primary: "bg-blue-600 hover:bg-blue-700 text-white",
secondary: "bg-gray-200 hover:bg-gray-300 text-gray-800",
danger: "bg-red-600 hover:bg-red-700 text-white",
ghost: "hover:bg-gray-100 text-gray-700",
};
const sizes = {
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2 text-sm",
lg: "px-6 py-3 text-base",
};
return (
<button class={`${base} ${variants[variant]} ${sizes[size]}`}>
{children}
</button>
);
}

เมื่อไหร่ควรใช้ @apply?

Section titled “เมื่อไหร่ควรใช้ @apply?”
/* ใช้ @apply เมื่อ: ซ้ำบ่อยมากและไม่อยู่ใน component framework */
.prose a {
@apply text-blue-600 underline hover:text-blue-800 transition-colors;
}
/* ใช้กับ third-party elements ที่ control ไม่ได้ */
.markdown-body h2 {
@apply text-2xl font-bold text-gray-900 mt-8 mb-4;
}
/* ไม่ควรใช้ @apply สร้าง component class -- ใช้ JSX component แทน */
.btn-primary {
@apply bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg;
}

Design Token Approach — CSS Variables + Tailwind

Section titled “Design Token Approach — CSS Variables + Tailwind”
tokens.css
/* กำหนดค่าตรงกลาง */
:root {
--color-primary: #6366f1;
--color-primary-hover: #4f46e5;
--radius-default: 0.5rem;
--shadow-card: 0 1px 3px rgba(0,0,0,0.1);
}
.dark {
--color-primary: #818cf8;
--color-primary-hover: #a5b4fc;
}
<!-- ใช้ CSS variables เป็น arbitrary values -->
<button class="bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)]
text-white px-4 py-2 rounded-[var(--radius-default)]">
Token Button
</button>
<div class="bg-white shadow-[var(--shadow-card)] rounded-[var(--radius-default)] p-6">
Token Card
</div>

ตัวอย่าง: Button Variants จาก Tailwind

Section titled “ตัวอย่าง: Button Variants จาก Tailwind”
<!-- Primary -->
<button class="bg-blue-600 hover:bg-blue-700 text-white
font-medium px-4 py-2 rounded-lg transition-colors">
Primary
</button>
<!-- Secondary -->
<button class="bg-gray-100 hover:bg-gray-200 text-gray-800
font-medium px-4 py-2 rounded-lg transition-colors">
Secondary
</button>
<!-- Outline -->
<button class="border border-gray-300 hover:bg-gray-50 text-gray-700
font-medium px-4 py-2 rounded-lg transition-colors">
Outline
</button>
<!-- Ghost -->
<button class="hover:bg-gray-100 text-gray-600
font-medium px-4 py-2 rounded-lg transition-colors">
Ghost
</button>
<!-- Destructive -->
<button class="bg-red-600 hover:bg-red-700 text-white
font-medium px-4 py-2 rounded-lg transition-colors">
Delete
</button>
Output
Buttons
Badges
Design Active Pending Error
Card
Dashboard Active

Overview of your data with key metrics and insights.


เรียง class ตามลำดับนี้เพื่อให้อ่านง่าย:

ลำดับหมวดตัวอย่าง
1Layoutflex, grid, block, hidden, relative
2Positioningabsolute, top-0, z-10
3Spacingp-4, m-2, gap-6
4Sizingw-full, h-16, max-w-md
5Typographytext-sm, font-bold, leading-relaxed
6Colorsbg-white, text-gray-900, border-gray-200
7Effectsshadow-md, opacity-50, rounded-lg
8Transitionstransition-colors, duration-200
9Stateshover:bg-blue-700, focus:ring-2
10Responsivemd:flex, lg:grid-cols-3
<!-- Don't: สลับหมวดมั่ว อ่านยาก -->
<div class="text-white rounded-lg hover:bg-blue-700 flex p-4 bg-blue-600 gap-4 items-center">
<!-- Do: เรียงตามหมวด layout > spacing > typography > colors > effects > states -->
<div class="flex items-center gap-4 p-4 text-white bg-blue-600 rounded-lg hover:bg-blue-700">
<!-- Don't: เขียน desktop-first แล้วซ่อนด้วย max-* (ไม่ใช่ Tailwind way) -->
<div class="grid grid-cols-3 max-md:grid-cols-1">
<!-- Do: mobile-first เริ่มจากเล็กแล้วเพิ่ม breakpoint ขึ้นไป -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
<!-- Don't: ใช้ ! (important) เพื่อ override -->
<p class="!text-red-500">Error message</p>
<!-- Do: ใช้ specificity ที่ถูกต้อง หรือ restructure HTML -->
<p class="text-red-500 font-medium">Error message</p>
/* Don't: เขียน CSS ยาวเมื่อ Tailwind ทำได้ */
.custom-card {
display: flex;
padding: 1.5rem;
background-color: white;
border-radius: 0.75rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
<!-- Do: ใช้ Tailwind classes ตรง ๆ -->
<div class="flex p-6 bg-white rounded-xl shadow-sm">
/* เขียน custom CSS เมื่อ: animation ซับซ้อน, pseudo-elements, หรือ styles ที่ Tailwind ไม่รองรับ */
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.animate-float {
animation: float 3s ease-in-out infinite;
}

File Structure สำหรับ Tailwind Projects

Section titled “File Structure สำหรับ Tailwind Projects”
src/
├── app.css <- @import "tailwindcss" + @theme block
├── components/
│ ├── Button.jsx <- component ที่รวม Tailwind classes
│ ├── Card.jsx
│ └── Layout.jsx
├── styles/
│ ├── tokens.css <- CSS variables / design tokens
│ └── animations.css <- custom @keyframes ที่ Tailwind ไม่มี
└── pages/
├── Home.jsx
└── Dashboard.jsx

  1. สร้าง Navbar — logo ซ้าย, links ขวา, responsive (hamburger บน mobile)

  2. สร้าง Card Component — รูป, title, description, button พร้อม hover effect

  3. สร้าง Sidebar Layout — sidebar ซ้าย + content ขวา ใช้ Flexbox

  4. ทำ Data Table — ตารางที่มี header, rows, border, hover state

  5. ลอง Dark Mode — ใช้ dark: prefix ให้ทุก component สลับ theme ได้