לדלג לתוכן

1.7 - עיצוב רספונסיבי - הרצאה

מה זה עיצוב רספונסיבי - Responsive Design

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

למה זה חשוב? כי היום יותר מ-60% מהתנועה באינטרנט מגיעה ממכשירים ניידים. אם האתר שלכם לא נראה טוב בטלפון - איבדתם את רוב הקהל.

בנוסף, גוגל מדרג אתרים לפי הביצועים שלהם בנייד (mobile-first indexing). אתר לא רספונסיבי פשוט לא יופיע בתוצאות חיפוש גבוהות.

הtag viewport

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

הtag הזו צריכה להיות בכל עמוד HTML:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

מה היא עושה:
- width=device-width - רוחב העמוד יהיה לפי רוחב המכשיר האמיתי
- initial-scale=1.0 - מונע זום אוטומטי

בלי הtag הזו, כל ה-CSS הרספונסיבי שנכתוב פשוט לא יעבוד כמו שצריך במובייל.

גישת Mobile-First מול Desktop-First

יש שתי גישות לכתיבת CSS רספונסיבי:

גישת Desktop-First

כותבים את ה-CSS לדסקטופ קודם, ואז משתמשים ב-max-width כדי להתאים למסכים קטנים:

/* default: desktop styles */
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

