บทความ › เว็บและแอป

ภาษาไทยบนเว็บ ตัดคำ ฟอนต์ เวลาอ่าน อะไรพัง และแก้ยังไงใน 5 เว็บ

เบราว์เซอร์ตัดคำไทยด้วยพจนานุกรม พลาดคำทับศัพท์ในหัวข้อใหญ่ keep-all ไม่ช่วย วิธีแก้ที่ใช้จริง เวลาอ่านนับตัวอักษร คู่ฟอนต์ไทย+อังกฤษ line-height และ lang

กลาง อ่าน 10 นาที 21 ตุลาคม 2569

อ่านจบแล้วจะรู้
  • เบราว์เซอร์หาจุดตัดคำไทยจากพจนานุกรม คำทับศัพท์อย่าง คลาวด์ โปรเจกต์ ไม่อยู่ในนั้น หัวข้อใหญ่จึงหักกลางคำ word-break keep-all ไม่ช่วย เพราะทำมาเพื่อ CJK
  • วิธีแก้ที่ได้ผลคือห่อวลีด้วย span ที่ nowrap และเว้นวรรครอบคำทับศัพท์ในหัวข้อ ให้ตัดได้แค่ระหว่างวลี
  • เวลาอ่านของไทยต้องนับตัวอักษร (เราใช้ 900 ตัว/นาที) นับคำแบบอังกฤษ (220 คำ/นาที) จะได้เลขผิดหลายเท่า
  • ไทยมีสระและวรรณยุกต์ซ้อนบนล่าง ต้องให้ line-height มากกว่าอังกฤษ และฟอนต์ไทยกับอังกฤษควรเป็นตระกูลเดียวกันหรือ x-height ใกล้กัน

เว็บภาษาไทยที่ดูดีบนจอคอมพิวเตอร์ เปิดบนมือถือแล้วหัวข้อหักคำว่า “คลาวด์” เป็น “คลา” กับ “วด์” คนละบรรทัด เวลาอ่านบอก 2 นาที ทั้งที่อ่านจริง 6 นาที และฟอนต์ไทยกับอังกฤษในประโยคเดียวกันสูงไม่เท่ากัน สามเรื่องนี้เราเจอครบตอนทำเว็บนี้ แล้วเจออีกครั้งตอนทำอีกสี่เว็บด้วย เอนจินเดียวกัน บทความนี้รวมสิ่งที่พัง เหตุผล และโค้ดที่ใช้แก้จริง

ตรวจกับ Chrome, Safari และ Firefox รุ่นปัจจุบัน (ต.ค. 2026) บน Windows 11, macOS และ iPhone ฟอนต์จาก Google Fonts

ไทยไม่มีช่องว่าง เบราว์เซอร์ต้องเดาคำ

ภาษาอังกฤษตัดบรรทัดที่ช่องว่าง ภาษาไทยไม่มีช่องว่างระหว่างคำ ช่องว่างที่มีคั่น “วลี” ไม่ใช่ “คำ” เบราว์เซอร์จึงต้องหาเองว่าคำจบตรงไหน สเปก CSS Text เขียนไว้ตรง ๆ ว่าภาษาไทย ลาว เขมร ต้องใช้ “lexical resource” คือพจนานุกรม Chrome และ Safari ใช้ตัวตัดคำแบบพจนานุกรมของ ICU ที่มีรายการคำไทยติดมา แต่ละเอนจินถือข้อมูลคนละชุด ผลจึงต่างกันได้บ้าง

พจนานุกรมพวกนี้รู้จักคำไทยทั่วไปดี ปัญหาอยู่ที่คำทับศัพท์ “คลาวด์” “โปรเจกต์” “เซิร์ฟเวอร์” พอไม่รู้จักทั้งคำ ตัวตัดคำก็หาคำย่อยที่รู้จักข้างใน แล้วตัดตรงนั้น ในย่อหน้าตัวหนังสือ 17px โอกาสที่จุดตัดจะตกกลางคำทับศัพท์มีน้อย แต่ h1 ขนาด 46px บนจอ 375px มีที่ให้แค่ 7-8 ตัวอักษรต่อบรรทัด ทุกบรรทัดต้องตัดสักที่ และที่นั้นมักเป็นกลางคำทับศัพท์

