→ חזרה למעבדה
המדריך המלא · HOW IT WAS BUILT

איך בונים
מעבדת אותיות

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

01 — הקונספט

האות העברית כגיבורה

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

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

22אותיות אינטראקטיביות
2צירים משתנים: wght + wdth
0תמונות — הכול פרוצדורלי
1200×1600ייצוא פוסטר PNG
02 — הפלטה והטיפוגרפיה

נייר, דיו, ורמיליון

נייר#EDE6D6
נייר כהה#E3DAC4
דיו#16130E
דיו רך#262019
ורמיליון#E8401C

שלושה גופנים, לכל אחד תפקיד אחד בלבד:

גרעיניות הנייר היא SVG זעיר של feTurbulence בשכבת overlay קבועה בשקיפות 5% — אפס קבצים חיצוניים:

/* paper grain — data-URI, no asset files */
body::after {
  content: ''; position: fixed; inset: 0;
  pointer-events: none; opacity: 0.05;
  background-image: url("data:image/svg+xml,...feTurbulence
    type='fractalNoise' baseFrequency='0.9'...");
}
03 — גופן משתנה בשני צירים

הלב: font-variation-settings

כל האנימציות הטיפוגרפיות באתר הן שינוי של שני מספרים. הטעינה מתבצעת עם טווחי הצירים המלאים בכתובת של Google Fonts:

https://fonts.googleapis.com/css2?family=
  Noto+Sans+Hebrew:wdth,wght@62.5..100,100..900&display=swap

ומכאן הכול נשלט מ־JS דרך פרופרטי CSS אחד:

// one helper, the whole site runs on it
const fvs = (w, d) => `'wght' ${w}, 'wdth' ${d}`;
el.style.fontVariationSettings = fvs(720, 84.5);

בהירו (Hero) הסמן עצמו הוא הסליידר: מיקום X ממופה לרוחב, מיקום Y למשקל, עם lerp (אינטרפולציה לינארית) בלולאת requestAnimationFrame כדי שהאות "תרדוף" את הסמן ברכות במקום לקפוץ:

hero.addEventListener('pointermove', (e) => {
  const x = (e.clientX - r.left) / r.width;   // 0..1
  const y = (e.clientY - r.top)  / r.height;
  targetD = 62.5 + (1 - x) * 37.5;  // wdth
  targetW = 100  + y * 800;         // wght
});

function tick() {
  curW += (targetW - curW) * 0.08;  // smooth chase
  curD += (targetD - curD) * 0.08;
  letter.style.fontVariationSettings = fvs(curW, curD);
  requestAnimationFrame(tick);
}

כשהסמן נח שלוש שניות, המערכת עוברת ל"נשימה" — גלי סינוס איטיים על שני הצירים, כך שהעמוד לעולם לא מת:

// idle breathing — the page is never dead
if (t - lastPointerTime > 3000) {
  idlePhase += 0.008;
  targetW = 500 + Math.sin(idlePhase) * 380;
  targetD = 81  + Math.cos(idlePhase * 0.7) * 18;
}
04 — צירים בגלילה

ScrollTrigger כסליידר ענק

שני פאנלים מוצמדים (pinned) הופכים את הגלילה עצמה למחוון: פאנל המשקל גורף wght מ־100 ל־900, ופאנל הרוחב מוחץ את wdth מ־100 ל־62.5. ה־progress של הטריגר ממופה ישירות לציר:

ScrollTrigger.create({
  trigger: '#axis-weight',
  start: 'top top', end: '+=140%',
  pin: '.panel-sticky', scrub: 0.4,
  onUpdate(self) {
    const w = 100 + self.progress * 800;
    word.style.fontVariationSettings = fvs(w, 100);
    valEl.textContent = w.toFixed(0);   // live instrument
    fill.style.width = (self.progress * 100) + '%';
  }
});

scrub: 0.4 נותן לאנימציה "משקל" קטן אחרי שהגלילה נעצרת — התחושה של מכשיר אנלוגי. תחת prefers-reduced-motion הסקראב מבוטל והמילים סטטיות.

05 — רשת האותיות

22 תאים, מעבר אחד

הרשת נבנית מ־22 אובייקטים (אות, שם, גימטריה). הטריק: font-variation-settings הוא פרופרטי אנימטיבילי ב־CSS, אז ההובר לא צריך JS בכלל — האות מתנפחת מ־wght 300 לגוש שחור צר של wght 900 / wdth 70 בטרנזישן אחד:

.letter-cell {
  font-variation-settings: 'wght' 300, 'wdth' 100;
  transition: font-variation-settings 0.45s cubic-bezier(0.22, 1, 0.36, 1),
              background 0.3s, color 0.3s;
}
.letter-cell:hover {
  background: var(--ink); color: var(--paper);
  font-variation-settings: 'wght' 900, 'wdth' 70;
}

לחיצה פותחת כרטיס מעבדה עם לולאת GSAP שמנשימה את המשקל הלוך ושוב (yoyo: true, repeat: -1) — ספסימן חי של האות.

06 — תחנת התאומים

מצב שיקוף: אות על אות

חמישה זוגות אותיות שמתבלבלים ביניהם — ד/ר, ה/ח, ב/כ, ו/ז, ם/ס — מוצגים זה מול זה עם מחוון משקל משותף. הפיצ'ר המרכזי הוא מצב שיקוף: שתי האותיות נערמות זו על זו, השנייה בוורמיליון שקוף עם mix-blend-mode: multiply — וכל פיקסל שבו הן נבדלות נדלק באדום:

/* overlay (x-ray) mode — the difference glows red */
.twins-stage.overlay .twin { position: absolute; inset: 0; }
.twins-stage.overlay .twin-b .twin-ch {
  color: var(--red);
  opacity: 0.62;
  mix-blend-mode: multiply;   /* ink stays ink, diff turns red */
}

אין כאן שום SVG או מסכות — זה אותו גופן חי פעמיים, וה־blend עושה את עבודת ההשוואה. החלפת זוג עוברת דרך אותה כוריאוגרפיית יציאה/כניסה של אות ההירו, כדי שהאתר ידבר שפת תנועה אחת.

07 — מצב פוסטר וייצוא PNG

מהדפדפן לקובץ הדפסה

התצוגה המקדימה היא DOM רגיל (מהיר לעדכן), אבל ההורדה מציירת את הפוסטר מחדש על <canvas> ברזולוציית 1200×1600. משקל עובר בקלות דרך מחרוזת הפונט של הקנבס; רוחב דורש טריק — הקנבס לא מכיר wdth מספרי, אבל כן מכיר ctx.fontStretch עם מילות מפתח, אז ממפים לערך הקרוב:

function nearestStretch(wdth) {
  const stops = [[62.5, 'extra-condensed'], [75, 'condensed'],
                 [87.5, 'semi-condensed'], [100, 'normal']];
  // pick the closest keyword the canvas understands
}

await document.fonts.load(`${wght} 100px "Noto Sans Hebrew"`);
ctx.fontStretch = nearestStretch(wdth);
ctx.font = `${wght} ${px}px "Noto Sans Hebrew"`;
ctx.direction = 'rtl';          // Hebrew-aware canvas text
ctx.fillText(text, 0, 0);
a.href = c.toDataURL('image/png');  // download

חשוב: document.fonts.load() לפני הציור, אחרת הקנבס מצייר בגופן fallback. גופני Google Fonts אינם "מכתימים" (taint) את הקנבס — הם אינם תמונות — ולכן הייצוא חוקי לחלוטין.

08 — ליטוש

הפרטים הקטנים

09 — שכפול

לבנות אחת כזו בעצמכם