← Tất cả cẩm nang

Menu và LCP: cách navigation chặn largest contentful paint của bạn

Chiến lược tải font menu: tránh layout shift và text vô hình

Các chiến lược font-display cho menu điều hướng—cân bằng tính nhất quán thương hiệu với LCP và CLS.

Bạn đã dành hàng giờ để chọn kiểu chữ hoàn hảo cho cửa hàng. Các nhãn menu sử dụng một sans-serif tùy chỉnh phù hợp hoàn toàn với thương hiệu của bạn. Nhưng mỗi lần kiểm tra cửa hàng trên mobile, bạn thấy một tia sáng text vô hình ở nơi menu phải có, theo sau là layout shift khi font cuối cùng tải xong. Điểm CLS của bạn là 0,18, Google đánh dấu là “cần cải thiện”, và bạn nghi ngờ font menu là thủ phạm.

Vấn đề không phải là font chính nó. Vấn đề là cách trình duyệt xử lý việc tải font. Theo mặc định, hầu hết các trình duyệt ẩn text cho đến khi custom font tải xong, điều này có thể mất 300 đến 800 mili giây trên kết nối mobile chậm. Khu vực menu ngồi trống rỗng, trang trông bị lỗi, và khi font cuối cùng xuất hiện, nó dịch chuyển mọi thứ bên dưới. Điều này làm tổn hại cả LCP (vì nội dung quan trọng bị trì hoãn) và CLS (vì layout di chuyển).

Bài viết này giải thích năm chiến lược font-display, loại nào hoạt động tốt nhất cho menu điều hướng, và cách triển khai tải font mà không phá vỡ thương hiệu hoặc Core Web Vitals của bạn.

Đọc nhanh
  • Trình duyệt ẩn text trong quá trình tải font theo mặc định, gây Flash of Invisible Text (FOIT).
  • font-display kiểm soát xem text có hiển thị ngay lập tức hay chờ custom font.
  • font-display: swap là lựa chọn tốt nhất cho text menu—hiển thị fallback ngay lập tức, hoán đổi khi sẵn sàng.
  • Hệ thống font (đã cài đặt trên thiết bị) có chi phí tải bằng không và loại bỏ FOIT/FOUT hoàn toàn.
  • Preload font có thể giảm trễ nhưng không loại bỏ nguy cơ layout shift.

Hai vấn đề tải font: FOIT và FOUT

Khi trình duyệt gặp một web font tùy chỉnh, nó phải quyết định phải làm gì với text sử dụng font đó trong khi file font vẫn còn đang tải. Có hai hành vi có thể, mỗi hành vi có những tradeoff.

Flash of Invisible Text (FOIT)

Trình duyệt ẩn text cho đến khi font tải xong. Không gian text được dành riêng, nhưng không có gì xuất hiện. Nếu font mất 500 mili giây để tải, người dùng sẽ nhìn vào khoảng trắng suốt nửa giây. Đây là hành vi mặc định trong hầu hết các trình duyệt.

FOIT xấu cho LCP vì nội dung text quan trọng không hiển thị cho đến khi font tải. Nó cũng xấu cho khả năng sử dụng—người dùng không thể đọc menu, điều này có thể khiến họ nghĩ trang bị lỗi.

Flash of Unstyled Text (FOUT)

Trình duyệt hiển thị text ngay lập tức bằng cách sử dụng font fallback (thường là system font như Arial hoặc Helvetica), sau đó hoán đổi sang custom font khi tải xong. Text có thể đọc được ngay lập tức, nhưng hoán đổi có thể gây layout shift nếu font fallback và custom font có kích thước khác nhau.

FOUT tốt hơn cho LCP (text xuất hiện ngay lập tức) nhưng có thể làm tổn hại CLS nếu không được xử lý cẩn thận. Chìa khóa là khớp kích thước của font fallback với custom font càng gần càng tốt.

Năm chiến lược font-display

Thuộc tính CSS font-display kiểm soát cách các trình duyệt xử lý khoảng thời gian tải font. Có năm giá trị có thể, mỗi giá trị có hành vi và fallback khác nhau.