/* tablet and below */
@media (max-width: 768px) {
  .container {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* mobile */
@media (max-width: 480px) {
  .container {
    grid-template-columns: 1fr;
  }
}

גישת Mobile-First (מומלצת)

כותבים את ה-CSS למובייל קודם, ואז משתמשים ב-min-width כדי להוסיף סגנונות למסכים גדולים:

/* default: mobile styles */
.container {
  display: grid;
  grid-template-columns: 1fr;
}

/* tablet and above */
@media (min-width: 768px) {
  .container {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* desktop */
@media (min-width: 1024px) {
  .container {
    grid-template-columns: repeat(3, 1fr);
  }
}

למה mobile-first עדיפה?
- מובייל הוא המקרה הפשוט ביותר (עמודה אחת, פחות מרכיבים מורכבים)
- קל יותר להוסיף מורכבות מאשר לגרוע אותה
- מתאים לאיך שגוגל מדרג אתרים
- מאלץ אותנו לחשוב על מה באמת חשוב באתר

איור המראה כיצד בגישת mobile-first הבסיס הוא עמודה אחת וכל שאילתת min-width שהתנאי שלה מתקיים דורסת את הכללים הקודמים ומוסיפה עמודות ככל שהמסך מתרחב

שאילתות מדיה - Media Queries

שאילתות מדיה מאפשרות להחיל CSS שונה בהתאם לתנאים - בדרך כלל רוחב המסך.

תחביר בסיסי

@media (min-width: 768px) {
  /* styles applied when screen is 768px or wider */
  .sidebar {
    display: block;
  }
}

שילוב תנאים

/* between 600px and 1024px */
@media (min-width: 600px) and (max-width: 1024px) {
  .container {
    padding: 20px;
  }
}

/* portrait orientation */
@media (orientation: portrait) {
  .hero {
    height: 50vh;
  }
}

נקודות שבירה נפוצות - breakpoints

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

/* mobile (small) */
/* up to 599px - default mobile-first styles, no media query needed */

/* tablet */
@media (min-width: 600px) { }

/* laptop */
@media (min-width: 768px) { }

/* desktop */
@media (min-width: 1024px) { }

/* large desktop */
@media (min-width: 1440px) { }

הטיפ הכי חשוב: אל תבחרו breakpoints לפי מכשירים ספציפיים. בחרו breakpoints לפי איפה ה-layout שלכם "נשבר" - כלומר איפה העיצוב מפסיק להיראות טוב.

שילוב media queries עם פלקסבוקס וגריד

דוגמה עם פלקסבוקס

.nav {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

@media (min-width: 768px) {
  .nav {
    flex-direction: row;
    justify-content: space-between;
  }
}

במובייל - תפריט אנכי. בדסקטופ - תפריט אופקי.

דוגמה עם גריד

.page {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-areas:
    "header"
    "main"
    "sidebar"
    "footer";
}

@media (min-width: 768px) {
  .page {
    grid-template-columns: 250px 1fr;
    grid-template-areas:
      "header  header"
      "sidebar main"
      "footer  footer";
  }
}

ה-layout כולו משתנה - במובייל הכל בעמודה אחת, בדסקטופ sidebar + main.

תמונות רספונסיביות

תמונות צריכות טיפול מיוחד כדי להיראות טוב בכל מסך.

הבסיס - max-width

img {
  max-width: 100%;
  height: auto;
}

זה הכלל הכי חשוב - התמונה לא תהיה רחבה יותר מהמיכל שלה, ותשמור על היחס.

הtag picture - תמונות שונות לגדלים שונים

<picture>
  <source media="(min-width: 1024px)" srcset="hero-large.jpg">
  <source media="(min-width: 600px)" srcset="hero-medium.jpg">
  <img src="hero-small.jpg" alt="hero image">
</picture>

הדפדפן יטען את התמונה המתאימה לגודל המסך - מה שחוסך bandwidth במובייל.

תכונת srcset

<img
  src="photo-400.jpg"
  srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
  sizes="(min-width: 1024px) 33vw, (min-width: 600px) 50vw, 100vw"
  alt="responsive photo"
>
  • srcset - רשימת תמונות עם הרוחב שלהן
  • sizes - אומר לדפדפן מה יהיה רוחב התמונה בתצוגה
  • הדפדפן בוחר אוטומטית את התמונה הכי מתאימה

טיפוגרפיה רספונסיבית

הבעיה

גודל פונט קבוע לא עובד בכל המסכים. כותרת של 48px מושלמת בדסקטופ אבל עצומה בטלפון.

הפתרון הישן - media queries

h1 {
  font-size: 24px;
}

@media (min-width: 768px) {
  h1 {
    font-size: 36px;
  }
}

@media (min-width: 1024px) {
  h1 {
    font-size: 48px;
  }
}

הפתרון המודרני - clamp

h1 {
  font-size: clamp(1.5rem, 4vw, 3rem);
}

clamp(minimum, preferred, maximum) - הפונט יגדל ויקטן בצורה חלקה:
- לא יהיה קטן מ-1.5rem (24px)
- ינסה להיות 4vw (4% מרוחב המסך)
- לא יהיה גדול מ-3rem (48px)

שורה אחת של CSS במקום 3 media queries, והתוצאה חלקה יותר.

איור המראה כיצד clamp שומר את גודל הפונט בין מינימום למקסימום, וכשהערך המועדף 4vw קטן מהמינימום או גדול מהמקסימום הגבול הוא שקובע

מערכת טיפוגרפיה רספונסיבית שלמה

:root {
  --text-xs: clamp(0.75rem, 1.5vw, 0.875rem);
  --text-sm: clamp(0.875rem, 1.8vw, 1rem);
  --text-base: clamp(1rem, 2vw, 1.125rem);
  --text-lg: clamp(1.125rem, 2.5vw, 1.5rem);
  --text-xl: clamp(1.5rem, 3vw, 2rem);
  --text-2xl: clamp(1.75rem, 4vw, 2.5rem);
  --text-3xl: clamp(2rem, 5vw, 3.5rem);
}

h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl); }
p  { font-size: var(--text-base); }

שאילתות מיכל - Container Queries

Container queries הם פיצ'ר חדש יחסית שמאפשר לעצב component לפי גודל המיכל שלו, ולא לפי גודל המסך.

.card-container {
  container-type: inline-size;
  container-name: card;
}

@container card (min-width: 400px) {
  .card {
    display: flex;
    flex-direction: row;
  }
}

@container card (max-width: 399px) {
  .card {
    display: flex;
    flex-direction: column;
  }
}

למה זה שימושי? כי media queries מסתכלות על גודל המסך כולו. אבל אם יש כרטיס שמופיע גם ב-sidebar צר וגם ב-main רחב - הוא צריך להתנהג אחרת בכל מקום, גם באותו גודל מסך.

container queries פותרות בדיוק את הבעיה הזו - הcomponent מתאים את עצמו לפי הגודל של המיכל שלו.

תבנית תפריט המבורגר - Hamburger Menu

בדסקטופ הניווט מוצג אופקית. במובייל, הניווט מוחלף בכפתור (שלושה קווים) שפותח תפריט.

המבנה הבסיסי ב-CSS בלבד (בלי JavaScript):

.nav-links {
  display: none;
}

.hamburger {
  display: block;
  font-size: 1.5rem;
  background: none;
  border: none;
  cursor: pointer;
}

@media (min-width: 768px) {
  .nav-links {
    display: flex;
    gap: 20px;
  }

  .hamburger {
    display: none;
  }
}

הכפתור מוצג רק במובייל, והקישורים רק בדסקטופ.
כדי שהכפתור ממש יפתח ויסגור את התפריט - צריך JavaScript (נלמד בהמשך).
אבל אפשר גם לעשות את זה עם טריק של checkbox ב-CSS בלבד:

<nav>
  <input type="checkbox" id="menu-toggle" class="menu-checkbox">
  <label for="menu-toggle" class="hamburger">&#9776;</label>
  <ul class="nav-links">
    <li><a href="#">home</a></li>
    <li><a href="#">about</a></li>
    <li><a href="#">contact</a></li>
  </ul>
</nav>
.menu-checkbox {
  display: none;
}

.nav-links {
  display: none;
  flex-direction: column;
  gap: 10px;
}

.menu-checkbox:checked ~ .nav-links {
  display: flex;
}

@media (min-width: 768px) {
  .hamburger {
    display: none;
  }

  .nav-links {
    display: flex;
    flex-direction: row;
  }
}

הטריק: כשלוחצים על ה-label, ה-checkbox נבחר, והסלקטור ~ מפעיל את הצגת התפריט.

טבלאות רספונסיביות

טבלאות הן אחד הדברים הקשים ביותר להפוך לרספונסיביים כי הן מעצם טבען רחבות.

אסטרטגיה 1 - גלילה אופקית

.table-wrapper {
  overflow-x: auto;
}

table {
  min-width: 600px;
  width: 100%;
}

פשוט עוטפים את הטבלה ב-div עם overflow-x: auto. אם הטבלה רחבה מהמסך, תהיה גלילה אופקית.

אסטרטגיה 2 - שינוי layout

@media (max-width: 600px) {
  table, thead, tbody, tr, th, td {
    display: block;
  }

  thead {
    display: none;
  }

  td {
    padding: 8px;
    text-align: right;
  }

  td::before {
    content: attr(data-label);
    float: left;
    font-weight: bold;
  }
}
<tr>
  <td data-label="Name">John</td>
  <td data-label="Email">john@example.com</td>
  <td data-label="Phone">050-1234567</td>
</tr>

במובייל, כל שורה הופכת לכרטיס - כל תא בשורה נפרדת עם label.

תבניות ניווט רספונסיביות

ניווט אופקי שנהפך לאנכי

.nav {
  display: flex;
  flex-direction: column;
  background: #333;
}

.nav a {
  color: white;
  padding: 15px 20px;
  text-decoration: none;
}

@media (min-width: 768px) {
  .nav {
    flex-direction: row;
  }

  .nav a {
    padding: 15px 25px;
  }
}

ניווט עם לוגו ותפריט

.navbar {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  padding: 10px 20px;
  background: #1a1a2e;
}

.logo {
  color: white;
  font-size: 1.5rem;
  font-weight: bold;
}

.nav-links {
  display: flex;
  gap: 20px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-links a {
  color: white;
  text-decoration: none;
}

@media (max-width: 768px) {
  .nav-links {
    flex-basis: 100%;
    flex-direction: column;
    gap: 10px;
    padding-top: 15px;
  }
}

בדיקה ב-DevTools Device Mode

כלי הפיתוח של הדפדפן מאפשרים לבדוק איך האתר נראה בגדלי מסך שונים.

כדי לפתוח Device Mode:
- לחצו F12 או Ctrl + Shift + I כדי לפתוח את DevTools
- לחצו על אייקון הטלפון/טאבלט (או Ctrl + Shift + M)
- בחרו מכשיר מהרשימה, או גררו את הידיות כדי לשנות גודל

מה שימושי ב-Device Mode:
- בדיקת breakpoints שונים
- סימולציה של מכשירים ספציפיים (iPhone, iPad, Pixel)
- בדיקת touch events
- בדיקת performance בסימולציה של מעבד איטי

טיפ: תמיד בדקו את האתר גם בטלפון אמיתי. הסימולציה של DevTools טובה אבל לא מושלמת.

יחידות CSS לעיצוב רספונסיבי

אחוזים - %

יחסי לאלמנט ההורה:

.child {
  width: 50%; /* half of parent's width */
}

יחידות viewport - vw, vh

.hero {
  height: 100vh; /* full viewport height */
  width: 100vw;  /* full viewport width */
}

.title {
  font-size: 5vw; /* 5% of viewport width */
}

rem

יחסי לגודל הפונט של html (ברירת מחדל 16px):

html {
  font-size: 16px;
}

h1 {
  font-size: 2rem; /* 32px */
  margin-bottom: 1.5rem; /* 24px */
}

rem מצוין לריווחים ולפונטים - הכל מתנהג באופן עקבי ואפשר לשנות את הגודל הבסיסי ב-media query.

הפונקציה clamp

.container {
  width: clamp(300px, 80%, 1200px);
  /* minimum 300px, prefers 80%, maximum 1200px */

  padding: clamp(10px, 3vw, 40px);
  /* minimum 10px, prefers 3vw, maximum 40px */
}

clamp() הוא הכלי הכי טוב לרספונסיביות - ערך אחד שמתאים לכל הגדלים.

min ו-max

.container {
  width: min(90%, 1200px);
  /* the smaller of 90% and 1200px */
}

.sidebar {
  width: max(200px, 20%);
  /* the larger of 200px and 20% */
}

משתני CSS לערכים רספונסיביים

אפשר לשלב CSS variables עם media queries ליצירת מערכת רספונסיבית מאורגנת:

:root {
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --container-width: 100%;
  --columns: 1;
}

@media (min-width: 768px) {
  :root {
    --spacing-sm: 12px;
    --spacing-md: 24px;
    --spacing-lg: 40px;
    --container-width: 750px;
    --columns: 2;
  }
}

@media (min-width: 1024px) {
  :root {
    --spacing-lg: 60px;
    --container-width: 960px;
    --columns: 3;
  }
}

.container {
  max-width: var(--container-width);
  margin: 0 auto;
  padding: var(--spacing-md);
}

.grid {
  display: grid;
  grid-template-columns: repeat(var(--columns), 1fr);
  gap: var(--spacing-md);
}

section {
  padding: var(--spacing-lg) 0;
}

היתרון: כל הערכים הרספונסיביים מוגדרים במקום אחד. קל לשנות ולתחזק.

סיכום

הנקודות החשובות ביותר בעיצוב רספונסיבי:
- תמיד הוסיפו את tag viewport ב-HTML
- העדיפו גישת mobile-first - כתבו CSS למובייל קודם, והוסיפו min-width media queries
- השתמשו ב-clamp() לטיפוגרפיה ולמרחקים רספונסיביים
- הגדירו max-width: 100% על תמונות
- השתמשו ביחידות יחסיות (%, rem, vw) במקום פיקסלים קבועים
- בדקו את האתר ב-DevTools Device Mode ובמכשירים אמיתיים
- container queries הם הפיצ'ר הבא - מאפשרים לcomponents להתאים למיכל שלהם
- שלבו CSS variables עם media queries למערכת רספונסיבית מאורגנת