לדלג לתוכן

1.9 טכניקות מתקדמות פתרון

פתרון תרגול טכניקות מתקדמות


תרגיל 1 - מערכת צבעים עם משתני CSS

:root {
  /* colors */
  --color-primary: #3498db;
  --color-primary-light: #5dade2;
  --color-primary-dark: #2471a3;
  --color-secondary: #2ecc71;
  --color-secondary-light: #58d68d;
  --color-accent: #f39c12;
  --color-danger: #e74c3c;

  /* text colors */
  --color-text: #333;
  --color-text-light: #666;
  --color-text-on-dark: #ffffff;

  /* background colors */
  --color-bg: #ffffff;
  --color-bg-secondary: #f5f5f5;

  /* border */
  --color-border: #ddd;

  /* spacing */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 40px;

  /* border radius */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 16px;

  /* 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);
}

body {
  font-family: Arial, sans-serif;
  background: var(--color-bg);
  color: var(--color-text);
  padding: var(--space-xl);
}

h1, h2, h3 {
  color: var(--color-text);
  margin-bottom: var(--space-md);
}

p {
  color: var(--color-text-light);
  margin-bottom: var(--space-md);
}

/* buttons */
.btn {
  padding: var(--space-sm) var(--space-md);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 1rem;
  color: var(--color-text-on-dark);
}

.btn--primary { background: var(--color-primary); }
.btn--primary:hover { background: var(--color-primary-dark); }
.btn--secondary { background: var(--color-secondary); }
.btn--danger { background: var(--color-danger); }

/* card */
.card {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-lg);
  box-shadow: var(--shadow-sm);
  margin-bottom: var(--space-lg);
}

/* input */
.input {
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: 1rem;
  color: var(--color-text);
  background: var(--color-bg);
}

.input:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
}
  • כל ערך צבע, ריווח, radius וצל מוגדר כמשתנה
  • אין ערכים "קשיחים" בשום סלקטור - הכל דרך var()
  • קל לשנות את כל ערכת הצבעים על ידי שינוי ה-:root

תרגיל 2 - מצב כהה/בהיר - Dark/Light Mode

/* light mode (default) */
:root {
  --color-text: #333;
  --color-text-light: #666;
  --color-text-on-dark: #ffffff;
  --color-bg: #ffffff;
  --color-bg-secondary: #f5f5f5;
  --color-border: #ddd;
  --color-card: #ffffff;
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
  --shadow-md: 0 4px 10px rgba(0, 0, 0, 0.1);
}

/* dark mode */
[data-theme="dark"] {
  --color-text: #e0e0e0;
  --color-text-light: #aaa;
  --color-text-on-dark: #ffffff;
  --color-bg: #1a1a2e;
  --color-bg-secondary: #16213e;
  --color-border: #2c2c4e;
  --color-card: #1f1f3a;
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
  --shadow-md: 0 4px 10px rgba(0, 0, 0, 0.3);
}

/* auto dark mode based on system preference */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --color-text: #e0e0e0;
    --color-text-light: #aaa;
    --color-bg: #1a1a2e;
    --color-bg-secondary: #16213e;
    --color-border: #2c2c4e;
    --color-card: #1f1f3a;
    --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
    --shadow-md: 0 4px 10px rgba(0, 0, 0, 0.3);
  }
}

/* smooth transition */
body {
  background: var(--color-bg);
  color: var(--color-text);
  transition: background 0.3s ease, color 0.3s ease;
}

.card {
  background: var(--color-card);
  border-color: var(--color-border);
  transition: background 0.3s ease, border-color 0.3s ease;
}

/* theme toggle button */
.theme-toggle {
  position: fixed;
  top: 20px;
  right: 20px;
  padding: 10px 20px;
  background: var(--color-bg-secondary);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  cursor: pointer;
}
<!-- light mode -->
<html data-theme="light">

<!-- dark mode -->
<html data-theme="dark">
  • [data-theme="dark"] דורס את המשתנים ב-:root
  • ה-prefers-color-scheme media query מזהה העדפת מערכת הפעלה
  • transition על body ו-card יוצר מעבר חלק בין המצבים
  • הסלקטור :root:not([data-theme="light"]) מבטיח שהעדפת מערכת לא תדרוס בחירה ידנית של light

תרגיל 3 - checkboxes מותאמים אישית