Giá trị Hành vi Tốt nhất cho
auto Trình duyệt quyết định (thường FOIT 3 giây, rồi FOUT) Không được khuyên—không thể dự đoán
block FOIT tối đa 3 giây, rồi hoán đổi Text trang trí nơi font thương hiệu quan trọng
swap FOUT ngay lập tức—hiển thị fallback, hoán đổi khi sẵn sàng Text nội dung, menu điều hướng
fallback FOIT 100ms, rồi FOUT 3s, rồi dính với fallback Tiêu đề nơi font quan trọng nhưng tốc độ cũng quan trọng
optional FOIT 100ms, rồi dùng fallback nếu font chưa sẵn sàng, không bao giờ hoán đổi Text quan trọng về hiệu suất

Đối với menu điều hướng, font-display: swap hầu như lúc nào cũng là lựa chọn đúng. Nó làm cho menu có thể đọc được ngay lập tức (tốt cho LCP và khả năng sử dụng) và hoán đổi sang font thương hiệu ngay khi tải xong (tốt cho tính nhất quán thương hiệu). Rủi ro là layout shift, mà chúng ta sẽ giải quyết trong một lúc.

Cách sử dụng font-display trong Stylesheet của bạn

Nếu font của bạn được định nghĩa trong CSS của bạn bằng cách sử dụng @font-face, hãy thêm thuộc tính font-display:

@font-face {
  font-family: 'BrandFont';
  src: url('/fonts/brandfont.woff2') format('woff2');
  font-display: swap;
}

.menu-link {
  font-family: 'BrandFont', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
}

Stack font-family là quan trọng. Liệt kê custom font của bạn trước, sau đó là system font làm fallback. Điều này đảm bảo text menu xuất hiện ngay lập tức bằng system font, rồi hoán đổi sang custom font của bạn khi tải xong.

Nếu font của bạn được tải từ Google Fonts, hãy thêm tham số display=swap vào URL:

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap">

Google Fonts sẽ tự động chèn font-display: swap vào CSS được tạo ra.

Ngăn chặn Layout Shift bằng Fallback Font Matching

Vấn đề với font-display: swap là nếu font fallback và custom font có kích thước hoặc khoảng cách khác nhau, text sẽ reflow khi hoán đổi xảy ra, gây layout shift. Điều này đặc biệt dễ nhận thấy trong menu điều hướng, nơi ngay cả một shift nhỏ cũng có thể di chuyển toàn bộ nội dung trang.

Giải pháp là điều chỉnh font fallback để khớp với kích thước của custom font bằng cách sử dụng các thuộc tính size-adjust, ascent-override, và descent-override. Đây là những tính năng CSS tương đối mới (được hỗ trợ trong tất cả các trình duyệt hiện đại từ 2024) cho phép bạn điều chỉnh font fallback để khớp với custom font.

Dưới đây là một ví dụ:

@font-face {
  font-family: 'BrandFont';
  src: url('/fonts/brandfont.woff2') format('woff2');
  font-display: swap;
}

@font-face {
  font-family: 'BrandFont Fallback';
  src: local('Arial');
  size-adjust: 107%;
  ascent-override: 95%;
  descent-override: 25%;
}

.menu-link {
  font-family: 'BrandFont', 'BrandFont Fallback', Arial, sans-serif;
}

Thuộc tính size-adjust chia tỷ lệ font fallback sao cho x-height của nó khớp với custom font. Các thuộc tính ascent-overridedescent-override điều chỉnh khoảng cách dọc.

Tìm giá trị đúng yêu cầu thử nghiệm. Các công cụ như Fallback Font Generator của Simon Hearne tự động hóa quy trình này. Bạn nhập custom font của mình, và công cụ tính toán các giá trị override tối ưu cho các system font phổ biến.

Khớp font fallback theo cách này làm giảm CLS từ font swapping gần bằng không. Text vẫn hoán đổi từ fallback sang custom font, nhưng layout không di chuyển vì kích thước là giống hệt nhau.

System Fonts: Giải pháp chi phí bằng không

Font nhanh nhất là không có font gì—hay đúng hơn, là system font đã được cài đặt trên thiết bị của người dùng. System font có chi phí tải bằng không, chi phí phân tích bằng không, và rủi ro layout shift bằng không.

Dưới đây là một system font stack hiện đại trông tốt trên tất cả các nền tảng:

.menu-link {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', Arial, sans-serif;
}

