→ לכל המאגר

B64 · תגיות שמצביעות על המסר המרכזי

התנהגויות vanilla JS · IntersectionObserver · CSS טקסט וכותרותכרטיסים ממתין

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

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

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

הדמו

גלול למטה, הדמו מגיע ↓

אתר שלא רק נראה טובאלא מביא לקוחות

עוד מסלול גלילה. נסה גם חזרה למעלה ↑
הזווית של כל חץ מחושבת ב-JS ממיקום אמיתי ולא נכתבת ביד, ולכן היא נשארת נכונה במובייל, שבו התגיות עוברות לשורות מעל ומתחת לעיגול. מלכודת: אם מודדים עם getBoundingClientRect לפני הכניסה, מקבלים את התגית המכווצת והמוזזת, והחצים מכוונים לנקודה שגויה. לכן offsetLeft ו-offsetTop, שמתעלמים מטרנספורמים. הנדנוד יושב על עטיפה פנימית, אחרת ה-animation דורס את ה-transform של הכניסה. בעור עם accent בהיר הטקסט בתגית מקבל var(--accent-ink) ונשאר קריא.

קוד להדבקה

הקוד כאן עומד בפני עצמו. כל טוקן עיצוב נושא ברירת מחדל, מחלקות העזר של המאגר מוטמעות בו, ותגי הסקריפט מסודרים לפי הסדר הנכון. פרויקט שמגדיר --accent או --line משלו יורש אותם אוטומטית. הרכיב הזה מסתמך על dir="rtl".

פתח קובץ עצמאי ↗
הצג את הקוד כאן

CSS

/* מחלקות עזר של המאגר שהרכיב הזה משתמש בהן */
.stage{padding:var(--sec, clamp(120px, 12vw, 260px)) var(--gutter, clamp(24px, 5vw, 96px));position:relative}
.stage.tight{padding-block:clamp(48px,5vw,90px)}
.stage.full{padding-inline:0}