เบราว์เซอร์ตัดเองปลูกผักบนคลาวด์ให้รอดครอบวลีด้วย nowrapปลูกผักบนคลาวด์ให้รอด
คำยืมอย่าง คลาวด์ ไม่อยู่ในพจนานุกรมตัดคำ ต้องบอกเบราว์เซอร์เอง

keep-all ไม่ช่วย

สิ่งแรกที่คนลองคือ word-break: keep-all พูดให้ชัด มันไม่ได้ผลกับภาษาไทย MDN เขียนว่า keep-all คือ “ห้ามตัดคำในข้อความจีน ญี่ปุ่น เกาหลี (CJK) ส่วนข้อความอื่นทำตัวเหมือน normal” และสเปก CSS Text ระบุว่าใน keep-all การตัดยังเกิดได้ “ตรงที่มีโอกาสตัดจาก dictionary-based breaking” ซึ่งก็คือภาษาไทยพอดี ทุกค่าของ word-break และ line-break ยังตัดไทยตามพจนานุกรมเดิม จุดที่ผิดก็ยังผิดเหมือนเดิม

เบราว์เซอร์ตัดคำไทยด้วยพจนานุกรม ไม่มี property ของ CSS ตัวไหนสอนคำใหม่ให้มัน ทางแก้คือบอกว่าตรงไหนห้ามตัด

วิธีแก้ 1: ห่อวลีด้วย nowrap

หัวข้อหน้าแรกเขียนด้วยมือ เราห่อแต่ละวลีด้วย <span class="nw"> ที่เป็น white-space: nowrap บรรทัดจะตัดได้เฉพาะระหว่าง span

<!-- ก่อน: เบราว์เซอร์ตัดได้ทุกจุดที่พจนานุกรมบอก -->
<h1>สร้างระบบที่ทำงานได้จริง บนคลาวด์และหน้างาน</h1>

<!-- หลัง: ตัดได้แค่ระหว่างวลี คลาวด์ ไม่มีทางหัก -->
<h1><span class="nw">สร้างระบบที่ทำงานได้จริง</span> <span class="nw">บนคลาวด์และหน้างาน</span></h1>
.nw{white-space:nowrap}

วลีต้องสั้นพอให้อยู่ในจอ 375px ที่ขนาดฟอนต์นั้น 14 ตัวอักษรที่ 36px กว้างราว 300px พอดี ยาวกว่านั้นจะล้นออกขวาเพราะห้ามตัด

วิธีแก้ 2: เว้นวรรครอบคำทับศัพท์ในหัวข้อบทความ

หัวข้อบทความมาจาก frontmatter ใส่ span ไม่ได้ เราเขียน component เล็ก ๆ ThaiTitle.astro แยกหัวข้อตามช่องว่าง แล้วห่อแต่ละชิ้นที่ยาวไม่เกิน 14 ตัวอักษรด้วย .nw ชิ้นที่ยาวกว่าปล่อยให้ตัดปกติเพื่อไม่ให้ล้นจอ ภาษาอังกฤษผ่านไปโดยไม่แตะ

const parts = text.split(' ');
{lang === 'th'
  ? parts.map((w, i) => <>{i > 0 && ' '}{w.length <= 14 ? <span class="nw">{w}</span> : w}</>)
  : text}

เวลาเขียนหัวข้อไทย ถ้าคำทับศัพท์ไหนหักบ่อย แค่เว้นวรรครอบคำนั้น

