השווה & השוואת כיתוב

למאפיין lang יכולה להיות משויכת שפה אחת בלבד. כלומר, למאפיין <html> יכולה להיות רק שפה אחת, גם אם יש בדף כמה שפות. מגדירים את lang כשפה הראשית של הדף.

מה אסור לעשות
<html lang="ar,en,fr,pt">...</html>
אין תמיכה במספר שפות.
מה מותר לעשות
<html lang="ar">...</html>
הגדרת השפה הראשית בלבד של הדף. במקרה הזה, השפה היא ערבית.

בדומה ללחצנים, קישורים מקבלים את השם הנגיש בעיקר מתוכן הטקסט. טריק נחמד ליצירת קישור הוא להוסיף את קטע הטקסט המשמעותי ביותר לקישור עצמו, במקום מילות מילוי כמו "כאן" או "קרא עוד".

לא תיאורי מספיק
Check out our guide to web performance <a href="/guide">here</a>.
תוכן שימושי!
Check out <a href="/guide">our guide to web performance</a>.

איך בודקים אם אנימציה מפעילה את הפריסה

אנימציה שמזיזה רכיב באמצעות רכיב שאינו transform, צפויה להיות איטית. בדוגמה הבאה הגעתי לאותה תוצאה חזותית עם אנימציה של top ו-left, ומשתמש ב-transform.

מה אסור לעשות
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     top: calc(90vh - 160px);
     left: calc(90vw - 200px);
  }
}
מה מותר לעשות
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     transform: translate(calc(90vw - 200px), calc(90vh - 160px));
  }
}

אפשר לבדוק זאת בעזרת שתי הדוגמאות הבאות של Glitch ולחקור את הביצועים באמצעות כלי פיתוח.

עם אותו תגי עיצוב, נוכל להחליף את: padding-top: 56.25% ב-aspect-ratio: 16 / 9, ולהגדיר את aspect-ratio ליחס קבוע של width ל-height.

שימוש במרווח פנימי למעלה
.container {
  width: 100%;
  padding-top: 56.25%;
}
שימוש ביחס גובה-רוחב
.container {
  width: 100%;
  aspect-ratio: 16 / 9;
}

השימוש ב-aspect-ratio במקום ב-padding-top הרבה יותר ברור, ולא משנה את מאפיין המרווח הפנימי כדי לבצע פעולות שחורגות מההיקף הרגיל שלו.

כן, נכון, אני משתמשת ב-reduce כדי לשרשר רצף של הבטחות. אני כל כך חכם. אבל זה תכנות קצת כל כך חכם שעדיף בלי.

עם זאת, כשממירים את הערכים שלמעלה לפונקציה אסינכרונית, מפתה לעבור לרצף מדי:

לא מומלץ – עוקב מדי
async function logInOrder(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(await response.text());
  }
}
נראה הרבה יותר מסודר, אבל האחזור השני שלי מתחיל רק אחרי הקראה מלאה של האחזור הראשון, וכן הלאה. זה הרבה יותר איטי מדוגמה ההבטחה שמבצעת את האחזורים במקביל. למרבה המזל, יש כאן דרך אמצע אידיאלית.
מומלץ – נחמד ומקביל
function markHandled(...promises) {
  Promise.allSettled(promises);
}

async function logInOrder(urls) {
  // fetch all the URLs in parallel
  const textPromises = urls.map(async (url) => {
    const response = await fetch(url);
    return response.text();
  });

  markHandled(...textPromises);

  // log them in sequence
  for (const textPromise of textPromises) {
    console.log(await textPromise);
  }
}
בדוגמה הזו, כתובות ה-URL מאוחזרות וניתנות לקריאה במקביל, אבל הביט "חכם" reduce מוחלף בביט סטנדרטי, משעמם וקריא ל-לולאה.

כתיבת מאפיינים מותאמים אישית של Houdini

