CODEMOTIVE

Design System Guidelines

คู่มืออ้างอิงสำหรับนักออกแบบและนักพัฒนาเว็บไซต์ Codemotive — ครอบคลุม สี, ตัวอักษร, Spacing, Components, Layout และแนวทางการเขียนโค้ด เพื่อให้งานทุกชิ้นมีความสอดคล้องกัน

ColorsTypographySpacingButtonsLayoutCode Style

Colors

Codemotive ใช้ระบบสีที่เรียบง่าย — Black สำหรับ primary, Orange (#ff7a52) สำหรับ accent, และ Gray scale สำหรับ text และ surface ต่างๆ

#141414

Brand Black

Primary text, backgrounds, CTAs

#ff7a52

Brand Orange

Accent, hover states, highlights

#ffffff

White

Backgrounds, cards, reversed text

#f9fafb

Gray 50

Section backgrounds, zebra rows

#f3f4f6

Gray 100

Code backgrounds, subtle fills

#9ca3af

Gray 400

Placeholder, secondary icons

#6b7280

Gray 500

Secondary text, captions

#4b5563

Gray 600

Body text, descriptions

#141414

Gray 900 (Dark BG)

Footer background, dark sections

Usage Rules

  • ใช้ #141414 เป็นสีหลักสำหรับ CTA buttons, headings, และ footer background
  • ใช้ #ff7a52 สำหรับ hover state ของ links/buttons, accents, และ highlight elements
  • ใช้ gray-600 (#4b5563) สำหรับ body text ทั่วไป
  • ใช้ gray-400–500 สำหรับ placeholder, captions, และ secondary info
  • ห้ามใช้สีอื่นนอกจากในตารางนี้โดยไม่มีเหตุผล — ให้ยึด design token เสมอ

Typography

ใช้ 2 ฟอนต์หลัก: IBM Plex Sans Thai สำหรับ UI ทั้งหมด, และ Bai Jamjureeสำหรับ blog content body (เฉพาะใน .blog-content class)

Primary UI Font

IBM Plex Sans Thai

สวัสดีครับ — ข้อความภาษาไทย — ABCDEFGHIJKLMNOPQRSTUVWXYZ — 0123456789

weights: 300, 400, 500, 600, 700

Blog Body Font

Bai Jamjuree

สวัสดีครับ — ข้อความในบทความ — The quick brown fox

weight: 400 only — class: .blog-content

LabelSizeWeightLine HeightUsage
Display / H1text-5xl / 3remfont-bold (700)leading-[1.1]Page hero headings
H2 Sectiontext-4xl / 2.25remfont-bold (700)leading-tightSection headings
H3 Cardtext-2xl / 1.5remfont-semibold (600)leading-snugCard titles, sub-sections
H4 Labeltext-xl / 1.25remfont-semibold (600)leading-normalSub-labels, table headers
Body Largetext-lg / 1.125remfont-normal (400)leading-relaxed (1.625)Lead paragraphs, descriptions
Body Basetext-base / 1remfont-normal (400)leading-relaxedGeneral body copy
Small / Captiontext-sm / 0.875remfont-medium (500)leading-normalLabels, badges, captions
XSmall / Metatext-xs / 0.75remfont-medium (500)leading-normalTags, timestamps, metadata

Live Examples

Display Heading — รับทำเว็บไซต์

Section Heading — บริการของเรา

Card Title — Landing Page

Body Large — เว็บไซต์คือสินทรัพย์บนโลกออนไลน์ที่เราเป็นเจ้าของ 100%

Caption / Label — เริ่มต้นเพียง 12,000 บาท

Spacing

ใช้ Tailwind spacing scale (1 unit = 4px) เป็นมาตรฐาน — ตาราง นี้แสดง tokens ที่ใช้บ่อยในโปรเจกต์

Tokenpx ValueUsage
416pxMinimum internal padding, gaps between tight elements
624pxDefault horizontal padding (mobile), card inner padding
832pxSection vertical spacing, button padding
1040pxComponent gap, form spacing
1248pxLarge gaps between related sections
1664pxDefault desktop page section padding
2496pxHero padding top/bottom (mobile)
lg:px-1248pxDesktop horizontal container padding

Container Pattern

<section className="max-w-360 mx-auto px-6 lg:px-12 py-16 lg:py-24">
  {/* page content */}
</section>

max-width: 1440px, horizontal padding: 24px mobile / 48px desktop, vertical padding: 64px mobile / 96px desktop

Buttons

Buttons ใช้ rounded-xl (12px) เป็นหลัก, padding px-8 py-4, font-semibold, และ transition-colors สำหรับ hover

Primary

className="bg-[#141414] text-white px-8 py-4 rounded-xl font-semibold hover:bg-[#ff7a52] transition-colors"

Primary Orange

className="bg-[#ff7a52] text-white px-8 py-4 rounded-xl font-semibold hover:bg-[#e5633d] transition-colors"

Outline Dark

className="border-2 border-[#141414] text-[#141414] px-8 py-4 rounded-xl font-semibold hover:bg-[#141414] hover:text-white transition-colors"

Ghost / Text Link

className="text-[#141414] font-semibold flex items-center gap-2 hover:text-[#ff7a52] transition-colors"

Button with Icon (common pattern)

<button className="bg-[#141414] text-white px-8 py-4 rounded-xl font-semibold hover:bg-[#ff7a52] transition-colors flex items-center gap-2">
  ติดต่อเรา
  <ArrowRight size={18} />
</button>

Border Radius

ใช้ rounded corners แบบ generous เพื่อให้ดูทันสมัยและ friendly

rounded-lg

8px

Small cards, badges, inputs

rounded-xl

12px

Buttons, medium cards

rounded-2xl

16px

Large cards, panels

rounded-3xl

24px

Image thumbnails, hero images

rounded-full

9999px

Avatars, pill badges

Shadows

ใช้ Tailwind default shadow scale — ไม่มี custom shadow tokens

shadow-sm

Subtle card lift

shadow-md

Default card shadow, button shadow

shadow-lg

Modals, dropdowns, floating panels

shadow-xl

Hero elements, large popups

Layout & Grid

Codemotive ใช้ CSS Grid ผ่าน Tailwind — max-width container คือ 1440px เสมอ

Standard Page Container

ใช้กับทุก section ของหน้า

<section className="max-w-360 mx-auto px-6 lg:px-12 py-16 lg:py-24">
  {/* content */}
</section>

2-Column Hero Grid

Hero section pattern (text + image/visual)

<section className="max-w-360 mx-auto px-6 lg:px-12 py-16 lg:py-24
  grid lg:grid-cols-2 gap-16 items-center">
  <div>{/* left: text content */}</div>
  <div>{/* right: visual */}</div>
</section>

Card Grid (3 columns)

Blog cards, portfolio cards, service cards

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
  {items.map((item) => (
    <Card key={item.id} {...item} />
  ))}
</div>

4-Column Footer Grid

Footer links layout

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-10">
  {/* columns */}
</div>

12-Column Asymmetric Grid

About page: image (5 cols) + content (7 cols)

<div className="grid lg:grid-cols-12 gap-12 lg:gap-16 items-start">
  <div className="lg:col-span-5">{/* image */}</div>
  <div className="lg:col-span-7">{/* content */}</div>
</div>

Icons

ใช้ lucide-react เป็นไลบรารีไอคอนหลัก — import เฉพาะที่ใช้งาน (tree-shakable)

Import Pattern

import { ArrowRight, Phone, Mail, Star, Check } from "lucide-react";

// Usage
<ArrowRight size={18} />          // default stroke
<Star size={20} className="text-[#ff7a52]" />  // colored
<Check size={16} strokeWidth={2.5} />          // custom stroke weight

Common Size Guide

SizeUsage
size={14}Inside badges, tags, small labels
size={16}Inline with small text, metadata
size={18}Default — buttons, list items
size={20}Card headers, section labels
size={24}Feature icons, standalone icons
size={32–48}Hero icons, large decorative icons

Code Style

TypeScript strict mode — functional components — server components by default

Component Template

import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "Page Title — Codemotive",
  description: "...",
};

export default function ExamplePage() {
  return (
    <div className="animate-fade-in">
      <section className="max-w-360 mx-auto px-6 lg:px-12 py-16 lg:py-24">
        {/* content */}
      </section>
    </div>
  );
}

Client Component

"use client"; // ← ต้องการเมื่อใช้ useState/useEffect/events

import { useState } from "react";

export default function InteractiveWidget() {
  const [isOpen, setIsOpen] = useState(false);
  // ...
}

Import Order

// 1. External packages
import type { Metadata } from "next";
import Link from "next/link";
import { ArrowRight } from "lucide-react";

// 2. Internal modules (@ alias)
import { services } from "@/data";
import { getMarkdownBlogPosts } from "@/lib/blog";

// 3. Components
import SectionBadge from "@/components/section-badge";
import CtaSection from "@/components/cta-section";

TypeScript Rules

// ✅ Good — explicit return type for exports
export const getService = (slug: string): Service | undefined =>
  services.find((s) => s.slug === slug);

// ✅ Good — unknown + narrow instead of any
const parseData = (raw: unknown): User => {
  if (!isUser(raw)) throw new Error("Invalid");
  return raw;
};

// ❌ Avoid
const bad = (data: any) => data.id; // no any!

Key Rules Summary

const by defaultlet only when reassignment needed, never var
Arrow functionsFor callbacks and anonymous functions
Template literalsOver string concatenation
Strict equality=== / !== always
Early returnsReduce nesting depth
No magic valuesExtract to named constants
Parameterized queriesNever concatenate user input into queries
Try/catch asyncAlways handle errors explicitly

Naming Conventions

แนวทางการตั้งชื่อ file, variable, type, และ constant ในโปรเจกต์

CategoryConventionExample
Files & Folderskebab-caseuser-profile.tsx, auth-service.ts
Variables & FunctionscamelCasegetUserData(), isLoading, blogPosts
Types & InterfacesPascalCaseBlogPost, ServiceItem, PortfolioCard
Constants (true constants)UPPER_SNAKE_CASEMAX_RESULTS, BASE_URL
Constants (derived values)camelCasedefaultTitle, primaryColor
Boolean variablesis/has/should/can prefixisLoading, hasPermission, canEdit
React ComponentsPascalCaseBlogCard, SectionBadge, NavBar
Component Fileskebab-caseblog-card.tsx, section-badge.tsx
Route Segmentskebab-case/services/landing-page-sale-page
CSS ClassesTailwind utilitiesNo custom class names unless in globals.css

Commit Message Format (Conventional Commits)

feat: add portfolio filter component
fix: resolve mobile nav overflow issue
chore: update dependencies
docs: add design system page
refactor: extract hero section into component
style: adjust spacing on services page
test: add unit tests for blog util functions

Animation

ใช้ animation เพียงรูปแบบเดียว — fade-in จาก globals.css — ครอบที่ root element ของแต่ละ page

/* globals.css */
@keyframes fade-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}
.animate-fade-in {
  animation: fade-in 0.5s ease-out;
}

