ข้ามไปยังเนื้อหา
ธีม

เจาะลึก theme.json ของ WordPress: settings, styles และลำดับการทับซ้อน

ทำความเข้าใจว่าทำไม settings กับ styles ใน theme.json ทำหน้าที่คนละอย่าง และแก้ปัญหาแก้ block theme แล้วหน้าเว็บไม่เปลี่ยน ด้วยการไล่ตามลำดับการทับซ้อนของ WordPress

เผยแพร่แล้ว

theme.json คือไฟล์ตั้งค่าไฟล์เดียวที่รากของ block theme ซึ่งทำงานสองอย่างแยกกันชัดเจน คือ settings ตัดสินว่า block editor จะเปิดอะไรให้ผู้ใช้ ส่วน styles ตัดสินว่าหน้าเว็บจริงจะหน้าตาเป็นอย่างไรโดยค่าเริ่มต้น WordPress จะอ่านไฟล์นี้ รวมเข้ากับค่าเริ่มต้นของตัวเอง และกับทุกอย่างที่บันทึกไว้ใน Site Editor แล้วส่งผลลัพธ์ออกมาเป็น CSS custom property พร้อมสไตล์ชีตแบบอินไลน์ในทุกหน้า

การสับสนระหว่างสองหน้าที่นี้คือสาเหตุที่การแก้ theme.json ส่วนใหญ่ดูเหมือนไม่มีผลอะไรเลย การประกาศสีไว้ใต้ settings ไม่ได้ทาสีนั้นลงที่ไหนทั้งสิ้น มันแค่สร้างตัวแปรกับช่องสีในแถบข้างของหน้าแก้ไขเท่านั้น

คีย์ระดับบนสุด

ไฟล์แบบย่อที่สุดหน้าตาประมาณนี้

{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 3,
  "settings": {},
  "styles": {},
  "templateParts": [],
  "customTemplates": []
}
คีย์ทำหน้าที่อะไร
$schemaใช้สำหรับเติมคำอัตโนมัติและตรวจสอบในโปรแกรมแก้ไขโค้ดเท่านั้น WordPress ไม่สนใจคีย์นี้
versionบอก WordPress ว่าให้อ่านโครงสร้างสคีมาแบบไหน ไม่ใช่เวอร์ชันของธีมคุณ
settingsสร้างตัวแปร CSS และกำหนดว่าหน้าแก้ไขจะเปิดอะไรให้บ้าง
stylesลงสไตล์เริ่มต้นจริง ๆ
templatePartsประกาศไฟล์ส่วนย่อยใน parts/ และบอกว่าแต่ละไฟล์อยู่ในพื้นที่ใด
customTemplatesลงทะเบียนเทมเพลตหน้าใน templates/ ให้เลือกได้จากหน้าแก้ไขเนื้อหา

version ทำให้คนเข้าใจผิดบ่อย เพราะหน้าตาเหมือนเลขเวอร์ชันของธีม แต่มันไม่ใช่ มันคือตัวเลือกโหมดการอ่านไฟล์ และ WordPress รุ่นเก่าจะข้ามคีย์จากสคีมารูปแบบที่มันไม่รู้จักไปแบบเงียบ ๆ ถ้าคุณต้องรองรับเว็บรุ่นเก่า ให้เล็งไปที่เวอร์ชันที่เว็บเหล่านั้นเข้าใจ อย่าคิดเอาเองว่าคีย์ใหม่จะค่อย ๆ ลดระดับลงมาให้ ณ เวลาที่เขียนบทความนี้เวอร์ชัน 3 คือรุ่นปัจจุบัน ส่วนเวอร์ชัน 2 ยังใช้กันแพร่หลายและยังถูกอ่านได้อยู่

$schema ชี้ไปยัง URL ของสคีมาที่ WordPress โฮสต์ไว้ การตรึงไว้ที่ trunk จะให้คำแนะนำใหม่ล่าสุดในโปรแกรมแก้ไขโค้ด แต่ถ้าคุณตั้งใจอยู่กับสคีมารุ่นเก่า การระบุ URL แบบเจาะจงเวอร์ชันจะปลอดภัยกว่า