הנה דוגמה להגדרת נכס מותאם אישית (Think: משתנה CSS), אבל עכשיו עם תחביר (type), ערך ראשוני (fallback) וירושה בוליאנית (האם הוא יורש את הערך מההורה שלו או לא?). כרגע יש תמיכה בתחביר CSS.registerProperty() ב-JavaScript, אבל ב-Chromium 85 ואילך יש תמיכה בתחביר @property בקובצי ה-CSS:

קובץ JavaScript נפרד (Chromium 78)
CSS.registerProperty({
  name: '--colorPrimary',
  syntax: '',
  initialValue: 'magenta',
  inherits: false
});
כלול בקובץ CSS (Chromium 85)
@property --colorPrimary {
  syntax: '';
  initial-value: magenta;
  inherits: false;
}

מעכשיו אפשר לגשת אל --colorPrimary כמו לכל נכס CSS מותאם אישית אחר, דרך var(--colorPrimary). עם זאת, ההבדל כאן הוא ש---colorPrimary לא נקרא רק כמחרוזת. יש בו נתונים!

backdrop-filter של CSS מחיל אפקט אחד או יותר על רכיב שקוף או שקוף. כדי להבין את זה, אפשר להיעזר בתמונות הבאות.

ללא שקיפות בחזית
משולש שמופיע מעל עיגול. לא ניתן לראות את העיגול דרך המשולש.
.frosty-glass-pane {
  backdrop-filter: blur(2px);
}
שקיפות בחזית
משולש שמופיע מעל עיגול. המשולש שקוף מאפשר לראות את העיגול באמצעותו.
.frosty-glass-pane {
  opacity: .9;
  backdrop-filter: blur(2px);
}

התמונה שמימין מראה איך רכיבים חופפים יוצגו אם לא ייעשה שימוש ב-backdrop-filter או אם הוא לא נתמך. בתמונה שמשמאל יופעל אפקט טשטוש באמצעות backdrop-filter. לתשומת ליבך, היא משתמשת ב-opacity בנוסף ל-backdrop-filter. בלי opacity לא תהיה שום אפשרות להחיל טשטוש. כמעט מיותר לציין שאם opacity מוגדרת לערך 1 (אטום לחלוטין) לא תהיה כל השפעה על הרקע.

עם זאת, בניגוד לאירוע unload, יש שימושים לגיטימיים ב-beforeunload. לדוגמה, כשרוצים להזהיר את המשתמש על כך שהשינויים שלא נשמרו, הם יאבדו אם הם יעזבו את הדף. במקרה כזה, מומלץ להוסיף פונקציות beforeunload listener רק אם למשתמש יש שינויים שלא נשמרו, ולאחר מכן להסיר אותם מיד לאחר שהשינויים שלא נשמרו נשמרים.

מה אסור לעשות
window.addEventListener('beforeunload', (event) => {
  if (pageHasUnsavedChanges()) {
    event.preventDefault();
    return event.returnValue = 'Are you sure you want to exit?';
  }
});
הקוד שלמעלה מוסיף האזנה ל-beforeunload ללא תנאי.
מה מותר לעשות
function beforeUnloadListener(event) {
  event.preventDefault();
  return event.returnValue = 'Are you sure you want to exit?';
};

// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  window.addEventListener('beforeunload', beforeUnloadListener);
});

// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  window.removeEventListener('beforeunload', beforeUnloadListener);
});
בקוד שלמעלה, הוספת ה-listener מסוג beforeunload מתווספת רק כשצריך (ומסירים אותו כשאין צורך).

צמצום השימוש ב-Cache-Control: no-store

Cache-Control: no-store היא כותרת HTTP ששרתי אינטרנט יכולים להגדיר בתשובות, שמורה לדפדפן לא לאחסן את התגובה במטמון HTTP כלשהו. צריך להשתמש באפשרות הזו למשאבים שמכילים מידע רגיש על המשתמש, לדוגמה, דפים שמחייבים התחברות.