<label class="custom-checkbox">
  <input type="checkbox">
  <span class="custom-checkbox__mark"></span>
  Option 1
</label>

<label class="custom-checkbox">
  <input type="checkbox">
  <span class="custom-checkbox__mark"></span>
  Option 2
</label>

<label class="custom-checkbox">
  <input type="checkbox" checked>
  <span class="custom-checkbox__mark"></span>
  Option 3 (pre-checked)
</label>
.custom-checkbox {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  padding: 8px 0;
  font-size: 1rem;
  user-select: none;
}

.custom-checkbox input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.custom-checkbox__mark {
  width: 22px;
  height: 22px;
  border: 2px solid #ccc;
  border-radius: 4px;
  position: relative;
  flex-shrink: 0;
  transition: background 0.2s ease, border-color 0.2s ease;
}

/* checkmark (hidden by default) */
.custom-checkbox__mark::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 7px;
  width: 5px;
  height: 10px;
  border: solid white;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
  opacity: 0;
  transition: opacity 0.2s ease;
}

/* checked state */
.custom-checkbox input:checked + .custom-checkbox__mark {
  background: #3498db;
  border-color: #3498db;
}

.custom-checkbox input:checked + .custom-checkbox__mark::after {
  opacity: 1;
}

/* hover */
.custom-checkbox:hover .custom-checkbox__mark {
  border-color: #3498db;
}

/* focus-visible */
.custom-checkbox input:focus-visible + .custom-checkbox__mark {
  outline: 3px solid rgba(52, 152, 219, 0.3);
  outline-offset: 2px;
}
  • ה-checkbox המקורי מוסתר עם opacity: 0 ו-position: absolute
  • ה-::after של ה-mark יוצר את סימן ה-V עם border trick
  • input:checked + .mark - הסלקטור + בוחר את האלמנט הבא אחרי ה-input
  • user-select: none מונע סימון טקסט בלחיצה כפולה

תרגיל 4 - tooltips עם פסאודו-אלמנטים

<button class="tooltip tooltip--top" data-tooltip="This is a tooltip">Hover me (top)</button>
<button class="tooltip tooltip--bottom" data-tooltip="Bottom tooltip">Hover me (bottom)</button>
<button class="tooltip tooltip--left" data-tooltip="Left tooltip">Hover me (left)</button>
<button class="tooltip tooltip--right" data-tooltip="Right tooltip">Hover me (right)</button>
.tooltip {
  position: relative;
  padding: 10px 20px;
  background: #3498db;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 1rem;
  margin: 40px;
}

/* tooltip bubble */
.tooltip::after {
  content: attr(data-tooltip);
  position: absolute;
  background: #333;
  color: white;
  padding: 6px 12px;
  border-radius: 4px;
  font-size: 0.8rem;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease, transform 0.3s ease;
}

/* tooltip arrow */
.tooltip::before {
  content: "";
  position: absolute;
  border: 6px solid transparent;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease, transform 0.3s ease;
}

/* show on hover */
.tooltip:hover::after,
.tooltip:hover::before {
  opacity: 1;
}

/* TOP (default) */
.tooltip--top::after {
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%) translateY(5px);
}

.tooltip--top:hover::after {
  transform: translateX(-50%) translateY(0);
}

.tooltip--top::before {
  bottom: calc(100% - 2px);
  left: 50%;
  transform: translateX(-50%);
  border-top-color: #333;
}

/* BOTTOM */
.tooltip--bottom::after {
  top: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%) translateY(-5px);
}

.tooltip--bottom:hover::after {
  transform: translateX(-50%) translateY(0);
}

.tooltip--bottom::before {
  top: calc(100% - 2px);
  left: 50%;
  transform: translateX(-50%);
  border-bottom-color: #333;
}

/* LEFT */
.tooltip--left::after {
  right: calc(100% + 10px);
  top: 50%;
  transform: translateY(-50%) translateX(5px);
}

.tooltip--left:hover::after {
  transform: translateY(-50%) translateX(0);
}

.tooltip--left::before {
  right: calc(100% - 2px);
  top: 50%;
  transform: translateY(-50%);
  border-left-color: #333;
}

/* RIGHT */
.tooltip--right::after {
  left: calc(100% + 10px);
  top: 50%;
  transform: translateY(-50%) translateX(-5px);
}

