→ לכל המאגר

B23 · תפריט מסך מלא עם כניסה מדורגת

התנהגויות CSS clip-path · JS ניווט ומבנה ממתין

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

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

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

הדמו

לוגו

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

סוף העמוד.

שלושה דברים שבדרך כלל שוכחים: נעילת הגלילה חייבת להחזיר את רוחב פס הגלילה כריפוד אחרת העמוד קופץ ברגע הפתיחה, aria-expanded ו-aria-hidden חייבים להתעדכן, והטאב חייב להיתפס בתוך השכבה. ה-clip-path נותן פתיחה שנפרשת מלמעלה למטה במקום דהייה סתמית.

קוד להדבקה

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

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

CSS

/* B23 · תפריט מסך מלא עם כניסה מדורגת */
.nv-bar{position:sticky;top:0;z-index:30;display:flex;align-items:center;justify-content:space-between;padding:16px var(--gutter, clamp(24px, 5vw, 96px));background:color-mix(in srgb,var(--bg, #f7f7fa) 86%,transparent);backdrop-filter:blur(10px);border-bottom:1px solid var(--line, #e4e4ee)}
.nv-toggle{display:flex;align-items:center;gap:10px;background:none;border:0;font:inherit;font-size:15px;cursor:pointer;color:var(--ink, #16182b);padding:6px}
.nv-burger{width:26px;height:14px;position:relative;display:block}
.nv-burger i{position:absolute;inset-inline:0;height:2px;background:var(--ink, #16182b);border-radius:2px;transition:transform .4s cubic-bezier(.2,.6,.2,1),opacity .25s}
.nv-burger i:nth-child(1){top:0}.nv-burger i:nth-child(2){top:6px}.nv-burger i:nth-child(3){top:12px}
.nv-open .nv-burger i:nth-child(1){transform:translateY(6px) rotate(45deg)}
.nv-open .nv-burger i:nth-child(2){opacity:0}
.nv-open .nv-burger i:nth-child(3){transform:translateY(-6px) rotate(-45deg)}
.nv-overlay{position:fixed;inset:0;z-index:29;background:var(--ink, #16182b);color:var(--bg, #f7f7fa);display:grid;align-content:center;gap:6px;padding:12vh var(--gutter, clamp(24px, 5vw, 96px)) 8vh;
  clip-path:inset(0 0 100% 0);transition:clip-path .62s cubic-bezier(.76,0,.24,1);visibility:hidden}
.nv-open .nv-overlay{clip-path:inset(0 0 0 0);visibility:visible}
.nv-link{display:block;font-size:clamp(34px,6vw,86px);font-weight:800;line-height:1.1;color:var(--bg, #f7f7fa);text-decoration:none;
  opacity:0;transform:translateY(28px);transition:opacity .5s,transform .5s;transition-delay:0s}
.nv-open .nv-link{opacity:1;transform:none}
.nv-open .nv-link:nth-child(1){transition-delay:.18s}
.nv-open .nv-link:nth-child(2){transition-delay:.25s}
.nv-open .nv-link:nth-child(3){transition-delay:.32s}
.nv-open .nv-link:nth-child(4){transition-delay:.39s}
.nv-link span{display:inline-block;transition:transform .35s cubic-bezier(.2,.6,.2,1),color .35s}
/* הזזה בכיוון הקריאה: ב-RTL שמאלה, ב-LTR ימינה */
.nv-link:hover span{transform:translateX(-14px);color:var(--accent, #4a3aff)}
[dir="ltr"] .nv-link:hover span{transform:translateX(14px)}
.nv-link small{font-size:14px;font-weight:500;opacity:.55;margin-inline-start:14px;vertical-align:middle}
.nv-foot{margin-top:8vh;display:flex;gap:22px;flex-wrap:wrap;font-size:14px;color:var(--bg, #f7f7fa);opacity:0;transition:opacity .5s .5s}
.nv-foot span{opacity:.62}
.nv-open .nv-foot{opacity:1}
/* עמוד כהה: הפוכים. השכבה מקבלת את הרקע של העמוד והטקסט את הדיו, בלי צבע קשיח */
@media (prefers-color-scheme: dark){.nv-overlay{background:var(--bg, #f7f7fa);color:var(--ink, #16182b)}.nv-link,.nv-foot{color:var(--ink, #16182b)}}
.nv-body{padding:var(--sec, clamp(120px, 12vw, 260px)) var(--gutter, clamp(24px, 5vw, 96px));max-width:60ch;margin-inline:auto;color:var(--muted, #6a6d85)}
@media (prefers-reduced-motion: reduce){.nv-overlay,.nv-link,.nv-foot{transition-duration:.01ms!important}}

HTML

<header class="nv-bar">
  <strong>לוגו</strong>
  <button class="nv-toggle" aria-expanded="false" aria-controls="nvmenu"><span class="nv-label">תפריט</span><span class="nv-burger" aria-hidden="true"><i></i><i></i><i></i></span></button>
</header>
<nav class="nv-overlay" id="nvmenu" aria-hidden="true">
  <a class="nv-link" href="#"><span>עבודות</span><small>01</small></a>
  <a class="nv-link" href="#"><span>שירותים</span><small>02</small></a>
  <a class="nv-link" href="#"><span>אודות</span><small>03</small></a>
  <a class="nv-link" href="#"><span>יצירת קשר</span><small>04</small></a>
  <div class="nv-foot"><span>info@example.com</span><span>03-1234567</span><span>אינסטגרם</span></div>
</nav>
<div class="nv-body"><p>גלול קצת, ואז פתח את התפריט. הגלילה מאחור ננעלת בלי שהעמוד יקפוץ, כי רוחב פס הגלילה מוחזר כריפוד.</p><p style="height:150vh"></p><p>סוף העמוד.</p></div>

JS

(function(){
  const btn=document.querySelector(".nv-toggle"),ov=document.querySelector(".nv-overlay"),root=document.documentElement;
  const links=[...ov.querySelectorAll(".nv-link")];
  let open=false,lastFocus=null;
  function set(v){
    open=v;
    document.body.classList.toggle("nv-open",v);
    btn.setAttribute("aria-expanded",String(v));
    ov.setAttribute("aria-hidden",String(!v));
    btn.querySelector(".nv-label").textContent=v?"סגירה":"תפריט";
    // נעילת גלילה בלי קפיצה: מחזירים את רוחב פס הגלילה כריפוד
    const sb=window.innerWidth-root.clientWidth;
    document.body.style.overflow=v?"hidden":"";
    document.body.style.paddingInlineEnd=v?sb+"px":"";
    if(v){lastFocus=document.activeElement;setTimeout(()=>links[0].focus(),260);}
    else if(lastFocus)lastFocus.focus();
  }
  btn.addEventListener("click",()=>set(!open));
  links.forEach(a=>a.addEventListener("click",()=>set(false)));
  addEventListener("keydown",e=>{
    if(!open)return;
    if(e.key==="Escape"){set(false);return;}
    if(e.key!=="Tab")return;
    // מלכודת פוקוס: הטאב לא בורח מאחורי השכבה
    const f=[btn,...links];
    const i=f.indexOf(document.activeElement);
    const next=e.shiftKey?(i<=0?f.length-1:i-1):(i===f.length-1?0:i+1);
    e.preventDefault();f[next].focus();
  });
})();