הרכיב fieldset, שמכיל כל קבוצת קלט (.fieldset-item), משתמש ב-gap: 1px כדי ליצור את גבולות הקו השיער בין הרכיבים. אין פתרון גבול מסובך!

פער מלא
.grid {
  display: grid;
  gap: 1px;
  background: var(--bg-surface-1);

  & > .fieldset-item {
    background: var(--bg-surface-2);
  }
}
תעלול גבול
.grid {
  display: grid;

  & > .fieldset-item {
    background: var(--bg-surface-2);

    &:not(:last-child) {
      border-bottom: 1px solid var(--bg-surface-1);
    }
  }
}

גלישת רשת טבעית

הפריסה המורכבת ביותר הייתה פריסת המאקרו, מערכת הפריסה הלוגית שבין <main> ל-<form>.

קלט
<input
  type="checkbox"
  id="text-notifications"
  name="text-notifications"
>
label
<label for="text-notifications">
  <h3>Text Messages</h3>
  <small>Get notified about all text messages sent to your device</small>
</label>

הרכיב fieldset, שמכיל כל קבוצת קלט (.fieldset-item), משתמש ב-gap: 1px כדי ליצור את גבולות הקו השיער בין הרכיבים. אין פתרון גבול מסובך!

פער מלא
.grid {
  display: grid;
  gap: 1px;
  background: var(--bg-surface-1);

  & > .fieldset-item {
    background: var(--bg-surface-2);
  }
}
תעלול גבול
.grid {
  display: grid;

  & > .fieldset-item {
    background: var(--bg-surface-2);

    &:not(:last-child) {
      border-bottom: 1px solid var(--bg-surface-1);
    }
  }
}

פריסה <header> כרטיסיות

הפריסה הבאה כמעט זהה: השיטה גמישה משמשת ליצירת סדר אנכי.

HTML
<snap-tabs>
  <header>
    <nav></nav>
    <span class="snap-indicator"></span>
  </header>
  <section></section>
</snap-tabs>
CSS
header {
  display: flex;
  flex-direction: column;
}

ה-.snap-indicator צריך לעבור בצורה אופקית עם קבוצת הקישורים, והפריסה של הכותרת עוזרת להגדיר את השלב הזה. אין כאן רכיבים ממוקמים מוחלטים!

Gentle Flex היא אסטרטגיית התמקדות בלבד אמיתית. הוא רך ועדין, כי בניגוד ל-place-content: center, לא מתבצע שינוי במידות של קופסאות ילדים בזמן המרכוז. בצורה עדינה ככל האפשר, כל הפריטים מוערמים, ממורכזים ומרווחים.

.gentle-flex {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1ch;
}
יתרונות
  • מטפל רק ביישור, כיוון והתפלגות
  • פעולות עריכה ותחזוקה מרוכזות במקום אחד
  • הפער מבטיח ריווח שווה בין n ילדים
חסרונות
  • רוב שורות הקוד

מעולה גם לפריסות מאקרו וגם לפריסות מיקרו.

Usage

gap יכול לקבל כל length או percentage של CSS.

.gap-example {
  display: grid;
  gap: 10px;
  gap: 2ch;
  gap: 5%;
  gap: 1em;
  gap: 3vmax;
}


אפשר להעביר רווח באורך 1, שישמש גם לשורה וגם לעמודה.

מקוצר
.grid {
  display: grid;
  gap: 10px;
}
הגדרה של שורות ועמודות ביחד בו-זמנית
בוצעה הרחבה
.grid {
  display: grid;
  row-gap: 10px;
  column-gap: 10px;
}


ניתן להעביר פער של שני אורכים, שישמשו כשורה ועמודה.

מקוצר
.grid {
  display: grid;
  gap: 10px 5%;
}
מגדירים גם שורות וגם עמודות בנפרד בבת אחת
בוצעה הרחבה
.grid {
  display: grid;
  row-gap: 10px;
  column-gap: 5%;
}