Điều này cung cấp cho bạn:

  • San Francisco trên macOS và iOS
  • Segoe UI trên Windows
  • Roboto trên Android
  • Mặc định thích hợp với nền tảng trên Linux

System font không đặc biệt như typeface tùy chỉnh, nhưng chúng sạch sẽ, dễ đọc, và được tối ưu hóa cho nền tảng của chúng. Nhiều trang web lưu lượng cao (GitHub, Medium, WordPress.com) sử dụng system font vì lý do hiệu suất.

Nếu hướng dẫn thương hiệu của bạn yêu cầu custom font, hãy cân nhắc sử dụng system font cho menu điều hướng và dành custom font cho tiêu đề, hero text, hoặc body copy nơi nó có tác động hình ảnh lớn hơn. Menu là UI chức năng. Nó không cần phải mang thương hiệu nặng như một tiêu đề làm.

Preload Font để giảm khoảng thời gian FOUT

Ngay cả với font-display: swap, cũng có một trễ giữa fallback xuất hiện và custom font hoán đổi. Trên kết nối chậm, điều này có thể là 500 mili giây hoặc nhiều hơn. Preload file font cho trình duyệt biết rằng hãy tải nó với độ ưu tiên cao vào đầu trang tải, điều này làm giảm trễ hoán đổi.

<link rel="preload" href="/fonts/brandfont.woff2" as="font" type="font/woff2" crossorigin>

Thuộc tính crossorigin được yêu cầu ngay cả khi font được lưu trữ trên tên miền của bạn, vì font được tải với chế độ CORS.

Preload không loại bỏ FOUT, nhưng nó giảm khoảng thời gian. Nếu font menu của bạn nhỏ (dưới 50KB) và quan trọng đối với thương hiệu, preload có thể cải thiện hiệu suất nhận thức.

Lưu ý: Preload cạnh tranh với các tài nguyên quan trọng khác như CSS và JavaScript. Chỉ preload font được sử dụng ở trên fold và thực sự cần thiết. Preload quá nhiều tài nguyên có thể làm chậm render ban đầu.

Icon Fonts vs Inline SVG

Nhiều menu điều hướng sử dụng icon font (Font Awesome, Remix Icon, Material Icons) cho mũi tên, biểu tượng tìm kiếm, và biểu tượng hamburger. Icon font chỉ là font, vì vậy chúng có cùng đặc tính tải như text font: FOIT theo mặc định, rủi ro layout shift, và chi phí tải (thường 50-150KB cho một full icon font).

Inline SVG là lựa chọn tốt hơn cho icon menu. Inline SVG là một phần của HTML, vì vậy nó xuất hiện ngay lập tức mà không cần yêu cầu mạng và không có trễ tải font. Nó cũng chia tỷ lệ hoàn hảo và hỗ trợ CSS styling.

Dưới đây là một ví dụ:

<button class="menu-toggle" aria-label="Open menu">
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
    <line x1="3" y1="12" x2="21" y2="12"></line>
    <line x1="3" y1="6" x2="21" y2="6"></line>
    <line x1="3" y1="18" x2="21" y2="18"></line>
  </svg>
</button>

Biểu tượng hamburger này là 200 byte. Một full icon font với 2.000 biểu tượng bạn sẽ không bao giờ sử dụng là 150KB. Inline SVG cũng loại bỏ vấn đề FOIT—biểu tượng xuất hiện ngay lập tức vì nó là một phần của HTML.

Các ứng dụng menu hiện đại như Navi+ sử dụng inline SVG cho tất cả các biểu tượng, đó là một lý do tại sao chúng tránh được các vấn đề layout shift làm ô nhiễm menu dựa trên icon font.

Tải Font cụ thể cho Shopify

Các Shopify theme thường tải font theo một trong ba cách: từ Google Fonts, từ CDN của Shopify (thông qua font picker cài đặt theme), hoặc từ file font tùy chỉnh trong thư mục asset của theme.

Google Fonts

Nếu theme của bạn sử dụng Google Fonts, hãy đảm bảo tham số display=swap nằm trong URL. Kiểm tra theme.liquid để tìm một dòng như thế này:

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600">

Thay đổi nó thành:

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap">

Shopify CDN Fonts

