לדלג לתוכן

8.1 סקירת גישות לעיצוב הרצאה

סקירת גישות לעיצוב - Styling Approaches Overview

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


סגנונות אינליין - Inline Styles

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

תחביר בסיסי

function InlineExample() {
  return (
    <div style={{ backgroundColor: "lightblue", padding: "20px" }}>
      <h1 style={{ color: "navy", fontSize: "24px" }}>כותרת מעוצבת</h1>
      <p style={{ lineHeight: 1.6 }}>טקסט עם רווח בין שורות</p>
    </div>
  );
}
  • שימו לב שמות התכונות הם ב-camelCase ולא ב-kebab-case
  • ערכים מספריים מקבלים אוטומטית px (למעט ערכים כמו lineHeight, opacity, zIndex)
  • ערכי מחרוזת נכתבים במרכאות

שימוש במשתנים וחישובים

function DynamicStyles({ isActive, size }: { isActive: boolean; size: number }) {
  const containerStyle: React.CSSProperties = {
    padding: size * 2,
    backgroundColor: isActive ? "#4CAF50" : "#ccc",
    borderRadius: "8px",
    transition: "background-color 0.3s ease",
  };

  return <div style={containerStyle}>תוכן דינמי</div>;
}
  • הטיפוס React.CSSProperties מספק השלמה אוטומטית ובדיקת טיפוסים
  • סגנונות אינליין מאפשרים שימוש בלוגיקה דינמית בקלות

יתרונות

  • פשוט ומהיר להתחלה
  • סגנונות דינמיים בקלות - אפשר לחשב ערכים על בסיס props או state
  • אין צורך בקבצים נוספים
  • אין בעיות של scope - הסגנון חל רק על האלמנט הספציפי

חסרונות

  • אין תמיכה ב-pseudo-classes כמו hover: ו-focus:
  • אין תמיכה ב-media queries
  • אין תמיכה ב-animations ו-keyframes
  • קוד ארוך ומסורבל כשיש הרבה סגנונות
  • אין שימוש חוזר בקלות
  • ביצועים נמוכים יותר - ריאקט יוצר אובייקט סגנון חדש בכל רנדור

CSS רגיל - Regular CSS

הגישה המסורתית - כתיבת CSS בקבצים נפרדים וייבוא שלהם לקומפוננטה.

דוגמה בסיסית

/* Button.css */
.button {
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
}

.button-primary {
  background-color: #007bff;
  color: white;
}

.button-primary:hover {
  background-color: #0056b3;
}

.button-secondary {
  background-color: #6c757d;
  color: white;
}
// Button.tsx
import "./Button.css";

interface ButtonProps {
  variant?: "primary" | "secondary";
  children: React.ReactNode;
  onClick?: () => void;
}

function Button({ variant = "primary", children, onClick }: ButtonProps) {
  return (
    <button className={`button button-${variant}`} onClick={onClick}>
      {children}
    </button>
  );
}

שימוש ב-clsx לשילוב מחלקות

הספריה clsx מאפשרת לשלב מחלקות CSS בצורה נוחה:

npm install clsx
import clsx from "clsx";
import "./Card.css";

interface CardProps {
  highlighted?: boolean;
  disabled?: boolean;
  size?: "small" | "medium" | "large";
  children: React.ReactNode;
}

function Card({ highlighted, disabled, size = "medium", children }: CardProps) {
  return (
    <div
      className={clsx("card", `card-${size}`, {
        "card-highlighted": highlighted,
        "card-disabled": disabled,
      })}
    >
      {children}
    </div>
  );
}

יתרונות

  • תחביר CSS מוכר ורגיל
  • תמיכה מלאה בכל תכונות CSS כולל pseudo-classes, media queries, animations
  • הפרדה ברורה בין לוגיקה לעיצוב
  • ביצועים טובים - הדפדפן מטפל ב-CSS ישירות

חסרונות

  • סגנונות גלובליים - שמות מחלקות יכולים להתנגש בין קומפוננטות שונות
  • קשה לנהל בפרויקטים גדולים
  • צריך לדאוג לייחודיות שמות (naming conventions כמו BEM)
  • אין קשר ישיר בין הקומפוננטה לסגנונות שלה

מודולי CSS - CSS Modules

פתרון שמשלב את היתרונות של CSS רגיל עם scoping אוטומטי. כל קובץ CSS Module יוצר מרחב שמות ייחודי.

איך זה עובד

/* Card.module.css */
.card {
  padding: 16px;
  border: 1px solid #ddd;
  border-radius: 8px;
}

.title {
  font-size: 20px;
  font-weight: bold;
  margin-bottom: 8px;
}

.content {
  color: #555;
  line-height: 1.6;
}
// Card.tsx
import styles from "./Card.module.css";