/* Usage — always wrap page root div */
<div className="animate-fade-in">
  {/* page content */}
</div>

ห้ามเพิ่ม animation อื่นโดยไม่จำเป็น — transition-colors สำหรับ hover เท่านั้น

Section Badge

Component <SectionBadge> ใช้เป็น eyebrow label เหนือ heading ของ section

SECTION BADGE
// src/components/section-badge.tsx
<SectionBadge>เปลี่ยนคนแปลกหน้าในเว็บ ให้เป็นลูกค้าประจำของคุณ</SectionBadge>

// Renders:
<div className="flex items-center gap-2 text-sm font-bold tracking-wider uppercase text-gray-500 mb-6">
  <div className="w-1.5 h-1.5 bg-[#141414] rounded-full" />
  {children}
</div>

Images

แนวทางการใช้งานรูปภาพในโปรเจกต์

Aspect Ratios & Classes

Hero / Full-widthh-[400px] lg:h-[600px] object-coverAbout page profile image
Blog coveraspect-video object-coverBlog card thumbnails
Portfolio thumbaspect-video object-cover rounded-2xlPortfolio grid cards
Avatar / Logow-16 h-16 object-containSmall logos, DBD badge

Image Hosting

  • Static assets: /public/images/
  • Media (photos, cloudinary): res.cloudinary.com/cmtd/image/upload/...
  • Blog post images: ระบุใน frontmatter img: และ coverImg:

Codemotive Design System

เอกสารนี้เป็น Internal Reference

อัปเดตเมื่อเพิ่ม component หรือ pattern ใหม่เข้าโปรเจกต์ ทุกการเปลี่ยนแปลง design token ควรสะท้อนในหน้านี้ด้วยเสมอ