settings: สิ่งที่หน้าแก้ไขเปิดให้ใช้

ทุกอย่างใต้ settings จะกลายเป็น CSS custom property และในกรณีส่วนใหญ่จะกลายเป็นตัวควบคุมในหน้าแก้ไขด้วย แต่ไม่มีอะไรใต้ settings ที่ลงสไตล์ให้อะไรเลย

{
  "settings": {
    "color": {
      "palette": [
        { "slug": "brand", "color": "#1f4ed8", "name": "Brand" },
        { "slug": "ink", "color": "#111827", "name": "Ink" }
      ],
      "custom": false
    },
    "typography": {
      "fontSizes": [
        { "slug": "small", "size": "0.875rem", "name": "Small" },
        { "slug": "large", "size": "1.5rem", "name": "Large" }
      ]
    },
    "layout": {
      "contentSize": "680px",
      "wideSize": "1200px"
    }
  }
}

ผลลัพธ์คือตัวแปรที่ตั้งชื่อจากเส้นทางของพรีเซ็ตและ slug

--wp--preset--color--brand
--wp--preset--color--ink
--wp--preset--font-size--small
--wp--preset--font-size--large

นอกจากนี้ยังได้คลาสสำเร็จรูปอย่าง .has-brand-color และ .has-brand-background-color ซึ่งเป็นวิธีที่หน้าแก้ไขใช้ลงสีที่ผู้ใช้เลือกให้กับบล็อก

บรรทัด "custom": false คืออีกครึ่งหนึ่งของหน้าที่ settings นั่นคือการปิดตัวเลือก ใส่บรรทัดนี้แล้วตัวเลือกสีอิสระจะหายไป เหลือให้ผู้ใช้เฉพาะพาเลตต์ของคุณเท่านั้น รูปแบบเดียวกันใช้ได้ทั่วทั้งไฟล์ ทั้ง settings.typography.customFontSize, settings.spacing.customSpacingSize, settings.border และอื่น ๆ ไม่มีคีย์ไหนใต้ settings ที่ใส่ไว้เพื่อความสวยงาม แต่ละคีย์ไม่เพิ่มตัวแปร ก็เพิ่มตัวควบคุม หรือไม่ก็เอาตัวควบคุมออก

settings.custom คือถังเปล่าให้คุณใส่ตัวแปรของตัวเอง

{
  "settings": {
    "custom": {
      "lineHeight": { "body": 1.6 },
      "shadow": { "card": "0 1px 3px rgba(0,0,0,0.12)" }
    }
  }
}

สองอันนี้จะกลายเป็น --wp--custom--line-height--body และ --wp--custom--shadow--card สังเกตการแปลงชื่อให้ดี คีย์แบบ camelCase จะถูกแยกเป็น kebab-case และการซ้อนชั้นจะเชื่อมด้วยขีดกลางสองตัว การเขียนชื่อตัวแปรผิดคือสาเหตุยอดฮิตที่ค่าหนึ่งกลับไปใช้ค่าสำรองแบบไม่มีเสียงเตือน

คุณกำหนด settings เจาะจงรายบล็อกได้ ซึ่งเป็นวิธีเปิดตัวเลือกสีให้ปุ่มแต่ไม่เปิดให้ย่อหน้า

{
  "settings": {
    "blocks": {
      "core/paragraph": {
        "color": { "custom": false, "customGradient": false }
      }
    }
  }
}

styles: สิ่งที่แสดงผลจริง

styles คือที่ที่ค่าถูกนำไปใช้จริง โครงสร้างของมันสะท้อนโครงสร้างในหน้าแก้ไข คือมีระดับราก ตามด้วย elements แล้วก็ blocks

{
  "styles": {
    "color": {
      "background": "var(--wp--preset--color--base)",
      "text": "var(--wp--preset--color--ink)"
    },
    "typography": {
      "lineHeight": "var(--wp--custom--line-height--body)"
    },
    "elements": {
      "link": {
        "color": { "text": "var(--wp--preset--color--brand)" },
        ":hover": { "typography": { "textDecoration": "none" } }
      }
    },
    "blocks": {
      "core/quote": {
        "typography": { "fontStyle": "italic" },
        "spacing": { "padding": { "left": "var(--wp--preset--spacing--40)" } }
      }
    }
  }
}

