افکت hover مجاورتی فقط با CSS مدرن
خلاصهٔ کاملتر
نویسنده تو این مقاله سراغ افکتی میره که خودش اسمش رو گذاشته hover proximity: ماوس که روی یه آیتم میره، اون آیتم بزرگتر میشه و آیتمهای نزدیکش هم با ضریب کمتری بزرگ میشن. به گفتهٔ خودش تو حالت یکبعدی (یه ردیف افقی یا عمودی) این کار سادهست و فقط ترکیب چند سلکتوره:
img:hover {
scale: 1.5;
}
img:hover + *,
img:has(+ *:hover) {
scale: 1.2;
}اینجا + آیتم بعدی رو میگیره و :has(+ *:hover) آیتم قبلی رو، و برای فاصلههای دورتر فقط کافیه + * بیشتری اضافه کنی. دردسر از جایی شروع میشه که بخوای همین کار رو تو یه گرید دوبعدی بکنی: آیتم بالایی و پایینی هم در واقع همسایهٔ قبلی و بعدی حساب میشن، ولی تعداد +ها به تعداد ستونها بستگی داره و تو گرید ریسپانسیو تعداد ستونها ثابت نیست. سلکتور دینامیک هم که نداریم.
راهحل نویسنده اینه که بهجای سلکتور بره سراغ مختصات. هر آیتم مختصات ثابت خودش [x, y] رو داره و همهٔ آیتمها هم مختصات آیتمی که ماوس روشه یعنی [i, j] رو میبینن. تعداد ستونها از عرض کانتینر با 100cqw و container query در میآد، تعداد سطرها از sibling-count()، و مختصات هر آیتم از sibling-index() با خارجقسمت و باقیماندهٔ تقسیم بر تعداد ستونها:
.container > * {
--n: round(down,(100cqw + var(--g))/(var(--s) + var(--g)));
--m: round(up,sibling-count()/var(--n));
--x: round(down,(sibling-index() - 1)/var(--n));
--y: mod(sibling-index() - 1,var(--n));
}سختترین قسمت، پیدا کردن مختصات آیتم زیر ماوس بدون جاوااسکریپته. نویسنده اینجا از scroll-driven animation استفاده میکنه: کانتینر با overflow: hidden تبدیل به scroll container میشه، آیتمی که hover شده با view-timeline یه تایملاین سفارشی روی هر دو محور تعریف میکنه، و چون هر آیتم جای متفاوتی تو کانتینر داره، پیشرفت انیمیشن برای هرکدوم یه عدد متفاوت بین ۰ و ۱ میده. اون عدد ضرب در تعداد سطر یا ستون منهای یک، میشه --i و --j.
قدم آخر انتخابه و اونجا if() با style query وارد میشه: اگه --i با --x و --j با --y برابر باشه، آیتم زیر ماوسه، و با calc(var(--x) - 1) میشه سراغ همسایهٔ بالایی رفت و بقیهٔ حالتها هم به همین شکل ساخته میشن. متغیرهای انیمیشن باید با @property ثبت بشن و نویسنده مقدار اولیهشون رو -1 میذاره تا وقتی هیچ آیتمی hover نشده، مختصات با هیچ آیتم واقعی جور در نیاد. یعنی خروجی هم ریسپانسیوه هم بدون جاوااسکریپت، ولی فعلاً فقط Chrome و Edge این قابلیتها رو کامل پشتیبانی میکنن.
نکات کلیدی:
- تو چیدمان یکبعدی، + همسایهٔ بعدی و :has(+ *:hover) همسایهٔ قبلی رو انتخاب میکنه
- تعداد ستونها با round(down,(100cqw + var(--g))/(var(--s) + var(--g))) و تعداد سطرها با sibling-count() حساب میشه
- مختصات هر آیتم از sibling-index() میآد: x خارجقسمت و y باقیماندهٔ تقسیم بر تعداد ستونها
- مختصات آیتم hover شده با view-timeline روی دو محور و بدون جاوااسکریپت به دست میآد
- شرطگذاری نهایی با if() و style query انجام میشه و متغیرها باید با @property و مقدار اولیهٔ -1 ثبت بشن
- شرط اصلی این روش اینه که همهٔ ستونها همعرض و همهٔ سطرها همارتفاع باشن، و فعلاً فقط Chrome و Edge پشتیبانی کامل دارن




