Scoped Styles
ใน React คุณ scope style ให้กับ component โดยตั้งชื่อไฟล์เป็น Button.module.css แล้ว import มา — bundler จะ rewrite ชื่อ class ให้เป็น hash ที่ unique ตอน build ใน Astro คุณเขียน CSS ธรรมดาใน <style> block ในไฟล์ .astro เดียวกัน compiler จะเพิ่ม data attribute hash ที่ unique ให้ทุก selector และทุก HTML element ใน component นั้น ให้ผลลัพธ์เดียวกันโดยไม่ต้องตั้งค่าใดๆ
เปรียบเทียบแบบ side-by-side
หัวข้อที่มีชื่อว่า “เปรียบเทียบแบบ side-by-side”// Button.tsx + Button.module.css// Button.module.css.button { background: #6366f1; color: #fff; border-radius: 6px; padding: .5rem 1rem; border: none; cursor: pointer; }.button:hover { background: #4f46e5; }
// Button.tsximport styles from './Button.module.css';export default function Button({ label }: { label: string }) { return <button className={styles.button}>{label}</button>;}---// Button.astroconst { label } = Astro.props;---<button class="button">{label}</button>
<style> .button { background: #6366f1; color: #fff; border-radius: 6px; padding: .5rem 1rem; border: none; cursor: pointer; } .button:hover { background: #4f46e5; }</style>สิ่งที่ Astro compiler สร้างขึ้นเบื้องหลัง:
<!-- compiled output — คุณไม่ต้องเขียนสิ่งนี้ด้วยตัวเอง --><button class="button astro-aBcD1234">Click me</button>
<style> .button.astro-aBcD1234 { background: #6366f1; ... }</style>hash suffix (astro-aBcD1234) ถูก inject อัตโนมัติ rule .button ใน component อื่นจะได้รับ hash ที่แตกต่างกัน ดังนั้น style จึงไม่ชนกัน
อะไรใส่ใน style block ได้บ้าง
หัวข้อที่มีชื่อว่า “อะไรใส่ใน style block ได้บ้าง”<style> block รับ CSS มาตรฐาน — custom properties, media queries, pseudo-selectors, nesting, @keyframes ทุกอย่างที่ browser target รองรับ ไม่มี CSS-in-JS API ให้เรียนรู้เพิ่มเติม
// Card.module.css.card { border: 1px solid #e2e8f0; border-radius: 8px; padding: 1rem; transition: transform .15s; }.card:hover { transform: translateY(-2px); }@media (max-width: 480px) { .card { padding: .75rem; } }
// Card.tsximport styles from './Card.module.css';export default function Card({ title }: { title: string }) { return <div className={styles.card}><h2>{title}</h2></div>;}---// Card.astroconst { title } = Astro.props;---<div class="card"><h2>{title}</h2></div>
<style> .card { border: 1px solid #e2e8f0; border-radius: 8px; padding: 1rem; transition: transform .15s; } .card:hover { transform: translateY(-2px); } @media (max-width: 480px) { .card { padding: .75rem; } }</style>ตัวอย่างที่รันได้
หัวข้อที่มีชื่อว่า “ตัวอย่างที่รันได้”---
const items = ["Dashboard", "Projects", "Settings"];
---
<html lang="en">
<head><meta charset="utf-8" /><title>Scoped styles</title></head>
<body>
<nav>
{items.map(item => (
<a href="#" class="nav-link">{item}</a>
))}
</nav>
<main>
<div class="card">
<h1>Scoped Styles Demo</h1>
<p>The <code>.card</code> and <code>.nav-link</code> styles live in this file and are scoped here by the Astro compiler — they cannot bleed into other components.</p>
</div>
</main>
</body>
</html>
<style>
body { font-family: sans-serif; margin: 0; background: #f8fafc; }
nav { background: #1e293b; padding: .75rem 1.5rem; display: flex; gap: 1rem; }
.nav-link { color: #94a3b8; text-decoration: none; font-size: .875rem; }
.nav-link:hover { color: #fff; }
main { padding: 2rem; }
.card { background: #fff; border: 1px solid #e2e8f0; border-radius: 10px; padding: 1.5rem; max-width: 480px; }
.card h1 { font-size: 1.25rem; margin: 0 0 .75rem; color: #1e293b; }
.card p { color: #64748b; line-height: 1.6; margin: 0; }
code { background: #f1f5f9; padding: .1em .35em; border-radius: 4px; font-size: .875em; }
</style>