# ก่อน: โปรเจกต์ อาจหักเป็น โปร / เจกต์
title: "ทำไมโปรเจกต์ต้องมี README ตั้งแต่วันแรก"
# หลัง: โปรเจกต์ เป็นชิ้นของตัวเอง ThaiTitle ห่อให้ทั้งคำ
title: "ทำไม โปรเจกต์ ต้องมี README ตั้งแต่วันแรก"

ช่องว่างเพิ่มหนึ่งตัวในหัวข้อไทยอ่านไม่สะดุด เพราะไทยใช้ช่องว่างคั่นวลีอยู่แล้ว และเป็นการแก้ที่ข้อมูล ไม่ใช่ CSS

ผลข้างเคียงฝั่งอังกฤษ และกฎ 520px

.nw ตัวเดียวกันถูกใช้กับ hero ภาษาอังกฤษด้วย เว็บที่ใช้ฟอนต์หัวข้อตัวกว้าง (Kanit) วลีอังกฤษวลีเดียวยาวกว่าจอมือถือแล้วล้นออกขวา ไทยไม่เป็นเพราะวลีสั้นกว่า อังกฤษมีช่องว่างและเบราว์เซอร์ตัดไม่ผิด จึงปล่อยให้อังกฤษตัดปกติบนจอแคบ

/* English has spaces to break at, and a wide display face can make one phrase wider than a phone */
@media (max-width:520px){html[lang="en"] .nw{white-space:normal}}

กฎนี้อยู่ใน CSS ร่วมของเอนจิน แก้ครั้งเดียวได้ทั้งสี่เว็บ

เวลาอ่าน นับตัวอักษร ไม่นับคำ

สูตรทั่วไปคือนับคำหารด้วย 200-250 คำต่อนาที ในโค้ด “นับคำ” คือ split(/\s+/) ซึ่งนับช่องว่าง บทความไทย 5,500 ตัวอักษรอาจมีช่องว่างแค่ 400 ตัว สูตรนี้จึงตอบ 2 นาที ทั้งที่อ่านจริงราว 6 นาที

เราแยกสองสูตรตามภาษาของไฟล์ ไทยนับตัวอักษร 900 ตัวต่อนาที อังกฤษนับคำ 220 คำต่อนาที โค้ดนับสองเท่าทั้งคู่ และตัด SVG ของแผนภาพออกก่อน

const m = postLang(p) === 'th'
  ? (prose.length + codeBlocks.length * 2) / 900
  : (prose.split(/\s+/).length + codeBlocks.split(/\s+/).length * 2) / 220;
return Math.max(1, Math.round(m));

ตัวเลข 900 มาจากการจับเวลาอ่านบทความของเราเองสองสามบท ไม่ใช่ค่าวิจัย ถ้าคนอ่านเป็นผู้เริ่มต้น 700 ก็สมเหตุสมผล สิ่งสำคัญคือหน่วยต้องเป็นตัวอักษร

คู่ฟอนต์ไทย + อังกฤษ

ฟอนต์ไทยแบ่งหยาบ ๆ เป็น “มีหัว” (looped) หัวกลมเล็กที่ต้นเส้นแบบหนังสือเรียน อ่านยาวสบายและดูเป็นทางการ กับ “ไม่มีหัว” (loopless) ที่ตัดหัวออกให้รูปทรงใกล้อักษรละติน ดูทันสมัย แต่บางคู่ (ด กับ ค) สับสนได้ที่ขนาดเล็ก ห้าเว็บของเราเลือกตามน้ำเสียงของเว็บ