Nếu theme của bạn sử dụng font picker tích hợp của Shopify (Settings > Typography), Shopify phục vụ font từ CDN của nó và tự động bao gồm font-display: swap kể từ 2023. Bạn không cần phải làm gì cả.

Custom Font Files

Nếu bạn đã tải lên file font tùy chỉnh vào thư mục asset của theme, hãy thêm font-display: swap vào khai báo @font-face trong CSS của bạn.

Đo lường tác động tải Font

Sử dụng Chrome DevTools để xem cách font ảnh hưởng đến LCP và CLS của bạn.

Kiểm tra FOIT

Mở DevTools, đi tới tab Network, lọc theo “Font”, và tải lại trang. Xem vùng menu. Nếu text menu vô hình trong một khoảng thời gian đáng chú ý trước khi xuất hiện, bạn có FOIT. Thêm font-display: swap để sửa.

Kiểm tra Layout Shift

Mở DevTools, đi tới tab Performance, nhấp Record, tải lại trang, và dừng ghi. Nhìn vào phần Experience để tìm các thanh “Layout Shift” màu đỏ. Nhấp vào một thanh để xem những phần tử nào đã di chuyển. Nếu text menu được liệt kê, font swap gây ra layout shift. Sử dụng fallback font matching để sửa.

Đo CLS

Chạy bài kiểm tra Lighthouse (trong DevTools hoặc PageSpeed Insights). Nhìn vào điểm CLS và chẩn đoán “Avoid large layout shifts”. Nếu font được liệt kê là nguyên nhân, bạn cần fallback matching tốt hơn hoặc nên cân nhắc chuyển sang system font.

Thắng nhanhNếu menu của bạn sử dụng custom font và bạn thấy vấn đề CLS, hãy thử chuyển sang system font stack làm bài kiểm tra. Chạy Lighthouse lại. Nếu CLS giảm đáng kể, custom font là vấn đề. Bạn có thể quyết định xem giá trị thương hiệu của custom font có đáng giá chi phí hiệu suất hay không, hoặc triển khai fallback matching để có cả hai.

Tải Font Menu tốt trông như thế nào

Một menu điều hướng được tối ưu hóa tốt xử lý font theo một trong hai cách.

Tùy chọn 1: System font. Không có custom font, chi phí tải bằng không, FOIT bằng không, layout shift bằng không. Menu xuất hiện ngay lập tức với text trông tự nhiên.

.menu-link {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
}

Tùy chọn 2: Custom font với swap và fallback matching. Text menu xuất hiện ngay lập tức trong system font, rồi hoán đổi sang custom font mà không có layout shift.

@font-face {
  font-family: 'BrandFont';
  src: url('/fonts/brandfont.woff2') format('woff2');
  font-display: swap;
}

@font-face {
  font-family: 'BrandFont Fallback';
  src: local('Arial');
  size-adjust: 107%;
}

.menu-link {
  font-family: 'BrandFont', 'BrandFont Fallback', Arial, sans-serif;
}

Cả hai cách tiếp cận đều kết quả trong một menu có thể đọc được trong giây đầu tiên tải trang, đó là những gì LCP đo lường. Cách tiếp cận đầu tiên đơn giản hơn và nhanh hơn. Cách tiếp cận thứ hai bảo tồn tính nhất quán thương hiệu với chi phí hiệu suất tối thiểu.

Đối với biểu tượng, sử dụng inline SVG thay vì icon font. Các biểu tượng xuất hiện ngay lập tức, chia tỷ lệ hoàn hảo, và chi phí vài trăm byte thay vì 150KB.

Hầu hết các cửa hàng chuyển từ tải font mặc định (FOIT, không fallback matching) sang một trong hai mẫu này thấy CLS giảm 0,05 đến 0,15 điểm và LCP cải thiện 200 đến 400 mili giây trên mobile. Menu trông giống nhau đối với người dùng, nhưng trình duyệt hiển thị nó nhanh hơn và mượt mà hơn.

Bài viết này là một phần của hướng dẫn lớn hơn về Menu và LCP: cách navigation chặn largest contentful paint của bạn.

Chia sẻ Facebook X

Bắt đầu với Navi+ AI Menu Builder

Chọn nền tảng của bạn — miễn phí cài đặt, hoạt động trong vài phút.