Scroll Shadow

لایهٔ نازکی از سایه که روی لبهٔ یک ناحیهٔ اسکرول‌پذیر می‌نشیند و فقط وقتی محتوای بیشتری در آن جهت هست دیده می‌شود. جای اسکرول‌بار سفارشی را نمی‌گیرد؛ کنارش یا به‌جای اسکرول‌بار بومی استفاده می‌شود تا سرریز محتوا بدون هیچ عنصر اضافه‌ای حس شود.

ری‌اکت ۱۹ و Next.js با پیاده‌سازی دسترس‌پذیری داخلی دیگویو ۳ با Composition API و Reka UISvelte ۵ با runes و Bits UIانگولار با signals و Angular CDK

این کامپوننت فعلاً برای ۱ فریم‌ورک از ۴ فریم‌ورک آماده است.

دیزاین‌سیستم مجموعه‌ای از قطعه‌های آماده، قاعده‌های تصمیم و زبان مشترک است که تیم طراحی و توسعه را هم‌جهت نگه می‌دارد. هدفش این نیست که خلاقیت را محدود کند؛ برعکس، با حذف تصمیم‌های تکراری روی جزئیات، وقت تیم را برای مسائل واقعی محصول آزاد می‌کند.

هر کامپوننت در دیگ سه لایه دارد: خودِ نشانه‌گذاری، رفتار تعاملی و توکن‌های بصری. این جدایی یعنی تغییر رنگ برند یا اندازهٔ فونت هیچ‌وقت به معنی نوشتن دوبارهٔ منطق نیست.

راست‌به‌چپ‌بودن یک ویژگیِ اضافه نیست؛ از همان لایهٔ اول تصمیم‌گیری بخشی از معماری بوده. جهت نوشتار، جای دکمه‌ها، انیمیشن‌های ورود و حتی آیکون‌های جهت‌دار همه با dir صفحه هماهنگ می‌شوند.

سایهٔ اسکرول یک نشانهٔ ظریف بصری است، نه یک اسکرول‌بار سفارشی. وقتی محتوا از کادر بیرون می‌زند، کاربر باید بدون نیاز به اسکرول کردن حدس بزند که ادامه‌ای هست؛ یک محو تدریجی در لبه دقیقاً همین کار را می‌کند.

در بسیاری از رابط‌های کاربری، اسکرول‌بار به‌خاطر دلایل زیبایی‌شناسی پنهان می‌شود. اما پنهان‌کردن اسکرول‌بار بدون جایگزین بصری، نشانهٔ سرریز محتوا را از بین می‌برد؛ سایهٔ اسکرول دقیقاً همین خلأ را پر می‌کند.

تفاوت حالت خودکار و حالت ثابت در دیدپذیری سایه مهم است: در حالت خودکار سایه واقعاً موقعیت اسکرول را دنبال می‌کند، اما در حالت ثابت طراح می‌تواند برای یک قاب یا تصویر ثابت، سایه را عمداً روی یک لبه قفل کند.

اندازهٔ سایه و حاشیهٔ فعال‌سازی هر دو قابل تنظیم‌اند تا این کامپوننت هم برای یک لیست باریک کناری و هم برای یک ناحیهٔ بزرگ محتوا به یک اندازه خوب کار کند.

در نهایت، هر قطعه‌ای در این سیستم باید بدون وابستگی بیرونی کار کند؛ فقط Tailwind و ری‌اکت. این یعنی نصب یک کامپوننت هیچ‌وقت باعث نمی‌شود بستهٔ نهاییِ پروژهٔ شما بزرگ‌تر از حد لازم شود.

این نمونه هنوز برای Vue پورت نشده است؛ آنچه می‌بینید نسخهٔ ری‌اکت است.

این نمونه هنوز برای Svelte پورت نشده است؛ آنچه می‌بینید نسخهٔ ری‌اکت است.

این نمونه هنوز برای Angular پورت نشده است؛ آنچه می‌بینید نسخهٔ ری‌اکت است.

نصب

با CLI اختصاصی dig-ui کامپوننت را از رجیستری دیگ نصب کنید؛ وابستگی‌ها و فایل‌ها خودکار اضافه می‌شوند.

نصب سریع با لینک سخت و دیسک مشترکپکیج‌منیجر پیش‌فرض Node.jsYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیج‌منیجر Bun
pnpm dlx dig-ui@latest add https://design-system-tau-green.vercel.app/r/scroll-shadow.json
npx dig-ui@latest add https://design-system-tau-green.vercel.app/r/scroll-shadow.json
yarn dlx dig-ui@latest add https://design-system-tau-green.vercel.app/r/scroll-shadow.json
bunx --bun dig-ui@latest add https://design-system-tau-green.vercel.app/r/scroll-shadow.json

این کامپوننت هنوز برای Vue پورت نشده است.

نسخهٔ ری‌اکت آماده است؛ پورت این فریم‌ورک روی Reka UI در دست کار است.

این کامپوننت هنوز برای Svelte پورت نشده است.

نسخهٔ ری‌اکت آماده است؛ پورت این فریم‌ورک روی Bits UI در دست کار است.

این کامپوننت هنوز برای Angular پورت نشده است.

نسخهٔ ری‌اکت آماده است؛ پورت این فریم‌ورک روی Angular CDK در دست کار است.