styles ระดับรากจะไปลงที่ body ส่วน elements ครอบคลุมอิลิเมนต์ HTML พื้นฐานที่ไม่ใช่บล็อก เช่น ลิงก์ ปุ่ม หัวเรื่อง และคำบรรยายภาพ ส่วน blocks เจาะจงไปที่บล็อกชนิดใดชนิดหนึ่งตามชื่อ

ข้อจำกัดที่ต้องพูดกันตรง ๆ คือ styles เขียนแทน CSS ได้ไม่หมด คุณเขียน media query ของตัวเองไม่ได้ ใช้ซีเล็กเตอร์ซับซ้อนไม่ได้ ไม่มีแอนิเมชัน และรองรับสถานะ pseudo-class เพียงชุดเล็ก ๆ ตัวอักษรแบบลื่นไหลและการกำหนดขนาดตามเลย์เอาต์ช่วยแทนที่งานที่เคยต้องใช้เบรกพอยต์ได้มาก แต่ไม่ทั้งหมด block theme ที่วางขายจริงส่วนใหญ่จึงมาพร้อม theme.json บวกสไตล์ชีตขนาดย่อม และนั่นคือผลลัพธ์ปกติ ไม่ใช่ความล้มเหลว

กับดัก settings กับ styles

คำถามที่ฝ่ายสนับสนุนเจอบ่อยที่สุดคือประโยคทำนองว่า “ผมใส่สีแบรนด์ลงไปแล้ว แต่ไม่มีอะไรเปลี่ยนเลย”

ถูกต้องแล้วครับ การใส่สีลงใน settings.color.palette สร้าง --wp--preset--color--brand กับช่องสีขึ้นมา แต่ไม่ได้ลงสีให้อะไร ถ้าอยากให้มันเป็นสีลิงก์เริ่มต้น คุณต้องเขียนไว้ใต้ styles ด้วย

{
  "settings": {
    "color": {
      "palette": [{ "slug": "brand", "color": "#1f4ed8", "name": "Brand" }]
    }
  },
  "styles": {
    "elements": {
      "link": { "color": { "text": "var(--wp--preset--color--brand)" } }
    }
  }
}

อ่านมันแบบนี้ก็ได้ settings คือกล่องสีที่คุณยื่นให้ผู้ใช้ ส่วน styles คือสีที่คุณทาไว้ก่อนที่เขาจะมาถึง

ลำดับการทับซ้อน จากต่ำสุดไปสูงสุด

ส่วนนี้คือคำอธิบายว่าทำไมเว็บถึง “ไม่สนใจ” ไฟล์ธีมของคุณ WordPress รวมข้อมูลหลายชั้นเข้าด้วยกันก่อนจะปล่อย CSS ออกมา

  1. ค่าเริ่มต้นของแกนหลัก WordPress — แกนหลักมี theme.json ของตัวเองที่มีพาเลตต์ ขนาดฟอนต์ และสเกลระยะห่างพื้นฐานมาให้
  2. theme.json ของธีมแม่
  3. theme.json ของธีมลูก — รวมทับธีมแม่แบบทีละคีย์ ไม่ได้แทนที่ทั้งไฟล์
  4. Global Styles ที่บันทึกในฐานข้อมูล — คือสิ่งที่แผง Styles ใน Site Editor เขียนลงไป
  5. สไตล์รายบล็อกบนบล็อกแต่ละตัว — แอตทริบิวต์ที่เก็บอยู่ในเนื้อหาโพสต์ แล้วแสดงผลเป็นสไตล์อินไลน์หรือคลาส

ชั้นที่ 4 คือชั้นที่ทำให้เว็บพัง วินาทีที่ใครสักคนเปิดแผง Styles แล้วกดบันทึก WordPress จะเก็บระเบียน Global Styles เอาไว้ และระเบียนนั้นจะชนะไฟล์ของคุณในทุกคุณสมบัติที่มันมี การแก้ theme.json หลังจากนั้นจะไม่ทำให้อะไรเปลี่ยนสำหรับคุณสมบัติเหล่านั้น กลายเป็นเว็บที่หน้าตาเหมือนเดิมเป๊ะหลังดีพลอย ทั้งที่ไฟล์บนดิสก์ต่างไปอย่างเห็นได้ชัด