เว็บหัวข้อ (display)เนื้อหา (body)ป้าย/โค้ด (mono)เหตุผล
6amdevBai Jamjuree 600-700IBM Plex Sans Thai 400-600IBM Plex Monoเว็บช่าง ไม่มีหัว คม
devmakerxChakra Petch 600-700IBM Plex Sans ThaiJetBrains Monoโทนเมกเกอร์ ตัวเหลี่ยม
mykasetKanit 500-600IBM Plex Sans ThaiIBM Plex Monoเกษตร เป็นมิตร ตัวหนาแน่น
aroysureMitr 400-700IBM Plex Sans Thai LoopedMitrเว็บอาหาร ป้ายใช้ Mitr แทนฟอนต์โค้ด อ่านอบอุ่นกว่า
pickoshopPridi 500-600IBM Plex Sans Thai Loopedใช้ body แทนร้านค้า มีหัว นุ่ม ไม่มีโค้ดบนเว็บ
  • ตระกูลเดียวกันก่อน IBM Plex Sans Thai กับ IBM Plex Mono มี x-height เดียวกัน ตัวเลขและคำอังกฤษในประโยคไทยจึงไม่ลอยไม่จม ถ้าตระกูลไม่ตรง ดูคำว่า “API” กลางประโยคไทยที่ 16px
  • ฟอนต์หัวข้อที่มีละตินในตัว Bai Jamjuree, Kanit, Mitr, Chakra Petch, Pridi มีอักษรละตินมาด้วย หัวข้อผสมไทย-อังกฤษจึงเป็นฟอนต์เดียว ไม่มีฟอนต์สำรองมาปน
  • น้ำหนักต่างกันตามฟอนต์ เอนจินตั้งหัวข้อ 700 แต่ Mitr และ Kanit ที่ 700 ตันเกินไปสำหรับไทย comment ใน tokens.css ของ aroysure เขียนไว้ว่า “Mitr reads best a step lighter” จึง override เป็น 600
  • โหลดเฉพาะชุดที่ใช้ Google Fonts ส่ง @font-face แยกตามชุดอักษร ชุด thai ครอบ U+0E01-0E5B และเครื่องหมายไม่กี่ตัว เบราว์เซอร์โหลดเฉพาะเมื่อหน้านั้นมีตัวอักษรในช่วงนั้น เราขอแค่น้ำหนักที่ใช้ (Bai+Jamjuree:wght@600;700, IBM+Plex+Sans+Thai:wght@400;500;600) และใส่ display=swap

line-height ไทยต้องสูงกว่า

ตัวอักษรไทยซ้อนได้สามชั้น พยัญชนะ สระบน วรรณยุกต์ (เช่น “ที่” “ปั้น”) และยังมีสระล่าง (อุ อู) line-height 1.5 ที่พออังกฤษ ทำให้วรรณยุกต์บรรทัดล่างชนสระล่างของบรรทัดบน เราใช้ 1.85 กับเนื้อหาบทความ 17px, 1.7 กับข้อความทั่วไป และ 1.2 กับหัวข้อ ที่ตัวใหญ่มีช่องว่างสัมพัทธ์พออยู่แล้ว ต่ำกว่านี้วรรณยุกต์ถูกตัด หัวข้อยังใส่ text-wrap: balance ซึ่งอยู่ร่วมกับ .nw ได้ เพราะ balance แค่เลือกจากจุดตัดที่อนุญาตอยู่แล้ว

lang ทำอะไร

<html lang="th"> และ lang="en" ที่หน้าอังกฤษ ไม่ได้ทำให้ตัดคำไทยแม่นขึ้น เบราว์เซอร์ตัดไทยตามตัวอักษร แต่มันทำสามอย่างที่ต้องการ ระบบเลือกฟอนต์สำรองที่ถูกก่อนฟอนต์เว็บมา (iOS หยิบ Thonburi ให้หน้าไทย ไม่ใช่ฟอนต์จีน) hyphens: auto ของหน้าอังกฤษทำงานได้ (ต้องมี lang เสมอ) และ screen reader อ่านด้วยเสียงที่ถูก ปุ่มสลับภาษาจึงใส่ lang ของภาษาปลายทางด้วย และมันคือสิ่งที่ html[lang="en"] .nw ข้างบนใช้แยกกฎ

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

  • ใส่ word-break: keep-all หรือ line-break: strict แล้วคิดว่าแก้ไทยได้ ทั้งสองค่าทำเพื่อ CJK
  • ห่อทั้งหัวข้อด้วย nowrap อันเดียว บนจอ 375px ล้นออกขวาทันที
  • โหลดฟอนต์ไทยทุกน้ำหนัก 100-900 ทั้งที่ใช้สามน้ำหนัก
  • ใช้ line-height 1.5 ตามแม่แบบอังกฤษ แล้ววรรณยุกต์ชนบรรทัดบน
  • นับเวลาอ่านด้วย split(/\s+/) กับไฟล์ไทย ได้เลขน้อยกว่าจริงราว 3 เท่า