.tooltip--right:hover::after {
  transform: translateY(-50%) translateX(0);
}

.tooltip--right::before {
  left: calc(100% - 2px);
  top: 50%;
  transform: translateY(-50%);
  border-right-color: #333;
}
  • ::after מכיל את טקסט ה-tooltip (מ-attr(data-tooltip))
  • ::before יוצר חץ עם border trick
  • pointer-events: none מונע מה-tooltip לחסום hover על אלמנטים אחרים
  • כל כיוון (top/bottom/left/right) ממקם את ה-tooltip בהתאם

תרגיל 5 - שימוש ב-has לעיצוב הורה

<!-- form validation -->
<div class="form-group">
  <label>Email</label>
  <input type="email" required placeholder="enter email">
</div>

<!-- card with/without image -->
<div class="card-has">
  <img src="https://picsum.photos/300/200" alt="photo">
  <h3>Card with image</h3>
  <p>This card has an image.</p>
</div>

<div class="card-has">
  <h3>Card without image</h3>
  <p>This card has no image.</p>
</div>

<!-- checklist -->
<div class="checklist">
  <label class="check-item">
    <input type="checkbox"> Buy groceries
  </label>
  <label class="check-item">
    <input type="checkbox" checked> Clean house
  </label>
  <label class="check-item">
    <input type="checkbox"> Call dentist
  </label>
</div>

<!-- search with has -->
<div class="search-bar">
  <input type="text" placeholder="Search...">
  <button>Search</button>
</div>
/* form group - red border when input is invalid */
.form-group {
  padding: 15px;
  border: 2px solid #ddd;
  border-radius: 8px;
  margin-bottom: 15px;
  transition: border-color 0.3s ease;
}

.form-group:has(input:invalid:not(:placeholder-shown)) {
  border-color: #e74c3c;
  background: #fdf0ef;
}

.form-group:has(input:valid:not(:placeholder-shown)) {
  border-color: #2ecc71;
}

/* card with/without image */
.card-has {
  border: 1px solid #ddd;
  border-radius: 8px;
  overflow: hidden;
  margin-bottom: 15px;
}

.card-has:has(img) {
  /* card has an image - no padding on top */
}

.card-has:not(:has(img)) {
  padding: 20px;
  border-left: 4px solid #3498db;
}

.card-has img {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.card-has h3, .card-has p {
  padding: 0 20px;
}

/* checklist - green background when checked */
.check-item {
  display: block;
  padding: 12px 15px;
  border-bottom: 1px solid #eee;
  cursor: pointer;
  transition: background 0.2s ease;
}

.check-item:has(input:checked) {
  background: #eafaf1;
  text-decoration: line-through;
  color: #999;
}

/* search bar - button disabled when empty */
.search-bar {
  display: flex;
  gap: 8px;
}

.search-bar button {
  padding: 8px 16px;
  background: #3498db;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  transition: opacity 0.2s ease;
}

.search-bar:has(input:placeholder-shown) button {
  opacity: 0.4;
  cursor: not-allowed;
}
  • :has(input:invalid:not(:placeholder-shown)) - שדה שנוגעים בו ויש בו ערך לא תקין
  • :not(:has(img)) - כרטיס שאין בו תמונה
  • :has(input:checked) - שורה שה-checkbox שלה מסומן
  • :has(input:placeholder-shown) - כשהinput ריק (ה-placeholder עדיין מוצג)

תרגיל 6 - שיפוץ CSS למתודולוגיית BEM

<nav class="main-nav">
  <div class="main-nav__logo">MyBrand</div>
  <ul class="main-nav__links">
    <li><a href="#" class="main-nav__link main-nav__link--active">Home</a></li>
    <li><a href="#" class="main-nav__link">About</a></li>
    <li><a href="#" class="main-nav__link">Contact</a></li>
  </ul>
  <button class="main-nav__cta">Sign Up</button>
</nav>

<section class="hero">
  <h1 class="hero__title">Welcome</h1>
  <p class="hero__text">This is the hero section.</p>
  <button class="hero__button hero__button--large">Get Started</button>
</section>

<section class="features">
  <div class="features__card">
    <h3 class="features__title">Feature 1</h3>
    <p class="features__text">Description</p>
  </div>
  <div class="features__card features__card--highlighted">
    <h3 class="features__title">Feature 2</h3>
    <p class="features__text">Description</p>
  </div>
  <div class="features__card">
    <h3 class="features__title">Feature 3</h3>
    <p class="features__text">Description</p>
  </div>
</section>
/* ===== Block: main-nav ===== */
.main-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 30px;
  background: #1a1a2e;
}

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

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