ทางแก้คือลบระเบียนที่บันทึกไว้ ไม่ใช่ยกระดับสงครามด้วย !important ให้เปิด Site Editor ไปที่ Styles เปิดแผงประวัติการแก้ไข แล้วรีเซ็ตกลับเป็นค่าเริ่มต้นของธีม แต่ต้องยืนยันกับลูกค้าหรือเจ้าของเว็บก่อน เพราะการรีเซ็ตคือการทิ้งงานที่เขาปรับแต่งไว้ นั่นคือต้นทุนจริง ไม่ใช่การกดฟรี ๆ

ผู้ต้องสงสัยอันดับสองคือแคช WordPress จะแคชข้อมูล theme.json ที่แปลงไว้แล้วแทนที่จะอ่านไฟล์ใหม่ทุกครั้งที่มีคนเข้าเว็บ ซึ่งเป็นสิ่งที่คุณต้องการบนเซิร์ฟเวอร์จริง และเป็นสิ่งที่คุณไม่ต้องการเลยตอนกำลังพัฒนา การเปิด WP_DEBUG หรือเปิดโหมดพัฒนาธีมใน WordPress รุ่นใหม่ จะทำให้ระบบอ่านไฟล์ใหม่ทุกครั้ง ถ้าการแก้ไขของคุณโผล่มาก็ต่อเมื่อดีพลอยหรือล้างแคชเท่านั้น นั่นแหละคืออาการนี้

templateParts และ customTemplates

ทั้งสองคีย์เป็นอาเรย์ที่อธิบายไฟล์ซึ่งมีอยู่ในธีมของคุณอยู่แล้ว

{
  "templateParts": [
    { "name": "header", "title": "Header", "area": "header" },
    { "name": "footer", "title": "Footer", "area": "footer" }
  ],
  "customTemplates": [
    { "name": "page-wide", "title": "Wide Page", "postTypes": ["page"] }
  ]
}

name ต้องตรงกับชื่อไฟล์โดยไม่มีนามสกุล เช่น parts/header.html และ templates/page-wide.html ค่า area มีความหมายจริง เพราะมันตัดสินว่าส่วนย่อยนั้นจะถูกห่อด้วยอิลิเมนต์ landmark ของส่วนหัวหรือส่วนท้าย และหน้าแก้ไขจะจัดหมวดมันอย่างไร ถ้าไม่ใส่ ระบบจะถือว่าส่วนย่อยนั้นไม่มีหมวด

customTemplates คือสิ่งที่ทำให้เทมเพลตโผล่ในเมนูเลือกเทมเพลตของหน้าแก้ไขเนื้อหา ถ้าไม่มีรายการนี้ ไฟล์ก็ยังอยู่ แต่ไม่มีใครเลือกมันได้

ลำดับการตรวจสอบอย่างรวดเร็ว

เมื่อ block theme ไม่ลงสไตล์ตามที่ไฟล์บอกไว้ ให้ไล่ตามนี้ก่อนจะไปแตะอะไร

  1. ดูซอร์สโค้ดของหน้า แล้วค้นหาบล็อก global styles ที่ระบบสร้างขึ้น ถ้าไม่เจอตัวแปรของคุณในนั้น ปัญหาอยู่ที่ settings หรือพิมพ์ slug ผิด
  2. ถ้าตัวแปรมีอยู่แต่ไม่มีใครเรียกใช้ ปัญหาคือขาดรายการใน styles
  3. ถ้ามีครบทั้งสองอย่างแต่ยังมีอะไรมาทับ ให้ตรวจหาระเบียน Global Styles ที่บันทึกไว้
  4. หลังจากนั้นค่อยไปดูสไตล์ชีตของคุณเอง ปลั๊กอิน หรือตัวสร้างหน้าที่แทรก CSS เข้ามาทีหลังในลำดับการทับซ้อน

FAQ

คำถาม

theme.json ใน WordPress คืออะไร

