1.9 טכניקות מתקדמות הרצאה
משתני CSS - CSS Variables (Custom Properties)¶
משתני CSS מאפשרים להגדיר ערך במקום אחד ולהשתמש בו בכל ה-CSS. כשרוצים לשנות - משנים במקום אחד והכל מתעדכן.
הגדרה ושימוש¶
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
--text-color: #333;
--spacing: 20px;
--border-radius: 8px;
}
.button {
background: var(--primary-color);
color: white;
padding: var(--spacing);
border-radius: var(--border-radius);
}
.card {
border: 2px solid var(--primary-color);
padding: var(--spacing);
border-radius: var(--border-radius);
color: var(--text-color);
}
- שם המשתנה תמיד מתחיל ב-
--(שני מקפים) - משתמשים ב-
var()כדי לקרוא את הערך
ערך ברירת מחדל - fallback¶
סקופ - Scope¶
משתנים ב-:root זמינים בכל מקום. אבל אפשר להגדיר משתנים בסלקטור ספציפי - והם יהיו זמינים רק לו ולילדיו:
:root {
--card-bg: white;
}
.dark-section {
--card-bg: #2c3e50;
--text-color: white;
}
.card {
background: var(--card-bg);
color: var(--text-color, #333);
}
כרטיס בתוך .dark-section יקבל רקע כהה וטקסט לבן. כרטיס מחוץ ל-.dark-section יקבל רקע לבן.
מערכת צבעים עם משתנים¶
:root {
/* color palette */
--color-primary: #3498db;
--color-primary-light: #5dade2;
--color-primary-dark: #2471a3;
--color-secondary: #2ecc71;
--color-secondary-light: #58d68d;
--color-danger: #e74c3c;
--color-warning: #f39c12;
--color-success: #27ae60;
/* neutral colors */
--color-text: #333;
--color-text-light: #666;
--color-bg: #ffffff;
--color-bg-secondary: #f5f5f5;
--color-border: #ddd;
/* spacing */
--space-xs: 4px;
--space-sm: 8px;
--space-md: 16px;
--space-lg: 24px;
--space-xl: 40px;
/* typography */
--font-family: 'Arial', sans-serif;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.25rem;
--font-size-xl: 1.5rem;
/* shadows */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 10px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 30px rgba(0, 0, 0, 0.15);
}
עם מערכת כזו, כל האתר עקבי - ואם רוצים לשנות צבע ראשי, משנים שורה אחת.
מצב כהה - Dark Mode¶
:root {
--color-text: #333;
--color-bg: #ffffff;
--color-bg-secondary: #f5f5f5;
--color-border: #ddd;
--color-card: #ffffff;
}
[data-theme="dark"] {
--color-text: #e0e0e0;
--color-bg: #1a1a2e;
--color-bg-secondary: #16213e;
--color-border: #2c2c4e;
--color-card: #1f1f3a;
}
body {
background: var(--color-bg);
color: var(--color-text);
}
.card {
background: var(--color-card);
border: 1px solid var(--color-border);
}
פשוט משנים את ה-attribute ב-HTML וכל האתר מחליף ערכת צבעים. בהמשך, כשנלמד JavaScript, נוכל לעשות את זה עם כפתור.
אפשר גם לזהות את ההעדפה של מערכת ההפעלה:
@media (prefers-color-scheme: dark) {
:root {
--color-text: #e0e0e0;
--color-bg: #1a1a2e;
--color-bg-secondary: #16213e;
--color-border: #2c2c4e;
}
}
פסאודו-קלאסים - Pseudo-Classes¶
פסאודו-קלאסים מאפשרים לבחור אלמנטים במצב מסוים.
מצבי אינטראקציה¶
a:hover { color: blue; } /* mouse is over the element */
a:active { color: red; } /* element is being clicked */
a:visited { color: purple; } /* link was visited */
a:focus { outline: 2px solid blue; } /* element has keyboard focus */
focus-visible ו-focus-within¶
/* only shows focus style when using keyboard (not mouse click) */
button:focus-visible {
outline: 3px solid #3498db;
outline-offset: 2px;
}
/* applies when any child has focus */
.form-group:focus-within {
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
}
:focus-visible מצוין - מראה outline רק למשתמשי מקלדת, לא ללוחצי עכבר.
:focus-within שימושי לטפסים - האזור כולו מסומן כשאחד השדות בו מקבל focus.
בחירת ילדים - Child Selectors¶
li:first-child { font-weight: bold; } /* first child */
li:last-child { border-bottom: none; } /* last child */
/* every odd item */
tr:nth-child(odd) { background: #f5f5f5; }
/* every even item */
tr:nth-child(even) { background: white; }
/* every 3rd item */
li:nth-child(3n) { color: red; }
/* the 2nd item */
li:nth-child(2) { font-style: italic; }
nth-of-type¶
nth-of-type דומה ל-nth-child אבל סופר רק אלמנטים מאותו סוג:
not - שלילה¶
/* all inputs except submit buttons */
input:not([type="submit"]) {
border: 1px solid #ccc;
padding: 10px;
}
/* all list items except the first one */
li:not(:first-child) {
border-top: 1px solid #eee;
}
is ו-where - קיבוץ¶
/* instead of writing */
header a:hover,
nav a:hover,
footer a:hover {
color: blue;
}
/* you can write */
:is(header, nav, footer) a:hover {
color: blue;
}
ההבדל בין :is() ל-:where(): הspecificity של :where() תמיד 0, בעוד :is() מקבלת את ה-specificity הגבוהה ביותר מבין הסלקטורים שבתוכה.
has - סלקטור הורה¶
:has() הוא אחד הפיצ'רים החזקים ביותר שנוספו ל-CSS. הוא מאפשר לבחור אלמנט בהתאם לילדיו:
/* card that contains an image */
.card:has(img) {
padding-top: 0;
}
/* card that has no image */
.card:not(:has(img)) {
padding: 20px;
}
/* form group where input is invalid */
.form-group:has(input:invalid) {
border-color: red;
}
/* label next to a checked checkbox */
label:has(+ input:checked) {
font-weight: bold;
}
לפני :has(), לא הייתה דרך ב-CSS לעצב הורה על סמך הילדים שלו. זה היה אפשרי רק עם JavaScript.
checked, disabled, empty¶
/* styled checkbox */
input:checked + label {
font-weight: bold;
color: green;
}
/* disabled button */
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* empty paragraph */
p:empty {
display: none;
}
פסאודו-אלמנטים - Pseudo-Elements¶
פסאודו-אלמנטים יוצרים אלמנטים "וירטואליים" שלא קיימים ב-HTML.
before ו-after¶
.quote::before {
content: '"';
font-size: 2rem;
color: #3498db;
}
.quote::after {
content: '"';
font-size: 2rem;
color: #3498db;
}
::before ו-::after יוצרים אלמנט לפני/אחרי התוכן של האלמנט.
ה-property content הוא חובה - בלי הוא הפסאודו-אלמנט לא יופיע.
שימושים נפוצים ל-before/after¶
/* decorative line under heading */
h2::after {
content: "";
display: block;
width: 50px;
height: 3px;
background: #3498db;
margin-top: 10px;
}
/* required field indicator */
.required::after {
content: " *";
color: red;
}
/* tooltip */
.tooltip {
position: relative;
}
.tooltip::after {
content: attr(data-tooltip);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #333;
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 0.8rem;
white-space: nowrap;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
.tooltip:hover::after {
opacity: 1;
}
attr() קורא ערך של attribute מה-HTML - שימושי מאוד ליצירת tooltips.
פסאודו-אלמנטים נוספים¶
/* style the placeholder text */
input::placeholder {
color: #999;
font-style: italic;
}
/* style selected text */
::selection {
background: #3498db;
color: white;
}
/* first line of a paragraph */
p::first-line {
font-weight: bold;
font-size: 1.1em;
}
/* first letter of a paragraph */
p::first-letter {
font-size: 3rem;
float: left;
line-height: 1;
margin-right: 8px;
color: #3498db;
}
פונקציות CSS¶
calc - חישובים¶
.sidebar {
width: 250px;
}
.main {
width: calc(100% - 250px - 20px); /* 100% minus sidebar minus gap */
}
calc() מאפשר לערבב יחידות שונות - פיקסלים עם אחוזים, rem עם vw, וכו.
.container {
padding: calc(var(--spacing) * 2);
width: calc(100vw - var(--sidebar-width));
font-size: calc(1rem + 0.5vw);
}
אפשר להשתמש ב-calc עם משתני CSS - זה שילוב חזק מאוד.
min, max, clamp¶
/* width will be the smaller of 90% and 1200px */
.container {
width: min(90%, 1200px);
}
/* font-size will be the larger of 16px and 2vw */
.text {
font-size: max(16px, 2vw);
}
/* fluid value with minimum and maximum bounds */
h1 {
font-size: clamp(1.5rem, 4vw, 3rem);
}
שימושים מעשיים:
/* fluid spacing */
.section {
padding: clamp(20px, 5vw, 60px);
}
/* fluid container */
.container {
width: min(90%, 1200px);
margin: 0 auto;
}
/* minimum touch target size */
.button {
min-height: max(44px, 2.5rem);
}
מתודולוגיית BEM¶
הבעיה¶
בפרויקטים גדולים, CSS נהיה בלתי נשלט:
- שמות מחלקות מתנגשים
- קשה לדעת מה כל class עושה
- שינוי בCSS אחד שובר דבר אחר
- CSS מנופח וחוזר על עצמו
הפתרון - BEM¶
BEM (Block, Element, Modifier) הוא convention לשמות מחלקות CSS:
- Block - רכיב עצמאי:
card,button,nav - Element - חלק מ-block, מסומן ב-
__:card__title,card__image - Modifier - גרסה שונה, מסומנת ב-
--:button--large,card--highlighted
דוגמה מלאה¶
<div class="card">
<img class="card__image" src="photo.jpg" alt="photo">
<div class="card__body">
<h3 class="card__title">Card Title</h3>
<p class="card__text">Card description text.</p>
<button class="card__button card__button--primary">Read More</button>
</div>
</div>
<div class="card card--featured">
<img class="card__image" src="photo2.jpg" alt="photo">
<div class="card__body">
<h3 class="card__title">Featured Card</h3>
<p class="card__text">This card is featured.</p>
<button class="card__button card__button--secondary">Learn More</button>
</div>
</div>
/* Block */
.card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: var(--shadow-sm);
}
/* Elements */
.card__image {
width: 100%;
height: 200px;
object-fit: cover;
}
.card__body {
padding: 20px;
}
.card__title {
font-size: 1.2rem;
margin: 0 0 10px;
}
.card__text {
color: #666;
margin: 0 0 15px;
}
.card__button {
padding: 8px 16px;
border: none;
border-radius: 5px;
cursor: pointer;
}
/* Modifiers */
.card--featured {
border: 2px solid gold;
box-shadow: var(--shadow-lg);
}
.card__button--primary {
background: var(--color-primary);
color: white;
}
.card__button--secondary {
background: transparent;
border: 1px solid var(--color-primary);
color: var(--color-primary);
}
היתרונות של BEM¶
- ברור מה כל class עושה
- אין סכנה של התנגשויות
- קל למצוא ולתחזק CSS
- כל רכיב מכיל את עצמו
גישות אחרות¶
BEM היא לא הגישה היחידה. קיימות גם:
- Tailwind CSS - utility classes: class="bg-blue-500 text-white p-4 rounded"
- CSS Modules - שמות אוטומטיים ייחודיים (נפוץ ב-React)
כל אחת מהגישות פותרת את אותה בעיה בדרך אחרת. BEM היא הכי פשוטה להתחיל איתה.
קינון CSS - CSS Nesting¶
קינון CSS הוא פיצ'ר חדש שמאפשר לכתוב סלקטורים מקוננים ישירות ב-CSS, בלי preprocessor כמו Sass.
תחביר בסיסי¶
.card {
background: white;
border-radius: 8px;
padding: 20px;
& .card-title {
font-size: 1.2rem;
margin-bottom: 10px;
}
& .card-text {
color: #666;
}
&:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
&.card--featured {
border: 2px solid gold;
}
@media (min-width: 768px) {
padding: 30px;
}
}
זה שקול ל:
.card { background: white; border-radius: 8px; padding: 20px; }
.card .card-title { font-size: 1.2rem; margin-bottom: 10px; }
.card .card-text { color: #666; }
.card:hover { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); }
.card.card--featured { border: 2px solid gold; }
@media (min-width: 768px) { .card { padding: 30px; } }
היתרונות¶
- קוד מאורגן יותר - כל מה ששייך לרכיב מקובץ יחד
- פחות חזרה על שמות סלקטורים
- media queries ישירות בתוך הרכיב
שימוש מעשי¶
.nav {
display: flex;
background: #1a1a2e;
padding: 10px 20px;
& a {
color: white;
text-decoration: none;
padding: 8px 16px;
&:hover {
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
}
&.active {
font-weight: bold;
border-bottom: 2px solid white;
}
}
@media (max-width: 768px) {
flex-direction: column;
}
}
הסימן & מייצג את הסלקטור ההורה. & a הופך ל-.nav a, ו-&:hover הופך ל-.nav a:hover (בתוך הקינון של & a).
CSS nesting נתמך בכל הדפדפנים המודרניים. זה חוסך הרבה קוד ועושה את ה-CSS הרבה יותר קריא ומאורגן.
סיכום¶
- משתני CSS - הגדירו ערכים פעם אחת ב-
:root, השתמשו בהם בכל מקום. בסיס ל-theming ו-dark mode - פסאודו-קלאסים - בחירת אלמנטים לפי מצב:
:hover,:nth-child(),:has(),:focus-visible - פסאודו-אלמנטים - יצירת אלמנטים ויזואליים:
::before,::after,::placeholder - פונקציות CSS -
calc()לחישובים,clamp()לערכים רספונסיביים,min()/max() - BEM - מתודולוגיה לשמות מחלקות: block__element--modifier
- קינון CSS - סלקטורים מקוננים לארגון קוד טוב יותר