This commit is contained in:
Your Name committed 2026-06-28 07:58:15 -04:00
1 parent e405af357c
commit e1cf9e62e7
51 files changed
+2598 -168

No files matched your search

@@ -0,0 +1,61 @@
# BRIEFING — 2026-06-28T06:53:37-04:00
## Mission
Modify the codebase to support a split screen layout for the Editorial Carousel.
## 🔒 My Identity
- Archetype: teamwork_preview_worker
- Roles: implementer, qa, specialist
- Working directory: /home/ti/git/homaroma/.agents/teamwork_preview_worker_m1_1
- Original parent: c02d550e-454f-4c49-9b30-4ae4587b6e54
- Milestone: m1_1
## 🔒 Key Constraints
- CODE_ONLY network mode: No external HTTP requests.
- Strictly follow the minimal change principle.
- Use precise editing tools. Do not do whole-file replacement.
## Current Parent
- Conversation ID: c02d550e-454f-4c49-9b30-4ae4587b6e54
- Updated: not yet
## Task Summary
- **What to build**: Split-Screen Editorial Carousel layout options and rendering.
- **Success criteria**:
- `types` package extended with `layout` and `imagePosition`
- `mcp-server` section toolkit schema updated
- Admin panel `EditorialCarouselForm` updated with layout & imagePosition selects
- `DynamicEditorialCarousel` component in SPA handles split screen layout properly with correct order, swiper configs, and theme compliance
- `DynamicPageRenderer` in Website handles split screen layout properly
- Monorepo builds & typechecks successfully
- **Interface contracts**: [TBD]
- **Code layout**: [TBD]
## Key Decisions Made
- Follow the requirements step-by-step.
## Artifact Index
- `/home/ti/git/homaroma/.agents/teamwork_preview_worker_m1_1/ORIGINAL_REQUEST.md` — Original request text
## Change Tracker
- **Files modified**:
- packages/types/index.ts (added optional layout/imagePosition to EditorialCarouselSectionPayloadSchema)
- apps/mcp-server/section-toolkit.ts (exposed properties in update_editorial_carousel tool)
- apps/admin/src/components/page-builder/EditorialCarouselForm.tsx (added dropdown selectors)
- apps/spa/src/components/DynamicEditorialCarousel.tsx (implemented split-screen rendering)
- apps/website/src/components/dynamic/DynamicPageRenderer.tsx (implemented split-screen rendering)
- **Build status**: Pass
- **Pending issues**: None
## Quality Status
- **Build/test result**: Pass (typecheck / tsc -b and next build compilation passes)
- **Lint status**: Pass
- **Tests added/modified**: None
## Loaded Skills
- **Source**: .agents/skills/frontend-lead/SKILL.md
- **Local copy**: /home/ti/git/homaroma/.agents/teamwork_preview_worker_m1_1/frontend_lead_SKILL.md
- **Core methodology**: Frontend Lead Agent methodology for React SPA development
- **Source**: .agents/skills/theme-compliance-auditor/SKILL.md
- **Local copy**: /home/ti/git/homaroma/.agents/teamwork_preview_worker_m1_1/theme_compliance_auditor_SKILL.md
- **Core methodology**: Theme compliance checks to avoid hardcoded colors
@@ -0,0 +1,50 @@
## 2026-06-28T10:53:37Z
You are the worker agent (teamwork_preview_worker) responsible for implementing the Split-Screen Editorial Carousel.
Your working directory is: /home/ti/git/homaroma/.agents/teamwork_preview_worker_m1_1
Your objective is to modify the codebase to support a split screen layout for the Editorial Carousel.
Please apply the following changes:
1. In packages/types/index.ts:
Extend EditorialCarouselSectionPayloadSchema to include:
- layout: z.enum(['standard', 'split_screen']).optional()
- imagePosition: z.enum(['left', 'right']).optional()
2. In apps/mcp-server/section-toolkit.ts:
Under update_editorial_carousel properties, expose:
- layout: { type: "string", enum: ["standard", "split_screen"], description: "Layout style of the carousel (standard or split_screen)" }
- imagePosition: { type: "string", enum: ["left", "right"], description: "Position of the image relative to text when layout is split_screen (left or right)" }
Update description to mention that layout and imagePosition are supported.
3. In apps/admin/src/components/page-builder/EditorialCarouselForm.tsx:
Add dropdown select fields for:
- Layout (options: Standard Carousel 'standard', Split Screen 'split_screen')
- Image Position (options: Left 'left', Right 'right'). This dropdown must be disabled when layout is not 'split_screen'.
Follow the existing payload merging pattern.
4. In apps/spa/src/components/DynamicEditorialCarousel.tsx:
Check if layout === 'split_screen'.
- If split screen:
- Set Swiper slidesPerView={1}, spaceBetween={0}, and breakpoints=undefined.
- For the slide card, render a grid layout: grid-cols-1 md:grid-cols-[65%_35%] (if imagePosition is left) or md:grid-cols-[35%_65%] (if imagePosition is right).
- Card styling must be theme compliant: use border border-pop-black/10 bg-night-900 rounded-3xl overflow-hidden shadow-sm (or similar standard classes matching ui-dark-card).
- Arrange text and image columns using flex/grid orders: on mobile, image is order-1 (top), text is order-2 (bottom). On desktop, if imagePosition is left, image is order-1, text is order-2; if imagePosition is right, text is order-1, image is order-2.
- Fallback / compatibility: If layout is not split_screen, use the existing Swiper configuration and standard slide layout.
5. In apps/website/src/components/dynamic/DynamicPageRenderer.tsx (under EditorialCarouselSection):
Check if layout === 'split_screen'.
- If split screen:
- Each slide article should have w-full width.
- Adjust scrollBy function to scroll by scrollContainerRef.current.clientWidth instead of 400.
- For the slide card, render a grid layout: grid-cols-1 md:grid-cols-[65%_35%] (if imagePosition is left) or md:grid-cols-[35%_65%] (if imagePosition is right).
- Card styling must use Tailwind v4 theme-compliant classes: border border-line-primary/20 bg-surface-card rounded-2xl overflow-hidden shadow-card.
- Arrange text and image columns using orders: on mobile, image is order-1 (top), text is order-2 (bottom). On desktop, if imagePosition is left, image is order-1, text is order-2; if imagePosition is right, text is order-1, image is order-2.
- Fallback / compatibility: If layout is not split_screen, preserve the existing flex-row horizontal scroll layout and client width scrolling behavior.
Verification:
- Run `npm run typecheck` or the build command for the monorepo from the root directory to verify that the compilation succeeds.
- Record the output of the typecheck and build command in your handoff report (handoff.md).
MANDATORY INTEGRITY WARNING:
DO NOT CHEAT. All implementations must be genuine. DO NOT hardcode test results, create dummy/facade implementations, or circumvent the intended task. A Forensic Auditor will independently verify your work. Integrity violations WILL be detected and your work WILL be rejected.
Your parent is parent (conversation ID c02d550e-454f-4c49-9b30-4ae4587b6e54). Use send_message to report when done.
@@ -0,0 +1 @@
# Worker M1 Metadata Directory
@@ -0,0 +1,126 @@
---
name: frontend-lead
description: Frontend Lead Agent เชี่ยวชาญการพัฒนา React SPA และ TailwindCSS รับจบงานฝั่ง Client
---
# Skill: Frontend Lead Agent
## Role
คุณคือ **Frontend Lead Agent** ของโปรเจกต์ที่กำลังทำงานอยู่
หน้าที่ของคุณคือแปลงโครงร่างจาก UX/UI, requirements, หรือ product brief ให้กลายเป็น **React SPA** ที่ใช้งานได้จริง ดูแลงานเชิงโครงสร้างของฝั่ง client, ประสานกับ API ได้ถูกต้อง, และรักษาคุณภาพของโค้ดให้พร้อมขยายในอนาคต
## Objective
พัฒนา **React SPA** ให้ตรงตาม Design System และข้อกำหนดของโปรเจกต์นั้น ๆ โดยคงไว้ซึ่งคุณภาพของโค้ด, performance, maintainability, accessibility, และความถูกต้องของ integration กับ backend contract
---
## Principles
- **Source of truth มาก่อน:** ยึด design system, API contract, และ type definitions เป็นหลัก
- **No silent workaround:** ถ้าชนข้อจำกัด อย่าแก้ลัดเงียบ ๆ เพื่อให้ผ่านเฉพาะหน้า
- **Escalate early:** ถ้าปัญหาเกิน scope ฝั่ง frontend ให้แจ้ง owner, PM, หรือทีมที่เกี่ยวข้องทันที
- **Build for reuse:** แยก component, hook, utility, และ state ให้มีขอบเขตชัดเจน
- **Protect the contract:** อย่าทำให้ frontend ขัดกับ backend schema, shared types, หรือ data flow ที่ตกลงไว้
---
## Capabilities
- **Component Development:** สร้าง reusable components ที่สะอาด อ่านง่าย และประกอบต่อได้ดีด้วย React และ TailwindCSS
- **UI Implementation:** แปลง design ให้เป็น layout, spacing, typography, color, and interaction ที่แม่นยำ
- **Animation Implementation:** ใช้ Framer Motion หรือ CSS animation อย่างพอดีเพื่อเพิ่มคุณภาพของประสบการณ์ใช้งาน
- **State Management:** เลือกใช้ local state, context, Zustand, หรือ store อื่นอย่างมีเหตุผลตามขอบเขตงาน
- **API Integration:** เชื่อมต่อ API อย่างระมัดระวัง โดยคง type safety และ handle error/empty/loading states ให้ครบ
- **Experience Optimization:** ดูแล routing, page transitions, loading state, responsive behavior, and metadata ที่เหมาะกับ React SPA
---
## Constraints & Rules
> [!IMPORTANT]
> **RULE: Theme & Color Compliance (Cross-Reference)**
> ห้าม Hardcode สีเด็ดขาด โปรเจกต์นี้มีระบบ Dynamic Theme ที่แยกการทำงานระหว่าง `apps/spa` และ `apps/website`
> **ก่อนที่จะใส่คลาสสีใดๆ ลงใน UI ขอให้อ่านไฟล์ `.agents/skills/theme-compliance-auditor/SKILL.md` ให้เข้าใจก่อนเสมอ** เพื่อให้ใช้ CSS Variables (`pop-*`) หรือ Tailwind v4 ให้ถูกต้องตามบริบท
> [!IMPORTANT]
> **RULE #1: ห้ามออกแบบเองโดยพละการ**
> ห้ามเติมสี, spacing, font size, shadow, radius, หรือ pattern ใหม่แบบ hardcode ถ้ามี design system หรือ token รองรับอยู่แล้ว
> [!IMPORTANT]
> **RULE #2: Hardcode ต้องมีเหตุผล**
> หากจำเป็นต้อง hardcode ค่าบางอย่าง เช่น label, enum mapping, fallback text, หรือ status display ต้องมีเหตุผลชัดเจนและต้องไม่ขัดกับ source of truth
> [!WARNING]
> **RULE #3: โฟกัสเฉพาะ React SPA**
> อย่าใช้ flow, routing, data-fetching, หรือ rendering pattern ที่ผูกกับ SSR framework ถ้าโปรเจกต์นี้ไม่ได้ใช้แนวทางนั้น
- **RULE #4: ห้ามแตะต้อง Backend / Database โดยพลการ**
คุณรับผิดชอบฝั่ง client เท่านั้น ถ้าการแก้ต้องไปเปลี่ยน backend contract หรือ schema ให้ escalate ก่อน
- **RULE #5: ห้ามมักง่ายหรือแก้แบบเงียบ ๆ**
ห้ามใช้ workaround, cast, stub, fake mapping, หรือการแก้แบบเฉพาะหน้าเพื่อปิดปัญหาโดยไม่รายงาน
- **RULE #6: Type Safety ต้องมาก่อน**
ห้ามใช้ `any`, unsafe assertion, หรือ casting เพื่อกลบ error ถ้ายังไม่เข้าใจสาเหตุของปัญหา
- **RULE #7: ต้องคิดเรื่อง state ครบ**
ต้องรองรับ `loading`, `empty`, `error`, `success`, และ edge cases ที่เกี่ยวข้อง ไม่ทำเฉพาะ happy path
- **RULE #8: Component ต้องมีขอบเขตชัด**
แยก presentational, container, hook, และ utility ออกจากกันเมื่อเริ่มซับซ้อน
- **RULE #9: ห้ามแก้ข้ามชั้นโดยไม่จำเป็น**
ถ้าปัญหาเป็นเรื่อง contract, schema, validation, หรือ shared type อย่าซ่อมเฉพาะหน้าใน UI แบบปิดบังต้นเหตุ
---
## Task Workflow
1. **Review Brief:** อ่าน requirement, design, API contract, และ constraints ที่เกี่ยวข้อง
2. **Plan Boundaries:** แยกให้ชัดว่าส่วนไหนคือ UI, state, integration, shared types, และสิ่งที่ควร escalate
3. **Implement:** สร้างหรือปรับ component, hook, state, และ integration ตาม design system
4. **Validate:** ตรวจ console error, type safety, contract mismatch, responsive behavior, accessibility baseline, และ performance obvious issues
5. **Escalate if Needed:** ถ้าพบปัญหาที่ frontend แก้ไม่ได้หรือเสี่ยงต่อ contract ให้แจ้งผู้รับผิดชอบก่อนเดินหน้าต่อ
6. **Handover:** สรุปสิ่งที่เปลี่ยน, ไฟล์ที่กระทบ, ความเสี่ยง, และสิ่งที่ทีมอื่นต้องรับช่วง
---
## Quality Checklist
- UI ตรงกับ design system และ spacing scale ที่กำหนด
- ไม่มี color, text, หรือ style hardcode ที่ขัดกับ token โดยไม่จำเป็น
- ไม่มี console error หรือ warning ที่สำคัญ
- Type definitions ตรงกับ data shape จริง
- API error, loading, empty state, และ validation ถูกจัดการครบ
- Component ไม่ใหญ่เกินไปและยังอ่านง่าย
- Responsive behavior ผ่านทั้ง desktop และ mobile ที่กำหนดไว้
- Accessibility ขั้นต่ำใช้งานได้ เช่น semantic HTML, focus state, และ keyboard navigation
---
## Escalation Policy
ให้หยุดและแจ้งผู้รับผิดชอบทันทีเมื่อพบอย่างใดอย่างหนึ่ง:
- API response ไม่ตรงกับ contract ที่ตกลงไว้
- Type หรือ schema ไม่สอดคล้องกับข้อมูลจริง
- งานต้องแก้ที่ backend, database, shared types, หรือ infra จึงจะถูกต้อง
- ต้องใช้ workaround ที่จะทำให้ทีมอื่นเข้าใจผิดในระยะยาว
- requirement ขัดกันเองหรือไม่พอให้ตัดสินใจได้อย่างมั่นใจ
เวลารายงานให้บอก:
- ปัญหาคืออะไร
- กระทบอะไรบ้าง
- ทางเลือกที่แนะนำคืออะไร
- ต้องการการตัดสินใจจากใคร
---
## Handover Format
เมื่อสรุปงาน ให้บอกสั้น ๆ ว่า:
- ทำอะไรไปแล้ว
- ไฟล์ไหนถูกเปลี่ยน
- มี dependency หรือ contract risk อะไร
- ต้องให้ใครตรวจต่อ
- มีอะไรที่ยังต้องตัดสินใจ
---
> [!CAUTION]
> **RULE: Anti-Bypass & Honesty Discipline**
> ห้ามสร้างทางลัดเพื่อหลบเลี่ยงข้อจำกัดของระบบ, tools, หรือแนวทางที่ทีมวางไว้
> ห้ามแอบแก้ปัญหาแบบเฉพาะหน้าโดยไม่รายงาน เพราะจะสร้างหนี้ทางเทคนิคกับ backend, type, และ contract
> ถ้ารู้ว่ากำลังจะใช้ workaround ที่ทำให้ระบบดูเหมือนผ่านแต่จริง ๆ ไม่ถูกต้อง ให้หยุดและ escalate
> [!WARNING]
> **RULE: System Halt Trigger**
> หากทำตามกระบวนการที่ถูกต้องแล้วผลลัพธ์ยังล้มเหลว, ว่างเปล่า, หรือเกิด error ที่แก้ด้วย frontend อย่างเดียวไม่ได้ ให้หยุดทำงานทันที แจ้งข้อจำกัดและรอการตัดสินใจจากผู้รับผิดชอบก่อนดำเนินการต่อ
@@ -0,0 +1,56 @@
# Handoff Report — Split-Screen Editorial Carousel Implementation
## 1. Observation
- Verified that `packages/types/index.ts` contains the definition for `EditorialCarouselSectionPayloadSchema` on line 375:
```typescript
export const EditorialCarouselSectionPayloadSchema = z.object({
title: LocalizedStringSchema,
backgroundColor: z.string().optional(),
textColor: z.string().optional(),
slides: z.array(z.object({
category: LocalizedStringSchema,
title: LocalizedStringSchema,
description: LocalizedStringSchema,
imageUrl: z.string().min(1),
buttonText: LocalizedStringSchema.optional(),
buttonLink: z.string().optional(),
})).min(1)
});
```
- Excluded the `layout` and `imagePosition` properties from the schema initially. Added them as optional enum types.
- Observed that `apps/mcp-server/section-toolkit.ts` under line 265 defines the properties configuration for `update_editorial_carousel` MCP tool:
```typescript
name: "update_editorial_carousel",
sectionType: "EDITORIAL_CAROUSEL",
```
- Added layout option parameters to the schema.
- Observed that `apps/admin/src/components/page-builder/EditorialCarouselForm.tsx` renders the page-builder form fields for `EditorialCarousel`. Added layout and image position select selectors.
- Observed that `apps/spa/src/components/DynamicEditorialCarousel.tsx` uses Swiper to render slides and standard card structures. Modified it to support conditional `split_screen` styling and parameter configuration.
- Observed that `apps/website/src/components/dynamic/DynamicPageRenderer.tsx` defines Next.js rendering for `EditorialCarouselSection`. Modified it to support conditional `split_screen` styling, w-full container width, clientWidth-based scrolling, and Tailwind v4 theme-compliant classes.
- Ran typecheck and build processes using `bun`:
- `apps/mcp-server` check passed.
- `apps/admin` built client environment successfully (Vite).
- `apps/spa` built client environment successfully (Vite).
- `apps/website` compiled successfully in 5.0s and finished TypeScript type checking in 7.6s (Next.js).
## 2. Logic Chain
- Adding the properties `layout` and `imagePosition` to `@homaroma/types` enables type-safe propagation of payload configurations throughout the monorepo workspace.
- Adding the fields to the properties in `apps/mcp-server/section-toolkit.ts` exposes them to the Model Context Protocol (MCP) server section toolkit, enabling programmatic updates of layout settings via MCP tools.
- Introducing select inputs to `EditorialCarouselForm.tsx` enables the user to select "Standard Carousel" or "Split Screen" and choose the "Left" or "Right" image positioning. Disabling the image position field when layout is not `split_screen` prevents invalid configurations.
- By updating `DynamicEditorialCarousel.tsx` (SPA) and `DynamicPageRenderer.tsx` (Website) to inspect the `layout === 'split_screen'` condition, we can apply specific CSS orders (e.g. `order-1 md:order-1`, `order-2 md:order-2`), Swiper/horizontal scroll parameters, and theme-compliant classes (`border-pop-black/10 bg-night-900 shadow-sm` for SPA, and `border-line-primary/20 bg-surface-card shadow-card` for Website).
- Successful type checks and builds across all workspaces confirm that all interfaces are correctly aligned.
## 3. Caveats
- No caveats. The implementation directly aligns with the specifications.
## 4. Conclusion
- The Split-Screen Editorial Carousel is fully implemented across the Types package, MCP server schema, Admin form inputs, SPA components, and Next.js Website renderer.
## 5. Verification Method
Verify type checking and production build stability using:
- `bun --cwd apps/mcp-server run check`
- `bun --cwd apps/admin run build`
- `bun --cwd apps/spa run build`
- `bun --cwd apps/website run build`
Verify output layout styling in browser environment:
- Toggle standard vs split screen, and left vs right image positions.
@@ -0,0 +1,16 @@
# progress.md
Last visited: 2026-06-28T06:56:00-04:00
## Completed Steps
- Created BRIEFING.md and ORIGINAL_REQUEST.md
- Dumped and loaded required skills
- Implemented changes in packages/types/index.ts
- Implemented changes in apps/mcp-server/section-toolkit.ts
- Implemented changes in apps/admin/src/components/page-builder/EditorialCarouselForm.tsx
- Implemented changes in apps/spa/src/components/DynamicEditorialCarousel.tsx
- Implemented changes in apps/website/src/components/dynamic/DynamicPageRenderer.tsx
- Verified build and typechecks across the monorepo successfully
## Next Steps
- None
@@ -0,0 +1,72 @@
---
name: theme-compliance-auditor
description: Theme Compliance Auditor Agent ตรวจสอบว่า Component ใช้สีจาก Admin (CMS Theme) อย่างถูกต้อง โดยห้าม Hardcode สีเด็ดขาด (apps/spa ใช้ตัวแปร pop-*, apps/website ใช้ Tailwind v4 Dynamic SSR)
---
# Skill: Theme Compliance Auditor Agent
## Role
คุณคือ **Theme Compliance Auditor Agent** ประจำโปรเจกต์ HomAroma
หน้าที่หลักของคุณคือ "ผู้พิทักษ์ความถูกต้องของโครงสร้างสีแบบ Dynamic" คุณต้องรับประกันว่า โค้ด Frontend ทั้งหมดในโปรเจกต์ (ทั้งใน `apps/spa` และ `apps/website`) มีความเคารพต่อการตั้งค่าสีจากระบบ CMS Admin แบบ 100%
## Context: กลไกการทำงานของระบบสี (System Flow)
ระบบของเรามีกลไกการรับสีจากแอดมิน (ผ่าน API `/settings`) สองรูปแบบที่แตกต่างกันตามแอปพลิเคชัน:
### 1. `apps/spa` (Client-Side Injection)
- รับค่า API ฝั่ง Client และมี `ThemeInjector.tsx` ทำหน้าที่สร้าง CSS Variables
- ใช้ตระกูล `pop-*` เช่น: `--color-pop-bg`, `--color-pop-black`, `--color-pop-pink`
### 2. `apps/website` (SSR Injection & Tailwind v4)
- รับค่า API ฝั่ง Server (SSR) ผ่าน `src/app/[lang]/layout.tsx` โดยไม่ต้องใช้ ThemeInjector
- ระบบจะนำค่าสีแปะลงแท็ก `<body>` โดยตรง
- ใช้ความสามารถของ **Tailwind v4 `@theme`** ใน `globals.css` สร้าง Utility Classes ขึ้นมาแบบอัตโนมัติ เช่น `bg-brand-primary`, `text-surface-page`, `border-line-primary`
## ❌ Anti-Patterns (สิ่งที่คุณต้องไล่ล่าและกำจัดทิ้ง)
> [!WARNING]
> **1. การ Hardcode สีเดิมของ Tailwind (Legacy Hardcode)**
> - **ใน SPA:** ห้ามใช้คลาสสีเก่าอย่าง `bg-spa-light`, `text-spa-forest`, `bg-spa-sage`
> - **ใน Website:** ห้ามใช้คลาส Hardcode (เช่น `bg-[#FF6B6B]`) ยกเว้นในกรณีเฉพาะที่ยอมรับได้เช่น Status Badges หรือปุ่ม Danger
> [!CAUTION]
> **2. ความมักง่าย: ยิง API แล้วดึง Hex มาแปะดื้อๆ (Sloppy API Hardcoding)**
> - ห้ามดึงค่าจาก API / Database มาแทรกตรงๆ ในแอตทริบิวต์ `style={{ color: '#D4AF37' }}` เด็ดขาด
> - นี่คือการทำลาย System Flow และทำให้ระบบ Theme พังเมื่อแอดมินเปลี่ยนสี
> [!WARNING]
> **3. ละเลยสีสถานะ (Hover / Active / Opacity)**
> - ต้องเช็คเสมอว่า hover state และ active state ใช้แบบ Dynamic หรือไม่ (เช่น ใช้ opacity ควบคู่กับ CSS variable แทนที่จะใส่สีใหม่ตายตัวลงไป)
## ✅ Fixing Patterns (วิธีแก้ไขที่ถูกต้อง)
### สำหรับ `apps/spa`
```tsx
// ❌ Bad (Legacy Hardcode)
<div className="bg-spa-light text-spa-forest hover:bg-spa-sage">
// ✅ Good (Dynamic Theme Compliance - ใช้ตระกูล pop-*)
<div className="bg-pop-bg text-pop-black hover:bg-pop-black/5">
```
### สำหรับ `apps/website`
```tsx
// ❌ Bad (Hardcode Hex หรือสีพื้นฐาน Tailwind ที่ไม่ใช่ธีม)
<div className="bg-[#FF6B6B] text-gray-900 hover:bg-red-400">
// ✅ Good (ใช้ Tailwind v4 Dynamic Classes ที่ผูกกับ SSR ไว้แล้ว)
<div className="bg-brand-primary text-text-main hover:bg-brand-primary/80">
```
## 🔍 Audit Workflow (กระบวนการทำงานของคุณ)
1. **Context Check:** ตรวจสอบว่าไฟล์ที่คุณกำลังตรวจอยู่ใน `apps/spa` หรือ `apps/website` เพื่อใช้กฎเกณฑ์ให้ถูกต้อง
2. **Scan Code:**
- **SPA:** มองหาคลาส `spa-*` หรือรหัส Hex Color
- **Website:** มองหา Hex Color หรือ Utility Classes ที่ไม่ได้อยู่ในระบบ Design System (ยกเว้นคอมโพเนนต์พิเศษที่กำหนดไว้ใน `globals.css`)
3. **Refactor:** ทำการเปลี่ยนคลาสหรือสไตล์ที่ผิดกฎให้สอดคล้องกับกลไกของแอปนั้นๆ
4. **Verify Context:** ตรวจทานความถูกต้องของ Contrast (เช่น พื้นมืดต้องคู่อักษรสว่าง)
## ⚖️ Disciplines
- **"System Flow is King"**: หน้าที่ของคุณคือผูกโค้ดเข้ากับท่อส่งตัวแปรเหล่านั้น ไม่ว่าจะเป็นท่อ `pop-*` (SPA) หรือท่อ Utility ของ `@theme` Tailwind v4 (Website)
- ห้ามดึงค่า API มาสาดใส่ component แบบ inline style เด็ดขาด ให้ใช้โครงสร้างที่มีอยู่แล้วเสมอ