เป็นไฟล์ตั้งค่า JSON ไฟล์เดียวที่วางอยู่ที่รากของ block theme และคุมสองเรื่อง คือ block editor จะเปิดตัวเลือกงานออกแบบอะไรให้ผู้ใช้บ้าง และสไตล์เริ่มต้นของเว็บเป็นอย่างไร WordPress จะอ่านไฟล์นี้ แล้วสร้าง CSS custom property กับสไตล์ชีตออกมา จากนั้นรวมเข้ากับค่าเริ่มต้นของแกนหลักและ Global Styles ที่ผู้ใช้บันทึกไว้

settings กับ styles ใน theme.json ต่างกันอย่างไร

settings เป็นตัวบอกว่ามีอะไรให้ใช้ได้บ้าง มันสร้างตัวแปร CSS และตัวควบคุมในหน้าแก้ไข แต่ไม่ได้ไปทาสีอะไรเองเลย ส่วน styles คือตัวที่เอาค่าเหล่านั้นไปใช้จริง การเพิ่มสีลงในพาเลตต์จึงไม่ทำให้อะไรเปลี่ยน จนกว่าคุณจะอ้างถึงสีนั้นใต้ styles หรือผู้ใช้เลือกมันเองในหน้าแก้ไข

ทำไมแก้ theme.json แล้วหน้าเว็บจริงไม่เปลี่ยน

เกือบทุกครั้งเป็นเพราะ Global Styles ที่บันทึกอยู่ในฐานข้อมูลทับค่าจาก theme.json อยู่ พอมีใครไปแตะแผง Styles ใน Site Editor สักครั้ง ข้อมูลชุดนั้นจะชนะทุกคุณสมบัติที่มันเก็บไว้ ให้เปิด Styles แล้วใช้แผงประวัติการแก้ไขรีเซ็ตกลับเป็นค่าเริ่มต้นของธีม แล้วโหลดหน้าใหม่ ผู้ต้องสงสัยอันดับสองคือแคชของข้อมูล theme.json ที่ถูกแปลงไว้แล้ว

มี theme.json แล้วยังต้องมีไฟล์ style.css อยู่ไหม

ต้องมี เพราะ WordPress ต้องใช้ style.css สำหรับคอมเมนต์ส่วนหัวของธีมที่ระบุชื่อธีม เวอร์ชัน และข้อมูลอื่น ๆ ไม่อย่างนั้นระบบจะไม่รู้จักธีมนี้เลย คุณจะปล่อยให้ไฟล์ว่างแล้วย้ายการตัดสินใจด้านดีไซน์ไปไว้ใน theme.json ก็ได้ หรือจะเก็บ CSS ไว้เล็กน้อยสำหรับสิ่งที่ theme.json เขียนไม่ได้ก็ได้

ควรใส่เลขเวอร์ชันอะไรใน theme.json

ใช้เวอร์ชันสูงสุดที่ WordPress รุ่นต่ำสุดที่คุณรองรับเข้าใจได้ คีย์ version บอก WordPress ว่าให้อ่านโครงสร้างสคีมาแบบไหน และ WordPress รุ่นเก่าจะข้ามคีย์ที่มันไม่รู้จักไปเงียบ ๆ ถ้าคุณต้องรองรับเว็บที่ยังใช้รุ่นเก่า ให้ยึดเวอร์ชันที่รุ่นนั้นรองรับ อย่าคิดเอาเองว่าคีย์ใหม่จะลดระดับลงมาให้เอง

theme.json ใช้แทน CSS ได้ทั้งหมดเลยไหม

ไม่ได้ มันครอบคลุมพรีเซ็ต ค่าเริ่มต้นของบล็อก สไตล์ของอิลิเมนต์ ความกว้างของเลย์เอาต์ และระยะห่างได้ดี แต่มันไม่มีแนวคิดของ media query ที่คุณเขียนเอง ไม่มีซีเล็กเตอร์ซับซ้อน ไม่มีแอนิเมชัน และรองรับสถานะ pseudo-class เพียงไม่กี่ตัว block theme ที่ใช้งานจริงส่วนใหญ่จึงมาพร้อม theme.json บวกกับสไตล์ชีตขนาดย่อมสำหรับส่วนที่เหลือ