function Card({ title, content }: { title: string; content: string }) {
  return (
    <div className={styles.card}>
      <h2 className={styles.title}>{title}</h2>
      <p className={styles.content}>{content}</p>
    </div>
  );
}
  • הסיומת .module.css אומרת ל-Vite ליצור שמות מחלקות ייחודיים
  • בזמן build, המחלקה card הופכת למשל ל-Card_card_x7g3k
  • כך אין התנגשויות בין קומפוננטות שונות

שימוש ב-composes

/* shared.module.css */
.flexCenter {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Header.module.css */
.header {
  composes: flexCenter from "./shared.module.css";
  height: 60px;
  background-color: #333;
  color: white;
}

יתרונות

  • סגנונות מקומיים אוטומטית - אין התנגשויות
  • תחביר CSS סטנדרטי לחלוטין
  • עובד מחוץ לקופסה עם Vite
  • תמיכה בכל תכונות CSS
  • ביצועים מצוינים

חסרונות

  • תחביר פחות אינטואיטיבי עבור סגנונות דינמיים
  • צריך קובץ CSS נפרד לכל קומפוננטה
  • שמות מחלקות מוגבלים למחרוזות (לא ניתן להשתמש ב-TypeScript)

CSS-in-JS - סגנונות בתוך JavaScript

גישה שבה כותבים CSS ישירות בקוד JavaScript/TypeScript. הספריה הפופולרית ביותר היא styled-components.

דוגמה עם styled-components

npm install styled-components
import styled from "styled-components";

const StyledButton = styled.button<{ $primary?: boolean }>`
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
  background-color: ${(props) => (props.$primary ? "#007bff" : "#6c757d")};
  color: white;

  &:hover {
    opacity: 0.9;
  }

  @media (max-width: 768px) {
    font-size: 14px;
    padding: 8px 16px;
  }
`;

const Card = styled.div`
  padding: 16px;
  border: 1px solid #ddd;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
`;

function Example() {
  return (
    <Card>
      <StyledButton $primary>כפתור ראשי</StyledButton>
      <StyledButton>כפתור משני</StyledButton>
    </Card>
  );
}
  • שם התכונות עם $ prefix - כך הם לא מועברים ל-DOM
  • תמיכה מלאה בנסטינג, pseudo-classes, media queries
  • הסגנונות דינמיים באופן טבעי דרך props

יתרונות

  • סגנונות דינמיים בקלות רבה
  • Scoping אוטומטי
  • אין קבצים נפרדים - הכל באותו קובץ
  • TypeScript מלא עם בדיקת טיפוסים
  • תמיכה ב-theming מובנית

חסרונות

  • עלות ביצועים בזמן ריצה - הספריה מייצרת CSS דינמית
  • גודל bundle גדול יותר
  • עקומת למידה - תחביר חדש (template literals)
  • ייתכנו בעיות עם SSR (Server Side Rendering)
  • הטרנד הנוכחי הוא להתרחק מ-CSS-in-JS בזמן ריצה לטובת פתרונות בזמן build

CSS שירותי - Utility-First CSS (Tailwind)

גישה שבה משתמשים במחלקות קטנות וממוקדות ישירות ב-HTML, במקום לכתוב CSS מותאם.

דוגמה עם Tailwind

function TailwindCard() {
  return (
    <div className="rounded-lg border border-gray-200 p-4 shadow-md hover:shadow-lg transition-shadow">
      <h2 className="text-xl font-bold text-gray-800 mb-2">כותרת</h2>
      <p className="text-gray-600 leading-relaxed">תוכן הכרטיסייה</p>
      <button className="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors">
        לחץ כאן
      </button>
    </div>
  );
}
  • כל מחלקה מייצגת תכונת CSS אחת
  • אין צורך לכתוב CSS בכלל
  • עיצוב רספונסיבי עם prefixes כמו sm:, md:, lg:

יתרונות

  • מהירות פיתוח גבוהה מאוד
  • אין צורך לחשוב על שמות מחלקות
  • קובץ CSS קטן - Tailwind מסיר מחלקות שלא בשימוש
  • עקביות בעיצוב דרך ערכי ברירת מחדל
  • רספונסיביות מובנית

חסרונות

  • HTML ארוך ומלא מחלקות
  • עקומת למידה - צריך לזכור שמות מחלקות
  • קשה יותר לעשות שינויים גלובליים
  • חלק מהמפתחים מרגישים שזה "מלכלך" את ה-HTML

ספריות קומפוננטות - Component Libraries

ספריות שמספקות קומפוננטות מעוצבות מוכנות לשימוש.

ספריות פופולריות

ספריה תיאור גודל Bundle התאמה אישית
Mantine ספריה מודרנית עם הוקים מובנים בינוני גבוהה מאוד
MUI (Material UI) מבוססת Material Design של Google גדול גבוהה
Chakra UI פשוטה ונגישה בינוני גבוהה
Ant Design פופולרית מאוד בסין גדול בינונית
Radix + שלכם קומפוננטות headless ללא עיצוב קטן מלאה
shadcn/ui מבוסס Radix עם Tailwind קטן מלאה

דוגמה עם Mantine

import { Button, Card, Text, Group, Badge } from "@mantine/core";

function MantineExample() {
  return (
    <Card shadow="sm" padding="lg" radius="md" withBorder>
      <Group justify="space-between" mb="xs">
        <Text fw={500}>כותרת הכרטיסייה</Text>
        <Badge color="pink" variant="light">
          חדש
        </Badge>
      </Group>
      <Text size="sm" c="dimmed">
        תיאור קצר של התוכן
      </Text>
      <Button variant="light" color="blue" fullWidth mt="md" radius="md">
        פתח
      </Button>
    </Card>
  );
}

יתרונות

  • מהירות פיתוח גבוהה מאוד
  • עיצוב מקצועי ועקבי מהקופסה
  • נגישות (a11y) מובנית
  • תיעוד מקיף ודוגמאות רבות
  • לא צריך לעצב מאפס

חסרונות

  • תלות בספריה חיצונית
  • גודל bundle גדול יותר
  • פחות שליטה על העיצוב הסופי
  • עקומת למידה של ה-API
  • שדרוגי גרסאות יכולים לשבור דברים

טבלת השוואה מסכמת

קריטריון Inline CSS רגיל CSS Modules CSS-in-JS Tailwind ספרית קומפוננטות
קלות התחלה גבוהה גבוהה בינונית בינונית בינונית בינונית
Scoping מובנה אין מובנה מובנה לא רלוונטי מובנה
סגנונות דינמיים מעולה חלש בינוני מעולה טוב טוב
ביצועים בינוני מעולה מעולה חלש מעולה טוב
תחזוקה חלש בינוני טוב טוב טוב מעולה
TypeScript מלא אין חלקי מלא חלקי מלא
Pseudo-classes אין מלא מלא מלא מלא מלא
גודל Bundle ללא קטן קטן גדול קטן בינוני-גדול

איך לבחור את הגישה הנכונה

לפרויקט קטן או אב-טיפוס

  • סגנונות אינליין או CSS רגיל
  • פשוט ומהיר, בלי תלויות נוספות

לפרויקט בינוני

  • CSS Modules עם Tailwind - שילוב מנצח
  • מודולים לקומפוננטות מורכבות, Tailwind ליתר

לפרויקט גדול או צוות גדול

  • ספרית קומפוננטות כבסיס (Mantine, MUI)
  • התאמה אישית דרך מערכת ה-theme של הספריה
  • שימוש ב-CSS Modules או Tailwind למקומות שצריך עיצוב מותאם

לאפליקציה עם דרישות עיצוב ייחודיות

  • Tailwind כבסיס עם קומפוננטות headless (Radix)
  • שליטה מלאה על העיצוב עם מבנה קומפוננטות נגיש

כלל אצבע

  • אם יש לכם מעצב UI עם עיצוב ייחודי - Tailwind + Radix
  • אם צריך לבנות מהר עם עיצוב מקצועי - Mantine
  • אם הפרויקט ענק עם צוות גדול - MUI או Mantine עם theme מותאם
  • אם הפרויקט פשוט - CSS Modules מספיקים

הגישה שלנו בקורס

בקורס הזה נתמקד בשתי גישות עיקריות:

  1. CSS Modules + Tailwind - לרגעים שנצטרך שליטה מלאה ונלמד את הבסיס
  2. Mantine - כספרית הקומפוננטות העיקרית שלנו

נבחרנו ב-Mantine מכמה סיבות:
- API נקי ואינטואיטיבי
- תיעוד מעולה
- הוקים שימושיים מובנים
- תמיכה מצוינת ב-TypeScript
- מערכת theme גמישה
- קהילה פעילה ותחזוקה שוטפת


סיכום

  • קיימות גישות רבות לעיצוב בריאקט, וכל אחת מתאימה למצבים שונים
  • סגנונות אינליין טובים למקרים פשוטים ודינמיים
  • CSS רגיל עובד אבל סובל מבעיות scope בפרויקטים גדולים
  • CSS Modules פותרים את בעיית ה-scope בצורה אלגנטית
  • CSS-in-JS (כמו styled-components) מאפשר סגנונות דינמיים חזקים אך עם עלות ביצועים
  • Tailwind מציע גישת utility-first שמאיצה פיתוח מאוד
  • ספריות קומפוננטות כמו Mantine מספקות עיצוב מקצועי מהקופסה
  • הבחירה תלויה בגודל הפרויקט, דרישות העיצוב, גודל הצוות והעדפות אישיות