/* B64 · תגיות שמצביעות על המסר המרכזי */
.aim{position:relative;height:min(78vh,640px);max-width:min(1240px,96vw);margin-inline:auto}
.aim-disc{position:absolute;left:50%;top:50%;width:min(56vh,460px,70vw);aspect-ratio:1;border-radius:50%;background:var(--ink, #16182b);color:var(--bg, #f7f7fa);
  display:grid;place-items:center;text-align:center;padding:9%;box-sizing:border-box;translate:-50% -50%;
  transform:scale(.55);opacity:0;transition:transform .9s cubic-bezier(.2,.6,.2,1),opacity .5s}
.aim-disc p{margin:0;font-size:clamp(22px,2.6vw,38px);line-height:1.2;font-weight:400}
.aim-disc b{display:block;font-weight:800;color:color-mix(in srgb,var(--accent, #4a3aff) 55%,var(--bg, #f7f7fa))}
.aim-chip{position:absolute;left:var(--x);top:var(--y);translate:-50% -50%;transition:transform .8s cubic-bezier(.2,.6,.2,1),opacity .4s;
  transform:translate(var(--fx,0),var(--fy,0)) scale(.4);opacity:0;transition-delay:calc(var(--i) * 70ms + 250ms)}
/* הנדנוד על עטיפה פנימית, כדי שלא יתנגש ב-transform של הכניסה */
.aim-bob{display:block;animation:aim-bob var(--d,5s) ease-in-out infinite alternate;animation-delay:calc(var(--i) * -900ms)}
.aim-pill{display:block;white-space:nowrap;background:var(--accent, #4a3aff);color:var(--accent-ink, #fff);font-weight:700;font-size:clamp(15px,1.35vw,19px);
  padding:12px 22px;border-radius:999px;box-shadow:0 12px 26px -14px color-mix(in srgb,var(--accent, #4a3aff) 80%,transparent);transition:transform .3s cubic-bezier(.2,.6,.2,1)}
.aim-chip:hover .aim-pill{transform:translateY(-3px)}
.aim-ptr{position:absolute;left:0;top:0;width:20px;height:18px;background:var(--accent, #4a3aff);clip-path:polygon(100% 50%,0 0,22% 50%,0 100%);
  transform:translate(var(--px,0),var(--py,0)) translate(-50%,-50%) rotate(var(--a,0deg))}
.aim.in .aim-disc{transform:scale(1);opacity:1}
.aim.in .aim-chip{transform:none;opacity:1}
@keyframes aim-bob{from{transform:translateY(-6px)}to{transform:translateY(6px)}}
@media (max-width:760px){
  .aim{height:auto;min-height:150vw}
  .aim-disc{width:64vw;top:50%}
  .aim-chip{left:var(--mx);top:var(--my)}
}
@media (prefers-reduced-motion: reduce){
  .aim-disc,.aim-chip{transition-duration:.01ms;transition-delay:0s}
  .aim-bob{animation:none}
}

HTML

<div class="stage tight"><div class="aim">
  <div class="aim-disc"><p>אתר שלא רק נראה טוב<b>אלא מביא לקוחות</b></p></div>
  <ul class="aim-chips" style="list-style:none;margin:0;padding:0">
    <li class="aim-chip" style="--i:0;--x:24%;--y:22%;--mx:26%;--my:8%;--d:5.2s"><span class="aim-bob"><span class="aim-pill">אסטרטגיה</span></span><i class="aim-ptr"></i></li>
    <li class="aim-chip" style="--i:1;--x:17%;--y:50%;--mx:74%;--my:8%;--d:6.1s"><span class="aim-bob"><span class="aim-pill">קופי שממיר</span></span><i class="aim-ptr"></i></li>
    <li class="aim-chip" style="--i:2;--x:25%;--y:78%;--mx:50%;--my:15%;--d:5.6s"><span class="aim-bob"><span class="aim-pill">עיצוב</span></span><i class="aim-ptr"></i></li>
    <li class="aim-chip" style="--i:3;--x:76%;--y:21%;--mx:26%;--my:92%;--d:6.4s"><span class="aim-bob"><span class="aim-pill">פיתוח</span></span><i class="aim-ptr"></i></li>
    <li class="aim-chip" style="--i:4;--x:83%;--y:52%;--mx:74%;--my:92%;--d:5s"><span class="aim-bob"><span class="aim-pill">אנימציה</span></span><i class="aim-ptr"></i></li>
    <li class="aim-chip" style="--i:5;--x:75%;--y:80%;--mx:50%;--my:85%;--d:5.8s"><span class="aim-bob"><span class="aim-pill">קידום בגוגל</span></span><i class="aim-ptr"></i></li>
  </ul>
</div></div>

JS

(function(){
  const box=document.querySelector(".aim"),disc=box.querySelector(".aim-disc");
  const chips=[...box.querySelectorAll(".aim-chip")];
  // החץ יושב על שפת התגית בכיוון המרכז. שפת גלולה אינה עיגול, ולכן מחשבים חיתוך של הקרן עם המלבן:
  // המרחק לשפה הוא המינימום בין חצי הרוחב חלקי |cos| לחצי הגובה חלקי |sin|.
  function aim(){
    const b=box.getBoundingClientRect(),d=disc.getBoundingClientRect();
    const cx=d.left+d.width/2-b.left,cy=d.top+d.height/2-b.top;
    chips.forEach(ch=>{
      const pill=ch.querySelector(".aim-pill"),ptr=ch.querySelector(".aim-ptr");
      // מודדים את מיקום התגית בלי הטרנספורם של הכניסה, אחרת לפני הכניסה החץ מכוון ממקום שגוי
      const x=ch.offsetLeft,y=ch.offsetTop; // translate:-50% -50% ממרכז את התגית על הנקודה הזאת
      const dx=cx-x,dy=cy-y,ang=Math.atan2(dy,dx);
      const hw=pill.offsetWidth/2,hh=pill.offsetHeight/2;
      const t=Math.min(hw/Math.max(Math.abs(Math.cos(ang)),1e-3),hh/Math.max(Math.abs(Math.sin(ang)),1e-3))+10;
      ptr.style.setProperty("--px",(pill.offsetWidth/2+Math.cos(ang)*t)+"px");
      ptr.style.setProperty("--py",(pill.offsetHeight/2+Math.sin(ang)*t)+"px");
      ptr.style.setProperty("--a",ang+"rad");
      // נקודת המוצא של הכניסה: מרכז העיגול, כך שהתגיות נראות כאילו יצאו ממנו
      ch.style.setProperty("--fx",(cx-x)*.7+"px");
      ch.style.setProperty("--fy",(cy-y)*.7+"px");
    });
  }
  aim();
  addEventListener("resize",aim);
  if(document.fonts)document.fonts.ready.then(aim);
  const io=new IntersectionObserver(es=>{if(es.some(e=>e.isIntersecting)){box.classList.add("in");io.disconnect();}},{threshold:.35});
  io.observe(box);
})();