האות העברית כגיבורה
הרעיון: במקום "אתר על טיפוגרפיה", לבנות מכשיר מעבדה שבו האות עצמה היא האובייקט הנחקר. כל אינטראקציה באתר מושכת בציר אמיתי של קובץ הגופן — שום דבר לא מזויף, שום דבר לא תמונה.
שפת העיצוב שאולה מגיליונות ספסימן של בתי דפוס: נייר חם, דיו כמעט־שחור, חותמת ורמיליון אחת, קווי מדידה, מספור עמודות וכיתוב מונו־ספייס באנגלית כ"תוויות מכשיר".
נייר, דיו, ורמיליון
שלושה גופנים, לכל אחד תפקיד אחד בלבד:
- Noto Sans Hebrew (משתנה) — הכוכב. היחיד בגוגל־פונטס עם ציר משקל
100..900וגם ציר רוחב62.5..100בעברית. - Heebo — טקסט ממשק וכפתורים.
- IBM Plex Mono — קריאות מכשיר: ערכי צירים, מספור, תוויות.
גרעיניות הנייר היא 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'...");
}
הלב: 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;
}
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 הסקראב מבוטל והמילים סטטיות.
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) — ספסימן חי של האות.
מצב שיקוף: אות על אות
חמישה זוגות אותיות שמתבלבלים ביניהם — ד/ר, ה/ח, ב/כ, ו/ז, ם/ס — מוצגים זה מול זה עם מחוון משקל משותף. הפיצ'ר המרכזי הוא מצב שיקוף: שתי האותיות נערמות זו על זו, השנייה בוורמיליון שקוף עם 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 עושה את עבודת ההשוואה. החלפת זוג עוברת דרך אותה כוריאוגרפיית יציאה/כניסה של אות ההירו, כדי שהאתר ידבר שפת תנועה אחת.
מהדפדפן לקובץ הדפסה
התצוגה המקדימה היא 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) את הקנבס — הם אינם תמונות — ולכן הייצוא חוקי לחלוטין.
הפרטים הקטנים
- גל הובר ברשת: ריחוף על אות מפעיל גם את שכנותיה — התאים במרחק 1 ו־2 מקבלים משקל ביניים (620 ו־440), כך שהרשת מתנהגת כמו קלידים של פסנתר ולא כמו טבלה.
- סקרול־ספיי: IntersectionObserver עם
rootMargin: '-40% 0px -55%'מדליק קו תחתון קבוע על קישור הניווט של התחנה הפעילה. - אלף־בית חי בפוטר: השורה הענקית מפוצלת ל־span לכל אות; כניסה מלוכסנת מהמרכז (stagger from center) בגלילה, וכל אות מתמלאת ורמיליון בריחוף.
- לואדר: שכבת דיו עם אות מתחלפת כל 110ms, שממתינה גם ל־
document.fonts.ready— כך אין הבזק של גופן שגוי (FOUT) לעולם. - הדר בבלנד:
mix-blend-mode: differenceהופך את הניווט לבהיר על דיו וכהה על נייר — בלי JS ובלי מדידת גלילה. - קווי מדידה בהירו: שני קווים דקים + תגית מונו שצמודים לסמן ומציגים את ערכי הצירים בזמן אמת — האתר כולו מרגיש כמו מכשיר.
- מרקיזות פנגרמה: "דג סקרן שט בים…" ו"שפן אכל קצת גזר…" — שתי פנגרמות עבריות בלולאת CSS אינסופית, כיוונים הפוכים.
- reduced-motion: כל לולאה — נשימה, מרקיזה, סקראב, ריבאונד — נעטפת בבדיקת
prefers-reduced-motionומקבלת מצב סטטי מכובד.
לבנות אחת כזו בעצמכם
- שלושה קבצים סטטיים:
index.html,css/style.css,js/main.js. אין באנדלר, אין תלויות מקומיות. - CDN בלבד:
gsap@3.12.5+ScrollTriggerמ־jsdelivr, גופנים מ־Google Fonts (גרסאות נעוצות). - מפת הדרכים: (1) טענו גופן משתנה עם טווחי צירים מלאים → (2) בנו helper אחד ל־
font-variation-settings→ (3) חברו אליו קלט: סמן, גלילה, סליידרים → (4) הוסיפו lerp לכל דבר שנע → (5) ייצאו לקנבס עםfonts.load. - לא נעשה שימוש בנכסי AI — האתר פרוצדורלי במלואו, והאות עושה את כל העבודה.