.main-nav__link {
  color: #ccc;
  text-decoration: none;
}

.main-nav__link:hover {
  color: white;
}

.main-nav__link--active {
  color: white;
  font-weight: bold;
}

.main-nav__cta {
  background: #e74c3c;
  color: white;
  border: none;
  padding: 8px 20px;
  border-radius: 5px;
  cursor: pointer;
}

/* ===== Block: hero ===== */
.hero {
  text-align: center;
  padding: 80px 20px;
  background: linear-gradient(135deg, #1a1a2e, #16213e);
  color: white;
}

.hero__title {
  font-size: 3rem;
  margin: 0 0 15px;
}

.hero__text {
  font-size: 1.2rem;
  margin: 0 0 25px;
  color: #ccc;
}

.hero__button {
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  background: #3498db;
  color: white;
}

.hero__button--large {
  padding: 14px 32px;
  font-size: 1.2rem;
}

/* ===== Block: features ===== */
.features {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  padding: 60px 30px;
}

.features__card {
  background: white;
  padding: 30px;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  text-align: center;
}

.features__card--highlighted {
  border: 2px solid #3498db;
  transform: scale(1.05);
}

.features__title {
  margin: 0 0 10px;
}

.features__text {
  color: #666;
  margin: 0;
}

גרסה עם CSS nesting:

.main-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 30px;
  background: #1a1a2e;

  & .main-nav__logo {
    color: white;
    font-size: 1.5rem;
    font-weight: bold;
  }

  & .main-nav__links {
    display: flex;
    list-style: none;
    gap: 20px;
    margin: 0;
    padding: 0;
  }

  & .main-nav__link {
    color: #ccc;
    text-decoration: none;

    &:hover { color: white; }
    &--active { color: white; font-weight: bold; }
  }

  & .main-nav__cta {
    background: #e74c3c;
    color: white;
    border: none;
    padding: 8px 20px;
    border-radius: 5px;
    cursor: pointer;
  }
}

תרגיל 7 - ערכים רספונסיביים עם calc ו-clamp

:root {
  --sidebar-width: 250px;
  --gap: 20px;
}

body {
  margin: 0;
  font-family: Arial, sans-serif;
}

.page {
  display: flex;
  min-height: 100vh;
}

.sidebar {
  width: var(--sidebar-width);
  flex-shrink: 0;
  background: #1a1a2e;
  color: white;
  padding: clamp(15px, 3vw, 30px);
}

.main {
  width: calc(100% - var(--sidebar-width) - var(--gap));
  margin-left: var(--gap);
  padding: clamp(15px, 3vw, 40px);
}

.container {
  width: min(90%, 1200px);
  margin: 0 auto;
}

.hero-section {
  height: max(50vh, 400px);
  background: linear-gradient(135deg, #3498db, #2ecc71);
  display: grid;
  place-items: center;
  color: white;
  text-align: center;
  padding: clamp(20px, 5vw, 60px);
}

.hero-section h1 {
  font-size: clamp(1.8rem, 5vw, 3.5rem);
  margin-bottom: clamp(10px, 2vw, 20px);
}

.hero-section p {
  font-size: clamp(1rem, 2vw, 1.3rem);
}

.content-section {
  padding: clamp(30px, 5vw, 80px) 0;
}

.content-section h2 {
  font-size: clamp(1.3rem, 3vw, 2rem);
  margin-bottom: clamp(15px, 3vw, 30px);
}

.content-section p {
  font-size: clamp(0.9rem, 1.5vw, 1.1rem);
  line-height: 1.7;
  max-width: min(100%, 70ch);
}
  • אין אף media query - הכל מתאים את עצמו אוטומטית
  • calc() מחשב את רוחב ה-main על פי רוחב ה-sidebar וה-gap
  • clamp() מגביל padding, font-size ו-spacing בין מינימום למקסימום
  • min() מגביל את רוחב ה-container
  • max() מבטיח גובה מינימלי ל-hero
  • 70ch - רוחב מקסימלי של 70 תווים לטקסט - אידיאלי לקריאות