מבוא: CSS ופונטים — הנקודה העיוורת שכולם מפספסים
אם עקבתם אחרי סדרת המאמרים שלנו על ביצועי אתרים, כבר כיסינו את Core Web Vitals, אופטימיזציית תמונות, JavaScript, ו-TTFB עם CDN ו-Caching. אבל יש נושא אחד שמפתחים נוטים להתעלם ממנו, ובכנות — זה מפתיע אותי בכל פעם מחדש: CSS ופונטים.
אלה לא רק עניין של "איך הדף נראה". הם משפיעים ישירות על כל מדדי Core Web Vitals: על LCP (כי CSS חוסם רינדור), על CLS (כי החלפת פונטים גורמת לקפיצות פריסה), ועל INP (כי סלקטורים מורכבים מאטים חישובי סגנון).
והמספרים מדברים בעד עצמם — לפי נתוני Web Almanac, יותר מ-40% מהאתרים טוענים לפחות 100KB של CSS שלא משמש כלל, ו-25% מקפיצות הפריסה נגרמות מטעינת פונטים. אז בואו נפרק את הנושא לעומק, עם דוגמאות קוד מעשיות שתוכלו ליישם כבר היום.
חלק 1: הבנת חסימת רינדור על ידי CSS
למה CSS חוסם את הרינדור
הנה עובדה שכדאי להפנים: הדפדפן לא יציג אף פיקסל על המסך עד שהוא מסיים לבנות את ה-CSSOM (CSS Object Model). כל קובץ CSS שמקושר ב-<head> של הדף? הוא חוסם את הרינדור הראשוני. ככל שקובצי ה-CSS גדולים יותר או מגיעים משרתים חיצוניים — כך הדף ייקח יותר זמן להופיע.
התהליך עובד ככה: הדפדפן מוריד את ה-HTML, מתחיל לבנות את ה-DOM, ואז נתקל בתגית <link rel="stylesheet">. ברגע הזה הוא פשוט עוצר הכל ומחכה שקובץ ה-CSS ייטען ויפורסר במלואו. רק אחר כך הוא ממשיך.
אם יש לכם שלושה קובצי CSS חיצוניים, כל אחד מ-CDN אחר — אתם בעצם מוסיפים שלוש נסיעות הלוך ושוב ברשת לפני שהמשתמש רואה משהו. אאוץ׳.
אסטרטגיית Critical CSS
הפתרון הכי אפקטיבי לבעיה הזו הוא הטמעת CSS קריטי ישירות בתוך ה-HTML. הרעיון פשוט (ויפה): מזהים את ה-CSS הנדרש לתצוגה הראשונית בלבד, מטמיעים אותו בתגית <style>, ואת שאר ה-CSS טוענים באופן אסינכרוני.
<head>
<!-- Critical CSS - inline -->
<style>
:root {
--color-primary: #1a73e8;
--font-body: system-ui, -apple-system, sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: var(--font-body); line-height: 1.6; }
.header { display: flex; align-items: center; padding: 1rem 2rem; }
.hero { min-height: 60vh; display: grid; place-items: center; }
.hero h1 { font-size: clamp(2rem, 5vw, 3.5rem); }
.hero img { width: 100%; height: auto; aspect-ratio: 16/9; }
</style>
<!-- Non-critical CSS - async loading -->
<link rel="preload" href="/css/main.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/css/main.css"></noscript>
</head>
עכשיו, איך מזהים מהו CSS קריטי? לא צריך לעשות את זה ידנית — יש כלים מעולים:
# Using the critical npm package
npm install critical --save-dev
# Generate critical CSS
npx critical index.html --base ./ --inline --minify \
--dimensions 1920x1080 --dimensions 375x667 \
--output index-optimized.html
הכלי critical סורק את הדף, מזהה אילו כללי CSS משפיעים על מה שהמשתמש רואה ראשון, ומייצר גרסה מוטמעת. השימוש בשתי רזולוציות מבטיח כיסוי גם לדסקטופ וגם למובייל.
חלוקת CSS לפי Media Queries
טכניקה פשוטה אבל יעילה מאוד שלא מספיק מפתחים מנצלים. חלוקת CSS לקבצים נפרדים לפי media query גורמת לדפדפן לסווג רק חלק מהקבצים כחוסמי רינדור:
<!-- Always render-blocking -->
<link rel="stylesheet" href="/css/core.css">
<!-- Only render-blocking on screens wider than 768px -->
<link rel="stylesheet" href="/css/desktop.css" media="(min-width: 769px)">
<!-- Only render-blocking when printing -->
<link rel="stylesheet" href="/css/print.css" media="print">
<!-- Not render-blocking on landscape devices -->
<link rel="stylesheet" href="/css/portrait.css" media="(orientation: portrait)">
בדוגמה הזו, משתמש במובייל לא ייחסם על ידי desktop.css. הדפדפן עדיין יוריד את הקובץ (בעדיפות נמוכה יותר), אבל לא יחכה לו לפני הרינדור. שינוי קטן, השפעה גדולה.
חלק 2: content-visibility ו-CSS Containment — מהפכה שקטה ברינדור
מהו CSS Containment
CSS Containment הוא מנגנון שבעצם אומר לדפדפן: "האלמנט הזה עצמאי — שינויים בתוכו לא משפיעים על שאר הדף." התוצאה? הדפדפן יכול לדלג על חישובי פריסה וצביעה מיותרים.
/* Layout containment - changes inside don't affect outside layout */
.card { contain: layout; }
/* Paint containment - content won't paint outside bounds */
.sidebar { contain: paint; }
/* Size containment - element size doesn't depend on children */
.widget { contain: size; }
/* Full containment - all of the above */
.isolated-component { contain: strict; }
/* Content containment - layout + paint (most common) */
.section { contain: content; }
הסוג שהכי כדאי להתחיל איתו הוא contain: content — הוא משלב layout ו-paint containment, בטוח להשתמש בו ברוב המקרים, ונותן שיפור ביצועים ממשי בדפים ארוכים.
content-visibility: auto — הכוכב של הסיפור
אם CSS Containment הוא הבסיס, אז content-visibility: auto הוא מה שבאמת עושה את ההבדל. התכונה הזו אומרת לדפדפן: "אל תרנדר את התוכן הזה עד שהמשתמש מתקרב אליו." התוצאה? הדפדפן דולג על עבודת רינדור של אלמנטים שאף אחד עדיין לא רואה.
והחדשות הממש טובות: נכון ל-2025, content-visibility נתמך בכל שלושת מנועי הדפדפנים המרכזיים — Chromium, Firefox ו-WebKit. אפשר להשתמש בזה בפרודקשן בלי חשש.
בדיקות ביצועים מראות תוצאות שפשוט קשה לא להתרגש מהן:
- שיפור של פי 7 בזמן רינדור ראשוני בדמו של web.dev
- ירידה של 40% בזמן פריסה ורינדור בבדיקות GitHub
- הפחתה ממשית במדדי Style, Layout ו-Paint בבדיקות DebugBear על דף עם כמעט 200 פוסטים
/* Basic usage */
.article-card {
content-visibility: auto;
contain-intrinsic-size: auto 300px;
}
/* More granular approach */
.blog-feed .post {
content-visibility: auto;
contain-intrinsic-size: auto 450px 200px; /* width height */
}
/* Hero section - always render immediately */
.hero-section {
content-visibility: visible;
}
/* Footer - defer completely */
.site-footer {
content-visibility: auto;
contain-intrinsic-size: auto 400px;
}
contain-intrinsic-size — כדי שהגלילה לא תקפוץ
בלי contain-intrinsic-size, אלמנטים עם content-visibility: auto פשוט יקרסו לגובה אפס כשהם מחוץ לחלון התצוגה. זה גורם לפס הגלילה לקפוץ בצורה מעצבנת (ומשתמשים שונאים את זה). הטריק הוא להוסיף את המילה auto לפני הערך — ככה הדפדפן "זוכר" את הגודל האמיתי אחרי שרנדר פעם אחת:
/* The 'auto' keyword remembers the real size after first render */
.lazy-section {
content-visibility: auto;
/* auto + fallback size: browser remembers actual size once rendered */
contain-intrinsic-size: auto 500px;
}
/* For a grid of cards with known dimensions */
.product-grid .card {
content-visibility: auto;
contain-intrinsic-size: auto 320px 280px;
}
מתי לא להשתמש ב-content-visibility
חשוב לי להדגיש — התכונה הזו היא לא כדור כסף. הנה מקרים שבהם עדיף להימנע:
- אלמנטים בחלון התצוגה הראשוני: לעולם אל תשתמשו ב-
content-visibility: autoעל תוכן שנראה מיד. זה כמו lazy loading לתמונת ה-LCP — פשוט לא עושים את זה. - אלמנטים קטנים: העלות של ניהול ה-containment עלולה להיות גבוהה מהחיסכון ברינדור.
- תוכן עם אנימציות קריטיות: content-visibility עלול להפריע לאנימציות שאמורות לרוץ ברקע.
- אלמנטים עם גובה דינמי: אם הגובה משתנה תדיר,
contain-intrinsic-sizeלא באמת יעזור.
חלק 3: אופטימיזציית Web Fonts — הגורם הנסתר ל-CLS ול-LCP
הבעיה: FOIT, FOUT וקפיצות שמשגעות
פונטים מותאמים אישית הם חלק בלתי נפרד מעיצוב מודרני, אבל (ותמיד יש "אבל") הם מגיעים עם עלות ביצועית לא מבוטלת. כשהדפדפן נתקל ב-@font-face שמפנה לפונט חיצוני, הוא צריך להוריד את הקובץ לפני שיכול להציג טקסט בפונט המבוקש. וזה מוביל לשתי תופעות בעייתיות:
- FOIT (Flash of Invisible Text): הדפדפן מסתיר את הטקסט עד שהפונט נטען. המשתמש רואה חלל ריק — וזה פוגע ישירות ב-LCP כי טקסט בלתי נראה לא נחשב כתוכן שנצבע.
- FOUT (Flash of Unstyled Text): הדפדפן מציג פונט חלופי ואז מחליף כשהפונט המבוקש מגיע — מה שגורם לקפיצת פריסה שמשפיעה על CLS.
25% מכל קפיצות הפריסה באתרים נגרמות מהחלפת פונטים. זה מספר עצום. וזה מספר שאפשר לצמצם דרמטית.
font-display — קו ההגנה הראשון
התכונה font-display ב-@font-face שולטת באיך הדפדפן מתנהג בזמן שהפונט נטען:
/* swap: Show fallback immediately, swap when ready */
/* Good for body text - prevents FOIT, but may cause CLS */
@font-face {
font-family: 'Rubik';
src: url('/fonts/rubik-regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
/* optional: Use font only if already cached */
/* Best for non-critical fonts - zero CLS, zero FOIT */
@font-face {
font-family: 'Decorative';
src: url('/fonts/decorative.woff2') format('woff2');
font-weight: 400;
font-display: optional;
}
/* fallback: Short block period, then swap */
/* Good compromise between swap and optional */
@font-face {
font-family: 'Heading';
src: url('/fonts/heading-bold.woff2') format('woff2');
font-weight: 700;
font-display: fallback;
}
ההמלצה שלי אחרי עבודה עם עשרות אתרים: השתמשו ב-font-display: swap לפונטים קריטיים (כותרות, טקסט גוף) וב-font-display: optional לפונטים דקורטיביים. עבור אתרים בעברית, swap הוא כמעט תמיד הבחירה הנכונה — משתמשים צריכים לראות טקסט מיד.
טעינה מוקדמת וארחוס עצמי של פונטים
דבר שחשוב להבין: פונט נטען רק כשהדפדפן נתקל באלמנט שמשתמש בו דרך font-family. זה אומר שהטעינה מתחילה מאוחר יחסית בתהליך. הפתרון? preload.
<head>
<!-- Preload critical fonts -->
<link rel="preload" href="/fonts/rubik-regular.woff2"
as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/rubik-bold.woff2"
as="font" type="font/woff2" crossorigin>
<!-- Don't preload non-critical fonts -->
<!-- decorative.woff2 will load naturally when needed -->
</head>
טיפ חשוב: תמיד הוסיפו crossorigin ל-preload של פונטים, גם כשהם באותו דומיין. בלי זה, הדפדפן יוריד את הפונט פעמיים. כן, באמת. למדתי את זה בדרך הקשה.
ובנוסף — ארחוס עצמי (self-hosting) של פונטים כמעט תמיד עדיף על Google Fonts. הסיבות:
- ביטול חיבור חיצוני: חוסכים DNS lookup, TCP handshake ו-TLS negotiation לשרת חיצוני.
- שליטה מלאה ב-caching: אתם קובעים את ה-Cache-Control headers.
- פרטיות: אין שליחת IP של המשתמשים לשרתי גוגל.
- אמינות: האתר שלכם לא נפגע אם שירות חיצוני נופל.
# Download Google Fonts for self-hosting
# Using google-webfonts-helper or fontsource
npm install @fontsource/rubik
# Or manually download and convert
# Always prioritize WOFF2 (30% smaller than WOFF)
# WOFF2 has 98%+ browser support in 2026
Subsetting — לגזור את מה שלא צריך
רוב קובצי הפונטים מגיעים עם אלפי גליפים שלעולם לא תשתמשו בהם. פונט עברי טיפוסי? הוא כולל גם תווים ערביים, יוונים, קיריליים ועוד. Subsetting מאפשר ליצור גרסה "רזה" שכוללת רק מה שבאמת צריך:
# Install pyftsubset (part of fonttools)
pip install fonttools brotli
# Create a Hebrew + Latin subset
pyftsubset Rubik-Regular.ttf \
--output-file=rubik-subset.woff2 \
--flavor=woff2 \
--layout-features='kern,liga,calt' \
--unicodes="U+0000-00FF,U+0590-05FF,U+FB1D-FB4F,U+2000-206F,U+2190-21FF"
# Result: 15KB instead of 80KB — an 80% reduction!
הטווחים בדוגמה כוללים ASCII בסיסי, עברית, צורות עברית נוספות, סימני פיסוק מיוחדים וחצים. התוצאה? מ-80KB ל-15KB. הפחתה של 80% — וזה מורגש.
גישה משלימה היא שימוש ב-unicode-range ב-@font-face, כך שהדפדפן יוריד רק את ה-subset שרלוונטי לתוכן בפועל:
/* Hebrew characters only */
@font-face {
font-family: 'Rubik';
src: url('/fonts/rubik-hebrew.woff2') format('woff2');
font-display: swap;
unicode-range: U+0590-05FF, U+FB1D-FB4F, U+200C-200D;
}
/* Latin characters */
@font-face {
font-family: 'Rubik';
src: url('/fonts/rubik-latin.woff2') format('woff2');
font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+0152-0153;
}
Font Metric Overrides — הנשק הסודי נגד CLS
זו אולי הטכניקה הכי חשובה במדריך הזה, ולדעתי גם הכי מוערכת-בחסר. גם עם font-display: swap, ההחלפה מפונט חלופי לפונט מותאם גורמת לקפיצת פריסה — אלא אם שני הפונטים תופסים בדיוק אותו מקום. וכאן נכנסים ה-Font Metric Overrides:
/* Define a tuned fallback font that matches Rubik's metrics */
@font-face {
font-family: 'Rubik Fallback';
src: local('Arial');
/* These values are calculated to match Rubik's metrics */
size-adjust: 107%;
ascent-override: 88%;
descent-override: 22%;
line-gap-override: 0%;
}
/* Use the fallback stack */
body {
font-family: 'Rubik', 'Rubik Fallback', system-ui, sans-serif;
}
המאפיינים האלה מכווננים את הפונט החלופי כך שיתפוס את אותו מרחב כמו הפונט המותאם:
- size-adjust: גודל יחסי של הפונט החלופי.
- ascent-override: גובה מעל קו הבסיס.
- descent-override: עומק מתחת לקו הבסיס.
- line-gap-override: מרווח בין שורות.
טכניקה זו יכולה להפחית CLS מהחלפת פונטים בעד 70%. חברת Duda, למשל, דיווחה על שיפור של 30% בציוני CLS רק מהוספת font metric overrides. זה שווה כל רגע שמשקיעים בכיול הערכים.
וכדי שלא תצטרכו לחשב את הערכים ידנית, הנה כלים שעושים את העבודה:
- Fontaine: ספרייה שמייצרת fallback fonts אוטומטית עם metric overrides.
- Capsize: כלי לניתוח מטריקות פונטים וייצור CSS מתאים.
- Next.js ו-Nuxt.js: שניהם כוללים תמיכה מובנית, אז אם אתם עובדים עם אחד מהם — פשוט תפעילו את זה.
# Using Fontaine for automatic fallback generation
npm install fontaine
# In your build config (Vite example)
import { FontaineTransform } from 'fontaine';
export default defineConfig({
plugins: [
FontaineTransform.vite({
fallbacks: ['Arial', 'Helvetica'],
resolvePath: (id) => new URL(id, import.meta.url),
}),
],
});
חלק 4: סלקטורים יעילים וארכיטקטורת CSS מודרנית
העלות הנסתרת של סלקטורים מורכבים
דבר שהרבה מפתחים לא יודעים: הדפדפן מעבד סלקטורים מימין לשמאל. הסלקטור .main .sidebar ul li a קודם כל מוצא את כל תגיות a בדף, ורק אז בודק אם כל אחת מהן עומדת בשאר הקריטריונים. בדף עם מאות אלמנטים, זה עלול לעלות ביוקר.
/* Expensive selectors - avoid */
.main .content .article .section .list li a.link { }
div > div > div > span { }
[class*="btn-"] { }
:nth-child(3n+2) > .card:not(.featured) { }
/* Efficient selectors - prefer */
.nav-link { }
.card--featured { }
.article-heading { }
[data-component="sidebar"] { }
בואו נהיה כנים: באתרים קטנים ההבדל הוא זניח. אבל באפליקציות גדולות עם אלפי אלמנטים ומאות כללי CSS? ההשפעה המצטברת על Style Recalculation משמעותית, במיוחד כשזה קורה בתגובה לאינטראקציה ומשפיע ישירות על INP.
CSS Layers (@layer) — סוף למלחמות ספציפיות
ב-2026, @layer הוא כבר לא ניסיוני אלא כלי סטנדרטי. במקום להילחם עם ספציפיות סלקטורים ולזרוק !important בכל מקום (כולנו היינו שם), אפשר להגדיר שכבות עם סדר עדיפויות ברור:
/* Define layer order explicitly */
@layer reset, base, components, utilities;
/* Reset layer - lowest priority */
@layer reset {
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
}
/* Base typography and colors */
@layer base {
body {
font-family: 'Rubik', system-ui, sans-serif;
line-height: 1.6;
color: #1a1a2e;
}
h1, h2, h3 { line-height: 1.2; }
}
/* Component styles */
@layer components {
.card {
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.btn {
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 600;
}
}
/* Utilities - highest priority */
@layer utilities {
.sr-only { position: absolute; clip: rect(0,0,0,0); }
.text-center { text-align: center; }
.mt-4 { margin-top: 1rem; }
}
היתרון לביצועים: כשלא צריך לנפח סלקטורים כדי "לנצח" ספציפיות, כותבים סלקטורים פשוטים ויעילים יותר. ולא במקרה — Tailwind CSS v4 בנוי מעל @layer, וזה אחד הגורמים לשיפור פי 5 בביצועי הבנייה לעומת גרסה 3.
הסרת CSS שלא משמש
אותם 40% של CSS לא מנוצל מ-Web Almanac? זה משקל שגם חוסם רינדור וגם מאט חישובי סגנון. הנה איך נפטרים ממנו:
# PurgeCSS - standalone tool
npm install purgecss --save-dev
# Create purgecss.config.js
module.exports = {
content: ['./src/**/*.html', './src/**/*.js', './src/**/*.jsx'],
css: ['./src/css/**/*.css'],
// Safelist dynamic classes
safelist: ['active', 'open', /^data-/],
};
# Run PurgeCSS
npx purgecss --config purgecss.config.js --output ./dist/css/
מילת אזהרה: כלים כאלה עובדים על ניתוח סטטי. אם יש לכם קלאסים שנוצרים דינמית (כמו class="status-" + status), חובה להוסיף אותם ל-safelist. אחרת תגלו בפרודקשן שחצי מהסטיילים נעלמו.
חלק 5: תכונות CSS מודרניות שמחליפות JavaScript
:has() — הסלקטור שמשנה את הכללים
הסלקטור :has() הוא, לדעתי, אחד מהשינויים המשמעותיים ביותר ב-CSS בשנים האחרונות. הוא מאפשר לבחור אלמנט הורה על סמך הילדים שלו — דבר שעד לא מזמן חייב JavaScript. פחות JavaScript על ה-main thread? INP טוב יותר.
/* Style form groups based on input state - no JS needed */
.form-group:has(input:invalid) {
border-color: #dc3545;
}
.form-group:has(input:focus) {
border-color: #1a73e8;
box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.15);
}
/* Show/hide elements based on checkbox state */
.filters:has(#show-advanced:checked) .advanced-options {
display: block;
}
/* Navigation style based on content */
.page:has(.hero-banner) .nav {
background: transparent;
position: absolute;
}
לפי Web Almanac 2024, החלפת פיצ׳רים מ-JavaScript ל-CSS טהור (כולל :has()) הביאה לשיפור של 20-30% ב-First Contentful Paint. לא רע בכלל עבור שינוי שלרוב דורש פחות קוד.
View Transitions API — מעברים חלקים בלי ספריות
במקום לגרור ספריות JavaScript כבדות בשביל אנימציות מעבר דף, ה-View Transitions API מאפשר ליצור מעברים חלקים עם CSS בלבד:
/* Enable view transitions */
@view-transition {
navigation: auto;
}
/* Customize the transition animation */
::view-transition-old(root) {
animation: fade-out 0.3s ease-out;
}
::view-transition-new(root) {
animation: fade-in 0.3s ease-in;
}
/* Named transitions for specific elements */
.article-hero {
view-transition-name: hero-image;
}
::view-transition-old(hero-image) {
animation: scale-down 0.4s ease-out;
}
::view-transition-new(hero-image) {
animation: scale-up 0.4s ease-in;
}
היתרון כאן כפול: חוסכים את הטעינה וההרצה של ספריית אנימציה, והדפדפן מנהל את האנימציות על ה-compositor thread במקום על ה-main thread. תרגום: אנימציות חלקות שלא פוגעות ב-INP.
חלק 6: כלים למדידה ומעקב
Chrome DevTools — מה שכל מפתח צריך לדעת
Chrome DevTools מציע כלים מעולים לניתוח ביצועי CSS, ושווה להכיר אותם לעומק:
- Coverage tab: מראה בדיוק כמה CSS בפועל בשימוש. פתחו עם Ctrl+Shift+P ← "Coverage" ותופתעו (או תיבהלו).
- Performance tab: עקבו אחר זמני Style Recalculation ו-Layout. מספרים גבוהים? סימן שיש בעיה עם הסלקטורים.
- Rendering tab: הפעילו "Paint flashing" כדי לראות אלו אזורים נצבעים מחדש. צביעה מיותרת = בזבוז משאבים.
- Lighthouse: מזהה CSS חוסם רינדור, בעיות פונטים ובעיות CLS במכה אחת.
ביקורות אוטומטיות ב-CI
אל תחכו לפרודקשן כדי לגלות בעיות. שלבו בדיקות ביצועים כחלק מה-CI:
// lighthouse-ci config for CSS performance checks
// lighthouserc.js
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000'],
numberOfRuns: 3,
},
assert: {
assertions: {
'render-blocking-resources': ['error', { maxLength: 0 }],
'unused-css-rules': ['warn', { maxLength: 1 }],
'font-display': ['error', { minScore: 1 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
'largest-contentful-paint': ['warn', { maxNumericValue: 2500 }],
},
},
},
};
# Add to CI pipeline (GitHub Actions example)
# .github/workflows/perf.yml
name: Performance Audit
on: [pull_request]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci && npm run build
- run: npm start & npx wait-on http://localhost:3000
- run: npx @lhci/cli autorun
חלק 7: רשימת בדיקה מעשית
לסיום, הנה רשימת בדיקה שאני משתמש בה בכל פרויקט. שמרו אותה.
CSS
- Critical CSS מוטמע? — CSS קריטי ב-
<style>inline, שאר ה-CSS נטען אסינכרונית. - CSS לא מנוצל הוסר? — PurgeCSS או Coverage tab של DevTools.
- content-visibility: auto מוגדר לאלמנטים מחוץ לחלון התצוגה (עם
contain-intrinsic-size). - Media queries בתגיות link — קובצי CSS לדסקטופ והדפסה מופרדים עם
mediaattribute. - סלקטורים פשוטים — בלי קינון עמוק או סלקטורים אוניברסליים יקרים.
- @layer מוגדר — ארכיטקטורת שכבות ברורה.
- CSS ממוזער ודחוס — מינימום bytes ברשת.
פונטים
- WOFF2 בלבד — ב-2026 אין סיבה להגיש פורמט אחר (98%+ תמיכה).
- font-display מוגדר —
swapלקריטיים,optionalלדקורטיביים. - Preload לפונטים קריטיים — עד 2 פונטים עם
rel="preload"(לא יותר). - Self-hosting — פונטים על השרת שלכם, לא משירות חיצוני.
- Subsetting — רק התווים שצריך (עברית + לטינית בסיסית).
- Font Metric Overrides — פונט חלופי מכוייל למניעת CLS.
- unicode-range — חלוקה לתתי-קבצים לפי שפה.
סיכום: ההשפעה המצטברת על Core Web Vitals
אז מה יוצא מכל זה? בואו נסכם את התמונה המלאה:
- LCP: Critical CSS inline + preload פונטים + content-visibility = שיפור של 30-50% בזמן הרינדור הראשוני.
- CLS: Font metric overrides + contain-intrinsic-size = הפחתה של עד 70% בקפיצות פריסה.
- INP: סלקטורים יעילים + החלפת JS ב-CSS (
:has(), View Transitions) + content-visibility = main thread פנוי יותר = תגובתיות טובה יותר.
אופטימיזציית CSS ופונטים היא לא פרויקט חד-פעמי שעושים ושוכחים. זה תהליך מתמשך שצריך להיות חלק מה-workflow היומיומי שלכם — בדיקות אוטומטיות ב-CI, מדידה של כל שינוי, ונתונים שמנחים החלטות.
אם יישמתם את כל הטכניקות מסדרת המאמרים שלנו — תמונות, JavaScript, שרת ו-CDN, ועכשיו CSS ופונטים — יש לכם ארגז כלים מלא לבניית אתרים מהירים שעומדים בסף Core Web Vitals. במאמר הבא נדבר על ניטור ביצועים בפרודקשן ו-Real User Monitoring, כי בסוף — מה שלא מודדים, לא משפרים.