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 בצורה נוחה:
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¶
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 מספיקים
הגישה שלנו בקורס¶
בקורס הזה נתמקד בשתי גישות עיקריות:
- CSS Modules + Tailwind - לרגעים שנצטרך שליטה מלאה ונלמד את הבסיס
- Mantine - כספרית הקומפוננטות העיקרית שלנו
נבחרנו ב-Mantine מכמה סיבות:
- API נקי ואינטואיטיבי
- תיעוד מעולה
- הוקים שימושיים מובנים
- תמיכה מצוינת ב-TypeScript
- מערכת theme גמישה
- קהילה פעילה ותחזוקה שוטפת
סיכום¶
- קיימות גישות רבות לעיצוב בריאקט, וכל אחת מתאימה למצבים שונים
- סגנונות אינליין טובים למקרים פשוטים ודינמיים
- CSS רגיל עובד אבל סובל מבעיות scope בפרויקטים גדולים
- CSS Modules פותרים את בעיית ה-scope בצורה אלגנטית
- CSS-in-JS (כמו styled-components) מאפשר סגנונות דינמיים חזקים אך עם עלות ביצועים
- Tailwind מציע גישת utility-first שמאיצה פיתוח מאוד
- ספריות קומפוננטות כמו Mantine מספקות עיצוב מקצועי מהקופסה
- הבחירה תלויה בגודל הפרויקט, דרישות העיצוב, גודל הצוות והעדפות אישיות