Day 3 / Hour 1

Tailwind Setup and Utilities

Tailwind setup, spacing, typography, colors, layout, and state utilities.

60 minutes
Learners start converting the UI to Tailwind.

Tailwind Setup and Utilities

Day 3 - ชั่วโมงที่ 1: Tailwind CSS Setup and Utility-first Styling

เป้าหมายของชั่วโมงนี้

หลังจบชั่วโมงแรกของ Day 3 ผู้เรียนควรสามารถ:

  1. เข้าใจว่า Tailwind CSS คืออะไร และต่างจาก CSS ปกติอย่างไร
  2. ติดตั้ง Tailwind CSS ใน Next.js project ที่สร้างจาก Day 2 ได้
  3. เข้าใจว่า globals.css ยังมีบทบาทอย่างไรหลังใช้ Tailwind
  4. ใช้ utility class พื้นฐาน เช่น spacing, typography, color, border และ layout ได้
  5. ค่อย ๆ แปลง style จาก CSS เดิมไปเป็น Tailwind class ได้
  6. เข้าใจว่า Tailwind จะช่วยให้ component ใน Day 2 ปรับ UI ได้เร็วขึ้น

ไฟล์ที่ใช้ในชั่วโมงนี้

ติดตั้ง package ที่ project root

สร้างหรือแก้ไฟล์:

postcss.config.mjs
src/app/globals.css
src/app/layout.tsx
src/app/page.tsx
src/components/*.tsx

ถ้า slide เป็น config ให้ระบุ File: postcss.config.mjs

ถ้า slide เป็น Tailwind class ของ UI ให้ระบุไฟล์ที่แก้ เช่น src/app/layout.tsx, src/app/page.tsx หรือไฟล์ component


โครงสร้างเวลา 60 นาที

เวลาหัวข้อรูปแบบ
0-5 นาทีRecap Day 2เชื่อมเข้าเนื้อหา
5-15 นาทีTailwind CSS คืออะไรExplain
15-25 นาทีติดตั้ง Tailwind ใน Next.js projectLive coding
25-35 นาทีUtility class พื้นฐานDemo
35-42 นาทีแปลง navigation จาก CSS เดิมเป็น TailwindLive coding
42-52 นาทีแปลง layout เดิมบางส่วนเป็น TailwindLive coding
52-60 นาทีสรุปสิ่งที่ทำทำทีละขั้นตอน

Slide 1: Recap จาก Day 2

ตอนนี้ project ของเรามีอะไรแล้ว

  • Next.js project
  • page.tsx
  • layout.tsx
  • globals.css
  • Issue type
  • mock data issues: Issue[]
  • IssueForm
  • IssueList
  • StatusBadge
  • route พื้นฐาน เช่น /issues, /issues/new, /issues/[id]

ปัญหาที่เริ่มเห็น

  • CSS ใน globals.css เริ่มยาว
  • บาง style ผูกกับ selector กว้างเกินไป
  • ถ้า component เยอะขึ้น การตั้ง class เองอาจเริ่มดูแลยาก
  • UI ยังเป็น static prototype มากกว่า application UI

Key Message

Day 3 เราจะทำให้ Next.js app พร้อมเป็น frontend CRUD prototype มากขึ้น โดยเริ่มจากปรับ UI ด้วย Tailwind CSS


Slide 2: Day 3 จะทำอะไรทั้งวัน

เป้าหมายของ Day 3

ทำให้ app จาก Day 2 พร้อมต่อยอดเข้า database ใน Day 4

Hour 1: ติดตั้ง Tailwind และเข้าใจ utility class
Hour 2: ปรับ component UI และ responsive layout
Hour 3: เพิ่ม form state และ validation ฝั่ง frontend
Hour 4: ทำ mock CRUD flow และเตรียมเชื่อม database

Key Message

วันนี้ยังไม่เชื่อม database จริง แต่เราจะทำให้ flow ฝั่ง frontend ใกล้กับระบบจริงมากขึ้น


Slide 3: Tailwind CSS คืออะไร

Tailwind CSS

Tailwind คือ utility-first CSS framework

แทนที่จะเขียน CSS แบบนี้:

.panel {
  background: white;
  border-radius: 8px;
  padding: 24px;
}

เราเขียน class ใน JSX/TSX แบบนี้:

<section className="rounded-lg border border-slate-200 bg-white p-6">
  ...
</section>

Key Message

Tailwind ไม่ได้ทำให้ไม่ต้องเข้าใจ CSS แต่ทำให้เราใช้ CSS concept ผ่าน class สำเร็จรูปได้เร็วขึ้น


Slide 4: CSS ปกติ vs Tailwind

CSS ปกติ

<button className="primary-button">ส่งข้อมูล</button>
.primary-button {
  border-radius: 6px;
  padding: 12px 16px;
  background: #0f766e;
  color: white;
}

Tailwind

<button className="rounded-md bg-teal-700 px-4 py-3 font-bold text-white hover:bg-teal-800">
  ส่งข้อมูล
</button>

ข้อดี

  • เห็น style อยู่ใกล้ component
  • ลดการตั้งชื่อ class เอง
  • ปรับ responsive และ state ได้เร็ว
  • เหมาะกับ component-based UI

Slide 5: ติดตั้ง Tailwind CSS ใน Next.js Project

ถ้า Day 2 เลือก Tailwind CSS: No

ให้ติดตั้ง Tailwind เพิ่มใน project เดิม

ตำแหน่งที่รันคำสั่ง

รันที่ project root ของ Next.js ซึ่งเป็น folder ที่มี package.json

npm install tailwindcss @tailwindcss/postcss postcss

สร้างไฟล์ postcss.config.mjs

วางไฟล์นี้ไว้ที่ project root ระดับเดียวกับ package.json

const config = {
  plugins: {
  "@tailwindcss/postcss": {},
  },
};
 
export default config;

เพิ่มใน src/app/globals.css

วาง @import "tailwindcss"; ไว้บรรทัดแรกของไฟล์ src/app/globals.css

@import "tailwindcss";

หมายเหตุ

ถ้า project ถูกสร้างพร้อม Tailwind ตั้งแต่ Day 2 อยู่แล้ว ให้ตรวจว่ามี postcss.config.mjs และ @import "tailwindcss"; แล้ว จากนั้นข้ามขั้นตอนติดตั้งได้


Slide 6: globals.css ยังจำเป็นไหม

ยังจำเป็น

แม้ใช้ Tailwind แล้ว globals.css ยังใช้สำหรับ:

  • import Tailwind
  • reset หรือ style global ที่จำเป็น
  • body background/font พื้นฐาน
  • style เฉพาะบางอย่างที่ไม่อยากเขียนซ้ำ

ตัวอย่าง

@import "tailwindcss";
 
* {
  box-sizing: border-box;
}
 
body {
  margin: 0;
}

Key Message

Tailwind ไม่ได้แปลว่าต้องลบ CSS ทั้งหมดทันที เราจะค่อย ๆ ย้ายจาก CSS เดิมไปเป็น utility class


Slide 7: Utility Class พื้นฐาน - Spacing

Padding

<div className="p-4">padding ทุกด้าน</div>
<div className="px-4 py-2">padding แนวนอนและแนวตั้ง</div>

Margin

<section className="mt-6">margin-top</section>
<main className="mx-auto">margin ซ้ายขวา auto</main>

Gap

<div className="flex gap-4">...</div>
<div className="grid gap-6">...</div>

ความหมายคร่าว ๆ

flowchart LR
  step0["p-4"]
  step1["padding"]
  step0 --> step1

Slide 8: Utility Class พื้นฐาน - Typography

<h1 className="text-3xl font-bold text-slate-950">
  ระบบแจ้งปัญหา IT
</h1>
 
<p className="text-sm text-slate-600">
  แจ้งและติดตามปัญหาการใช้งานระบบภายใน
</p>

Class ที่ใช้บ่อย

Classความหมาย
text-smตัวอักษรขนาดเล็ก
text-lgตัวอักษรขนาดใหญ่
text-3xlheading ขนาดใหญ่
font-mediumน้ำหนักตัวอักษรกลาง
font-boldตัวหนา
text-slate-600สีตัวอักษร

Slide 9: Utility Class พื้นฐาน - Color, Border, Radius

<section className="rounded-lg border border-slate-200 bg-white">
  ...
</section>

Class ที่ใช้บ่อย

Classความหมาย
bg-whiteพื้นหลังสีขาว
bg-slate-50พื้นหลังเทาอ่อน
text-slate-900ตัวอักษรสีเข้ม
borderเส้นขอบ
border-slate-200สีเส้นขอบ
rounded-mdมุมโค้งปานกลาง
rounded-lgมุมโค้งใหญ่กว่า

Slide 10: Utility Class พื้นฐาน - Layout

Container

<main className="mx-auto max-w-5xl px-6 py-8">
  ...
</main>

Flex

<div className="flex items-center justify-between gap-4">
  ...
</div>

Grid

<div className="grid gap-4 md:grid-cols-2">
  ...
</div>

Key Message

Tailwind ใช้ responsive prefix เช่น md: เพื่อบอกว่า style นี้เริ่มทำงานตั้งแต่ breakpoint นั้นขึ้นไป


Slide 11: Responsive Prefix

ตัวอย่าง

<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
  ...
</div>

อธิบาย

flowchart LR
  step0["default"]
  step1["1 column"]
  step0 --> step1

ใช้กับระบบเรา

  • form field เรียงแนวตั้งบน mobile
  • ชื่อผู้แจ้งและอีเมลเป็น 2 คอลัมน์บน desktop
  • dashboard card อาจเป็น 3 คอลัมน์บนจอกว้าง

Slide 12: State Prefix

Hover

<button className="bg-teal-700 hover:bg-teal-800">
  ส่งข้อมูล
</button>

Focus

<input className="focus:border-teal-700 focus:outline-none focus:ring-4 focus:ring-teal-100" />

Disabled

<button className="disabled:cursor-not-allowed disabled:bg-slate-300">
  ส่งข้อมูล
</button>

Key Message

Hover/focus/disabled ที่เราเรียนใน CSS Day 1 ยังเป็น concept เดิม แค่เขียนด้วย Tailwind class


Slide 13: แปลง Navigation เป็น Tailwind

File

src/app/layout.tsx

ตำแหน่งที่แก้

แทนที่เฉพาะ <nav>...</nav> เดิมใน RootLayout ด้วย navigation เวอร์ชัน Tailwind นี้

<nav className="border-b border-slate-200 bg-white" aria-label="เมนูหลัก">
  <div className="mx-auto flex max-w-5xl flex-wrap gap-2 px-6 py-3">
    <Link
      href="/"
      className="rounded-md px-3 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-100 hover:text-slate-950"
    >
      หน้าแรก
    </Link>
    <Link
      href="/issues"
      className="rounded-md px-3 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-100 hover:text-slate-950"
    >
      รายการปัญหา
    </Link>
    <Link
      href="/issues/new"
      className="rounded-md px-3 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-100 hover:text-slate-950"
    >
      แจ้งปัญหาใหม่
    </Link>
  </div>
</nav>

หลังจากเปลี่ยนแล้ว

class เดิมอย่าง .app-nav และ .app-nav-inner ใน src/app/globals.css ยังไม่ต้องรีบลบทันที แต่ถ้าเช็คแล้วไม่มีที่ไหนใช้ ค่อยลบออกได้

Key Message

Navigation อยู่ใน layout.tsx เพราะใช้ร่วมกันทุกหน้า ส่วน Tailwind class ทำให้ style อยู่ใกล้ JSX ที่แสดง UI นั้นจริง ๆ


Slide 14: แปลง Header เป็น Tailwind

File

src/app/page.tsx

ตำแหน่งที่แก้

แทนที่ <header>...</header> เดิมใน HomePage ด้วย header เวอร์ชัน Tailwind นี้

จาก CSS เดิม

<header>
  <h1>ระบบแจ้งปัญหา IT</h1>
  <p>แจ้งและติดตามปัญหาการใช้งานระบบภายใน</p>
</header>

เป็น Tailwind

<header className="bg-teal-800 px-6 py-8 text-white">
  <div className="mx-auto max-w-5xl">
  <h1 className="text-3xl font-bold">ระบบแจ้งปัญหา IT</h1>
  <p className="mt-2 text-teal-100">
    แจ้งและติดตามปัญหาการใช้งานระบบภายใน
  </p>
  </div>
</header>

Key Message

เริ่มแปลงทีละส่วน อย่าแปลงทั้งหน้าในครั้งเดียว


Slide 15: แปลง Main และ Section

File

src/app/page.tsx

ตำแหน่งที่แก้

ปรับ <main> เดิมใน HomePage ให้มี className และปรับ section แรกของ form ให้เป็น section เวอร์ชันนี้

<main className="mx-auto max-w-5xl px-6 py-8">
  <section className="rounded-lg border border-slate-200 bg-white p-6">
  <h2 className="text-xl font-bold text-slate-950">แจ้งปัญหาใหม่</h2>
  <p className="mt-1 text-sm text-slate-600">
    กรอกข้อมูลปัญหาที่ต้องการแจ้งให้ฝ่าย IT ตรวจสอบ
  </p>
  </section>
</main>

อธิบาย

  • mx-auto max-w-5xl จำกัดความกว้างและจัดกลาง
  • px-6 py-8 กำหนดระยะห่าง
  • rounded-lg border bg-white p-6 ทำ section เป็นพื้นที่อ่านง่าย

Slide 16: แปลง Form Group

File

src/app/page.tsx หรือ src/components/IssueForm.tsx

ตำแหน่งที่แก้

ใช้ code Tailwind นี้แทน <div className="form-group">...</div> ของ field title ก่อน จากนั้นค่อยปรับ field อื่นด้วย pattern เดียวกัน

HTML/TSX เดิม

<div className="form-group">
  <label htmlFor="title">หัวข้อปัญหา</label>
  <input id="title" name="title" type="text" required />
</div>

Tailwind

<div className="grid gap-2">
  <label htmlFor="title" className="text-sm font-semibold text-slate-800">
  หัวข้อปัญหา
  </label>
  <input
  id="title"
  name="title"
  type="text"
  required
  className="w-full rounded-md border border-slate-300 px-3 py-2 text-sm focus:border-teal-700 focus:outline-none focus:ring-4 focus:ring-teal-100"
  />
</div>

Speaker Notes

อย่าเพิ่งให้ผู้เรียนจำ class ทั้งหมด ให้เข้าใจว่าแต่ละกลุ่มคือ spacing, border, font, focus state


Slide 17: แปลง Button

File

src/app/page.tsx หรือ src/components/IssueForm.tsx

ตำแหน่งที่แก้

แทนที่ปุ่ม submit เดิมใน form:

<button type="submit">ส่งข้อมูล</button>

ด้วยปุ่มที่ใส่ Tailwind class นี้

<button
  type="submit"
  className="rounded-md bg-teal-700 px-4 py-3 text-sm font-bold text-white hover:bg-teal-800 focus:outline-none focus:ring-4 focus:ring-teal-100 disabled:cursor-not-allowed disabled:bg-slate-300"
>
  ส่งข้อมูล
</button>

เชื่อมกับ Day 1

นี่คือ concept เดิม:

  • background
  • padding
  • border radius
  • hover
  • focus
  • disabled

แต่เขียนด้วย Tailwind class


Slide 18: ควรลบ CSS เดิมไหม

คำตอบ

ยังไม่ต้องลบทั้งหมดทันที

วิธีที่แนะนำ

  1. ติดตั้ง Tailwind
  2. แปลง navigation, page หรือ component ทีละส่วน
  3. ตรวจหน้าเว็บ
  4. ถ้า section ไหนไม่ใช้ CSS เดิมแล้ว ค่อยลบ selector นั้น
  5. เก็บ global style ที่จำเป็นไว้

Key Message

การ migrate UI ที่ดีควรทำทีละขั้น เพื่อให้รู้ว่าถ้า layout พัง มันพังจากจุดไหน


Slide 19: โค้ดสุดท้ายของ Tailwind ขั้นแรก

ขั้นตอน

ใน Next.js project:

  1. ติดตั้ง Tailwind ถ้ายังไม่มี
  2. เพิ่ม @import "tailwindcss"; ใน globals.css
  3. แปลง navigation ใน layout.tsx เป็น Tailwind
  4. แปลง header เป็น Tailwind
  5. แปลง main และ section แรกเป็น Tailwind
  6. แปลง button submit เป็น Tailwind
  7. เปิด browser ตรวจว่า UI ยังแสดงถูกต้อง

ผลลัพธ์

หน้าแรกยังเป็นระบบแจ้งปัญหาเหมือนเดิม แต่เริ่มมี Tailwind class ใน JSX/TSX แล้ว

ตัวอย่างโค้ดสุดท้ายที่ควรเห็น

<nav className="border-b border-slate-200 bg-white" aria-label="เมนูหลัก">
  <div className="mx-auto flex max-w-5xl flex-wrap gap-2 px-6 py-3">
    <Link
      href="/"
      className="rounded-md px-3 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-100"
    >
      หน้าแรก
    </Link>
  </div>
</nav>
 
<header className="bg-teal-800 px-6 py-8 text-white">
  <div className="mx-auto max-w-5xl">
    <h1 className="text-3xl font-bold">ระบบแจ้งปัญหา IT</h1>
    <p className="mt-2 text-teal-100">
      แจ้งและติดตามปัญหาการใช้งานระบบภายใน
    </p>
  </div>
</header>
 
<main className="mx-auto max-w-5xl px-6 py-8">
  <section className="rounded-lg border border-slate-200 bg-white p-6">
    <h2 className="text-xl font-bold text-slate-950">แจ้งปัญหาใหม่</h2>
    <button
      type="submit"
      className="rounded-md bg-teal-700 px-4 py-3 text-sm font-bold text-white hover:bg-teal-800"
    >
      ส่งข้อมูล
    </button>
  </section>
</main>

Slide 20: Common Mistakes

ข้อผิดพลาดที่พบบ่อย

  • ลืม restart dev server หลังติดตั้ง package
  • ลืมเพิ่ม @import "tailwindcss";
  • สะกด class ผิด
  • ใส่ class ยาวเกินไปจนอ่านยาก
  • ลบ CSS เดิมเร็วเกินไปจนหน้าเว็บพัง
  • ใช้สีเยอะเกินไปจน UI ไม่เป็นระบบ
  • พยายามจำทุก class แทนที่จะเข้าใจ pattern

Speaker Notes

Tailwind ควรช่วยให้ทำงานเร็วขึ้น ไม่ใช่ทำให้ผู้เรียนกังวลว่าต้องจำ class ทั้งหมด


Slide 21: Recap ชั่วโมงแรกของ Day 3

สิ่งที่ได้เรียน

  • Tailwind คือ utility-first CSS framework
  • Tailwind ใช้ CSS concept เดิม เช่น spacing, color, border, layout, state
  • Next.js project ที่ไม่ได้เลือก Tailwind ตั้งแต่แรกสามารถติดตั้งเพิ่มได้
  • globals.css ยังใช้ import Tailwind และเก็บ global style ได้
  • การแปลง CSS เดิมไป Tailwind ควรทำทีละส่วน

ต่อไป

เราจะปรับ component จาก Day 2 เช่น IssueForm, IssueList และ StatusBadge ให้ใช้ Tailwind และ responsive layout ที่พร้อมใช้งานมากขึ้น



คำศัพท์สำคัญ

คำศัพท์ความหมาย
Tailwind CSSCSS framework แบบ utility-first
Utility classclass สำเร็จรูปที่แทน CSS property
Responsive prefixprefix เช่น md: หรือ lg: สำหรับ breakpoint
State prefixprefix เช่น hover:, focus:, disabled:
globals.cssCSS กลางของ Next.js app
PostCSSเครื่องมือประมวลผล CSS ที่ Tailwind ใช้ร่วมกับ Next.js

อ้างอิงสำหรับผู้สอน