แหล่งอ้างอิง

คำถามที่พบบ่อย

ทำไมไม่ใส่ zero-width space (U+200B) ระหว่างคำไทยทุกคำไปเลย

ทำได้ แต่ต้องมีตัวตัดคำตอน build ตัวอักษรที่มองไม่เห็นจะหลุดไปใน copy-paste และผลค้นหา และเสียเวลากับทุกบทความ สำหรับเรา ปัญหาเกิดเฉพาะหัวข้อตัวใหญ่ที่คำน้อย การเว้นวรรครอบคำทับศัพท์ในหัวข้อจึงพอ

ใช้ฟอนต์ไทยจาก Google Fonts ช้าไหม

ไฟล์แยกตามชุดอักษร เบราว์เซอร์โหลดเฉพาะชุดที่หน้านั้นใช้จริงผ่าน unicode-range หน้าภาษาไทยโหลดชุด thai กับ latin เท่านั้น เราขอแค่ 2-3 น้ำหนักต่อตระกูล และใส่ display=swap ให้ข้อความขึ้นก่อนฟอนต์มา

lang="th" ช่วยให้เบราว์เซอร์ตัดคำไทยถูกขึ้นไหม

ไม่โดยตรง การตัดคำไทยดูจากตัวอักษรไทยในข้อความ ไม่ได้ดู lang แต่ lang ยังจำเป็นสำหรับฟอนต์สำรองของระบบ hyphens ของอังกฤษ screen reader และเป็นตัวที่ CSS ของเราใช้แยกกฎไทยกับอังกฤษ

คำสั่งและค่าตั้งในบทความทดสอบบนระบบตัวอย่าง ลองบนเครื่องทดสอบก่อนใช้กับระบบจริงเสมอ

อ่านต่อ

#เว็บกลาง

CSS anchor positioning ใช้ได้ทุก browser แล้ว: tooltip และเมนู ไม่ต้องเขียน JS

anchor positioning เป็น Baseline ตั้งแต่ Firefox 147 ผูก tooltip หรือเมนูกับปุ่มด้วย CSS ล้วน หลบขอบจอเองด้วย position-try-fallbacks พร้อมทางสำรองสำหรับ browser เก่า

อ่าน 9 นาที
#เว็บกลาง

สี่เว็บจาก เอนจิน เดียว แชร์โค้ดด้วย Astro monorepo แต่หน้าตาไม่เหมือนกัน

แยก core/ กับโฟลเดอร์ต่อเว็บ ให้สี่เว็บใช้ layout i18n และ deploy ชุดเดียว ต่างกันแค่สี ฟอนต์ และหน้าแรก ผ่าน design tokens พร้อมบั๊กจริงที่แก้ครั้งเดียวได้ทั้งสี่เว็บ

อ่าน 15 นาที
#เว็บเริ่มต้น

เปิด dialog และ popover ไม่ต้องเขียน JS: command และ commandfor ใน HTML

ปุ่ม HTML สั่งเปิดปิด dialog และ popover ได้เองด้วย command กับ commandfor ใช้ได้ทุกเบราว์เซอร์หลักตั้งแต่ธันวาคม 2025 ค่าที่มีให้ คำสั่งของเราเอง และทางสำรอง

อ่าน 11 นาที