استفاده

import { ScrollShadow } from "@/components/ui/scroll-shadow"

<div className="h-72 w-full max-w-sm overflow-hidden rounded-md border">
  <ScrollShadow className="h-full">محتوای بلند…</ScrollShadow>
</div>

این کامپوننت هنوز برای Vue پورت نشده است.

نسخهٔ ری‌اکت آماده است؛ پورت این فریم‌ورک روی Reka UI در دست کار است.

این کامپوننت هنوز برای Svelte پورت نشده است.

نسخهٔ ری‌اکت آماده است؛ پورت این فریم‌ورک روی Bits UI در دست کار است.

این کامپوننت هنوز برای Angular پورت نشده است.

نسخهٔ ری‌اکت آماده است؛ پورت این فریم‌ورک روی Angular CDK در دست کار است.

ترکیب اجزا

پیش‌فرض عمودی است و روی محور y اسکرول می‌شود؛ برای افقی orientation=«horizontal» بدهید و مطمئن شوید فرزندِ داخلی عرض ثابت و بزرگ‌تر از کادر دارد (مثلاً یک ردیف flex با w-max). ScrollShadow خودش هیچ نوار اسکرولی نمی‌سازد، این را بدهید یا با hideScrollbar پنهانش کنید و با ScrollArea یا نوار بومی مرورگر ترکیبش کنید. اگر کادر با border/rounded می‌خواهید، آن را روی یک والدِ overflow-hidden بگذارید نه خودِ ScrollShadow؛ چون سایه با mask-image روی کل عنصر اثر می‌گذارد، اگر border مستقیم روی ScrollShadow باشد لبهٔ کادر هم مثل متن محو و ناپدید می‌شود.

دسترس‌پذیری

  • چون خودِ عنصر overflow-auto دارد، با چرخ ماوس، لمس و کشیدن اسکرول‌بار بومی کار می‌کند؛ چیزی از رفتار پیش‌فرض مرورگر گرفته نمی‌شود.
  • سایه فقط یک نشانهٔ بصری اضافه است، نه جایگزین اطلاع‌رسانی؛ برای کاربرانی که mask-image پشتیبانی نمی‌شود (مرورگرهای بسیار قدیمی) محتوا هنوز کامل قابل اسکرول است، فقط سایه دیده نمی‌شود.
  • برای دسترسی با صفحه‌کلید یک tabIndex={0} و aria-label مناسب روی ScrollShadow بگذارید تا با کلیدهای جهت‌دار قابل اسکرول شود.
  • در حالت hideScrollbar کاربرانی که با موس کار می‌کنند نشانهٔ کمتری برای «چقدر مانده» دارند؛ برای فهرست‌های خیلی بلند اسکرول‌بار را پنهان نکنید.

مرجع API

ScrollShadow

ویژگینوعپیش‌فرضتوضیح
orientation"vertical" | "horizontal""vertical"محور اسکرول و در نتیجه لبه‌هایی که سایه می‌گیرند.
sizenumber40اندازهٔ ناحیهٔ محوشوندهٔ سایه به پیکسل.
offsetnumber0حاشیه‌ای به پیکسل که پیش از رسیدن دقیق به لبه، سایه را نگه می‌دارد یا حذف می‌کند.
hideScrollbarbooleanfalseپنهان‌کردن نوار اسکرول بومی؛ اسکرول با چرخ ماوس/لمس همچنان کار می‌کند.
visibility"auto" | "top" | "bottom" | "left" | "right" | "both" | "none""auto"«auto» سایه را از روی موقعیت واقعیِ اسکرول محاسبه می‌کند. مقادیر دیگر آن را روی یک لبهٔ ثابت قفل می‌کنند (top/bottom برای عمودی، left/right برای افقی).
enabledbooleantrueبا false کاملاً سایه را خاموش می‌کند؛ اسکرول خودش دست‌نخورده می‌ماند.
onVisibilityChange(visibility: "top" | "bottom" | "left" | "right" | "both" | "none") => voidهر بار لبهٔ سرریز واقعاً عوض شود صدا زده می‌شود؛ صرف‌نظر از مقدار visibility.
classNamestringاینجا حتماً ارتفاع یا عرض بدهید؛ بدون اندازهٔ ثابت هیچ‌وقت سرریزی برای نشان‌دادن نیست. اگر کادر دور می‌خواهید، آن را روی یک والدِ overflow-hidden بگذارید نه اینجا.

data-slot

ویژگینوعپیش‌فرضتوضیح
scroll-shadowdivعنصر اسکرول‌پذیر؛ data-orientation و data-edge («none»/«both» یا برحسب جهت: «top»/«bottom»/«left»/«right») هم روی همین عنصرند.

این کامپوننت هنوز برای Vue پورت نشده است.

نسخهٔ ری‌اکت آماده است؛ پورت این فریم‌ورک روی Reka UI در دست کار است.

این کامپوننت هنوز برای Svelte پورت نشده است.

نسخهٔ ری‌اکت آماده است؛ پورت این فریم‌ورک روی Bits UI در دست کار است.

این کامپوننت هنوز برای Angular پورت نشده است.

نسخهٔ ری‌اکت آماده است؛ پورت این فریم‌ورک روی Angular CDK در دست کار است.