یک نمای کلی از نحوه ساخت یک عنصر سفارشی راهنمای رنگی و قابل دسترس.
در این پست میخواهم نظرات خود را در مورد چگونگی ساخت یک عنصر سفارشی سازگار با رنگ و در دسترس <tool-tip>
به اشتراک بگذارم. نسخه ی نمایشی را امتحان کنید و منبع را مشاهده کنید !
اگر ویدیو را ترجیح می دهید، در اینجا یک نسخه YouTube از این پست وجود دارد:
نمای کلی
راهنمای ابزار یک پوشش غیرمدال، غیر مسدود کننده و غیر تعاملی است که حاوی اطلاعات تکمیلی برای رابط های کاربر است. به طور پیشفرض پنهان میشود و هنگامی که یک عنصر مرتبط با ماوس یا فوکوس قرار میگیرد، پنهان میشود. یک راهنمای ابزار را نمی توان مستقیماً انتخاب کرد یا با آن تعامل کرد. راهنمای ابزار جایگزینی برای برچسب ها یا سایر اطلاعات با ارزش بالا نیست، کاربر باید بتواند کار خود را بدون راهنمایی ابزار به طور کامل انجام دهد.
Toggletip در مقابل Tooltip
مانند بسیاری از مؤلفهها، توصیفهای متفاوتی از چیستی یک tooltip وجود دارد، برای مثال در MDN ، WAI ARIA ، Sarah Higley و Inclusive Components . من از تفکیک بین راهنمای ابزار و ضامن خوشم می آید. یک راهنمای ابزار باید حاوی اطلاعات تکمیلی غیرتعاملی باشد، در حالی که یک toggletip میتواند حاوی اطلاعات تعاملی و مهم باشد. دلیل اصلی این شکاف دسترسی است، اینکه چگونه از کاربران انتظار می رود به پنجره بازشو رفته و به اطلاعات و دکمه های داخل دسترسی داشته باشند. Toggletip ها به سرعت پیچیده می شوند.
در اینجا یک ویدیو از یک toggletip از سایت Designcember آمده است. پوششی با تعاملی که کاربر می تواند آن را باز کند و کاوش کند، سپس با رد کردن نور یا کلید فرار ببندد:
این چالش رابط کاربری گرافیکی مسیر یک راهنمای ابزار را طی کرد و به دنبال انجام تقریباً همه کارها با CSS بود و در اینجا نحوه ساخت آن آورده شده است.
نشانه گذاری
من استفاده از عنصر سفارشی <tool-tip>
را انتخاب کردم. نویسندگان اگر مایل نباشند نیازی به ایجاد عناصر سفارشی در اجزای وب ندارند. مرورگر با <foo-bar>
مانند <div>
رفتار می کند. شما می توانید یک عنصر سفارشی مانند نام کلاس را با ویژگی کمتر در نظر بگیرید. هیچ جاوا اسکریپتی در کار نیست.
<tool-tip>A tooltip</tool-tip>
این مانند یک div با مقداری متن در داخل است. میتوانیم با افزودن [role="tooltip"]
به درخت دسترسپذیری صفحهخوانهای توانمند متصل شویم.
<tool-tip role="tooltip">A tooltip</tool-tip>
اکنون، برای صفحهخوانها، به عنوان یک راهنمای ابزار شناخته میشود. در مثال زیر ببینید که چگونه عنصر پیوند اول دارای یک عنصر راهنمای شناسایی شده در درخت خود است و دومی ندارد؟ دومی این نقش را ندارد. در بخش سبکها، این نمای درختی را بهبود خواهیم داد.
بعد ما به راهنمای ابزار نیاز داریم تا قابل فوکوس نباشد. اگر یک صفحهخوان نقش راهنمای ابزار را درک نکند، به کاربران اجازه میدهد تا روی <tool-tip>
برای خواندن مطالب تمرکز کنند، و تجربه کاربر به این نیاز ندارد. صفحهخوانها محتوا را به عنصر والد اضافه میکنند و به این ترتیب، برای دسترسی به آن نیازی به تمرکز ندارد. در اینجا میتوانیم از inert
استفاده کنیم تا مطمئن شویم هیچ کاربری بهطور تصادفی این محتوای راهنمای ابزار را در جریان برگههای خود پیدا نخواهد کرد:
<tool-tip inert role="tooltip">A tooltip</tool-tip>
سپس استفاده از ویژگی ها را به عنوان رابط برای تعیین موقعیت راهنمای ابزار انتخاب کردم. بهطور پیشفرض، همه <tool-tip>
موقعیت «بالا» را به خود میگیرند، اما موقعیت را میتوان روی یک عنصر با افزودن tip-position
سفارشی کرد:
<tool-tip role="tooltip" tip-position="right ">A tooltip</tool-tip>
من تمایل دارم از ویژگی ها به جای کلاس ها برای مواردی مانند این استفاده کنم تا <tool-tip>
نتواند چندین موقعیت را به طور همزمان به آن اختصاص دهد. فقط یکی می تواند باشد یا هیچ.
در نهایت، عناصر <tool-tip>
را در داخل عنصری که میخواهید برای آن راهنمای ابزار ارائه کنید، قرار دهید. در اینجا من متن alt
را با قرار دادن یک تصویر و یک <tool-tip>
در داخل عنصر <picture>
با کاربران بینا به اشتراک می گذارم:
<picture>
<img alt="The GUI Challenges skull logo" width="100" src="...">
<tool-tip role="tooltip" tip-position="bottom">
The <b>GUI Challenges</b> skull logo
</tool-tip>
</picture>
در اینجا من یک <tool-tip>
را در داخل عنصر <abbr>
قرار می دهم:
<p>
The <abbr>HTML <tool-tip role="tooltip" tip-position="top">Hyper Text Markup Language</tool-tip></abbr> abbr element.
</p>
دسترسی
از آنجایی که من ساختن راهنمای ابزار و نه toggletips را انتخاب کرده ام، این بخش بسیار ساده تر است. ابتدا اجازه دهید تجربه کاربری مورد نظر ما را شرح دهم:
- در فضاهای محدود یا رابط های به هم ریخته، پیام های تکمیلی را پنهان کنید.
- هنگامی که کاربر شناور می شود، فوکوس می کند یا از لمس برای تعامل با یک عنصر استفاده می کند، پیام را آشکار می کند.
- وقتی نشانگر، فوکوس یا لمس به پایان رسید، پیام را دوباره پنهان کنید.
- در نهایت، اگر کاربر ترجیحی برای کاهش حرکت مشخص کرده باشد، اطمینان حاصل کنید که هرگونه حرکت کاهش می یابد.
هدف ما ارسال پیام تکمیلی بر اساس تقاضا است. یک کاربر بینا ماوس یا صفحه کلید می تواند برای فاش کردن پیام و خواندن آن با چشمان خود شناور را نشان دهد. یک کاربر صفحهخوان غیر بینا میتواند برای فاش کردن پیام تمرکز کند و آن را به صورت شنیداری از طریق ابزار خود دریافت کند.
در بخش قبل، درخت دسترسی، نقش راهنمای ابزار و بی اثر را پوشش دادیم، آنچه باقی می ماند آزمایش آن است و تأیید تجربه کاربر به طور مناسب پیام راهنمای ابزار را به کاربر نشان می دهد. پس از آزمایش، مشخص نیست که کدام قسمت از پیام قابل شنیدن یک راهنمای ابزار است. هنگام اشکالزدایی در درخت دسترسی نیز مشاهده میشود، متن پیوند «بالا» با هم اجرا میشود، بدون تردید، با «نگاه کنید، راهنمای ابزار!». صفحه خوان متن را به عنوان محتوای راهنمای ابزار نمی شکند یا شناسایی نمی کند.
فقط یک شبه عنصر صفحه خوان را به <tool-tip>
اضافه کنید و می توانیم متن درخواستی خود را برای کاربران غیر بینا اضافه کنیم.
&::before {
content: "; Has tooltip: ";
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
width: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
}
در زیر می توانید درخت دسترسی به روز شده را مشاهده کنید که اکنون دارای یک نقطه ویرگول بعد از متن پیوند و یک اعلان برای راهنمای ابزار "Has tooltip: " است.
اکنون، زمانی که کاربر صفحهخوان روی پیوند متمرکز میشود، میگوید «بالا» و مکث کوچکی میکند، سپس «دارای راهنمای ابزار: نگاه، راهنماییهای ابزار» را اعلام میکند. این به کاربر صفحهخوان چند نکته UX خوب میدهد. تردید بین متن پیوند و راهنمای ابزار تفکیک خوبی ایجاد می کند. به علاوه، هنگامی که "دارای راهنمای ابزار" اعلام می شود، کاربر صفحه خوان می تواند به راحتی آن را لغو کند اگر قبلاً آن را شنیده باشد. همانطور که قبلاً پیام تکمیلی را مشاهده کرده اید، بسیار یادآور شناور و باز کردن سریع است. این یک برابری UX خوبی بود.
سبک ها
عنصر <tool-tip>
فرزند عنصری است که پیامهای تکمیلی را برای آن نشان میدهد، بنابراین اجازه دهید ابتدا با موارد ضروری برای جلوه همپوشانی شروع کنیم. با position absolute
آن را از جریان سند خارج کنید:
tool-tip {
position: absolute;
z-index: 1;
}
اگر والد یک زمینه انباشته نیست، راهنمای ابزار خود را در نزدیکترین متن قرار می دهد، که آن چیزی نیست که ما می خواهیم. یک انتخابگر جدید در بلوک وجود دارد که می تواند کمک کند، :has()
:
:has(> tool-tip) {
position: relative;
}
زیاد نگران پشتیبانی مرورگر نباشید. اول، به یاد داشته باشید که این نکات مکمل هستند. اگر کار نمی کنند باید خوب باشد. دوم، در بخش جاوا اسکریپت، یک اسکریپت برای پر کردن عملکرد مورد نیاز مرورگرهای بدون پشتیبانی :has()
مستقر می کنیم.
در مرحله بعد، بیایید نکات ابزار را غیر تعاملی کنیم تا رویدادهای اشاره گر را از عنصر والد خود نبرند:
tool-tip {
…
pointer-events: none;
user-select: none;
}
سپس، راهنمای ابزار را با کدورت مخفی کنید تا بتوانیم راهنمای ابزار را با یک crossfade انتقال دهیم:
tool-tip {
opacity: 0;
}
:has(> tool-tip):is(:hover, :focus-visible, :active) > tool-tip {
opacity: 1;
}
:is()
و :has()
کارهای سنگین را در اینجا انجام میدهند، و باعث میشوند که tool-tip
حاوی عناصر والد از تعامل کاربر برای تغییر قابلیت مشاهده یک راهنمای ابزار کودک آگاه شوند. کاربران ماوس میتوانند شناور کنند، کاربران صفحهکلید و صفحهخوان میتوانند تمرکز کنند، و کاربران لمسی میتوانند ضربه بزنند.
با توجه به اینکه پوشش نمایش و پنهان برای کاربران بینا کار می کند، زمان آن رسیده است که چند سبک برای طرح زمینه، موقعیت یابی و اضافه کردن شکل مثلث به حباب اضافه کنید. سبکهای زیر با استفاده از ویژگیهای سفارشی شروع میشوند، بر اساس جایی که تا اینجای کار بودهایم، اما همچنین سایهها، تایپوگرافی و رنگها را اضافه میکنند تا مانند یک راهنمای ابزار شناور به نظر برسد:
tool-tip {
--_p-inline: 1.5ch;
--_p-block: .75ch;
--_triangle-size: 7px;
--_bg: hsl(0 0% 20%);
--_shadow-alpha: 50%;
--_bottom-tip: conic-gradient(from -30deg at bottom, rgba(0,0,0,0), #000 1deg 60deg, rgba(0,0,0,0) 61deg) bottom / 100% 50% no-repeat;
--_top-tip: conic-gradient(from 150deg at top, rgba(0,0,0,0), #000 1deg 60deg, rgba(0,0,0,0) 61deg) top / 100% 50% no-repeat;
--_right-tip: conic-gradient(from -120deg at right, rgba(0,0,0,0), #000 1deg 60deg, rgba(0,0,0,0) 61deg) right / 50% 100% no-repeat;
--_left-tip: conic-gradient(from 60deg at left, rgba(0,0,0,0), #000 1deg 60deg, rgba(0,0,0,0) 61deg) left / 50% 100% no-repeat;
pointer-events: none;
user-select: none;
opacity: 0;
transform: translateX(var(--_x, 0)) translateY(var(--_y, 0));
transition: opacity .2s ease, transform .2s ease;
position: absolute;
z-index: 1;
inline-size: max-content;
max-inline-size: 25ch;
text-align: start;
font-size: 1rem;
font-weight: normal;
line-height: normal;
line-height: initial;
padding: var(--_p-block) var(--_p-inline);
margin: 0;
border-radius: 5px;
background: var(--_bg);
color: CanvasText;
will-change: filter;
filter:
drop-shadow(0 3px 3px hsl(0 0% 0% / var(--_shadow-alpha)))
drop-shadow(0 12px 12px hsl(0 0% 0% / var(--_shadow-alpha)));
}
/* create a stacking context for elements with > tool-tips */
:has(> tool-tip) {
position: relative;
}
/* when those parent elements have focus, hover, etc */
:has(> tool-tip):is(:hover, :focus-visible, :active) > tool-tip {
opacity: 1;
transition-delay: 200ms;
}
/* prepend some prose for screen readers only */
tool-tip::before {
content: "; Has tooltip: ";
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
width: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
}
/* tooltip shape is a pseudo element so we can cast a shadow */
tool-tip::after {
content: "";
background: var(--_bg);
position: absolute;
z-index: -1;
inset: 0;
mask: var(--_tip);
}
/* top tooltip styles */
tool-tip:is(
[tip-position="top"],
[tip-position="block-start"],
:not([tip-position]),
[tip-position="bottom"],
[tip-position="block-end"]
) {
text-align: center;
}
تنظیمات تم
راهنمای ابزار فقط دارای چند رنگ برای مدیریت است زیرا رنگ متن از طریق کلمه کلیدی سیستم CanvasText
از صفحه به ارث می رسد. همچنین، از آنجایی که ویژگیهای سفارشی را برای ذخیره مقادیر ایجاد کردهایم، میتوانیم فقط آن ویژگیهای سفارشی را بهروزرسانی کنیم و به تم اجازه دهیم بقیه موارد را مدیریت کند:
@media (prefers-color-scheme: light) {
tool-tip {
--_bg: white;
--_shadow-alpha: 15%;
}
}
برای تم روشن، پسزمینه را به رنگ سفید تطبیق میدهیم و سایهها را با تنظیم تیرگی آنها بسیار کمتر میکنیم.
راست به چپ
برای پشتیبانی از حالتهای خواندن راست به چپ، یک ویژگی سفارشی مقدار جهت سند را به ترتیب در مقدار ۱- یا ۱ ذخیره میکند.
tool-tip {
--isRTL: -1;
}
tool-tip:dir(rtl) {
--isRTL: 1;
}
این می تواند برای کمک به قرار دادن راهنمای ابزار استفاده شود:
tool-tip[tip-position="top"]) {
--_x: calc(50% * var(--isRTL));
}
و همچنین کمک به جایی که مثلث است:
tool-tip[tip-position="right"]::after {
--_tip: var(--_left-tip);
}
tool-tip[tip-position="right"]:dir(rtl)::after {
--_tip: var(--_right-tip);
}
در نهایت، همچنین می تواند برای تبدیل های منطقی در translateX()
استفاده شود:
--_x: calc(var(--isRTL) * -3px * -1);
موقعیت یابی نوک ابزار
راهنمای ابزار را به صورت منطقی با ویژگی های inset-block
یا inset-inline
قرار دهید تا موقعیت های فیزیکی و منطقی راهنمای ابزار را مدیریت کنید. کد زیر نشان می دهد که چگونه هر یک از چهار موقعیت برای هر دو جهت چپ به راست و راست به چپ استایل بندی شده اند.
تراز بالا و بلوک-شروع
tool-tip:is([tip-position="top"], [tip-position="block-start"], :not([tip-position])) {
inset-inline-start: 50%;
inset-block-end: calc(100% + var(--_p-block) + var(--_triangle-size));
--_x: calc(50% * var(--isRTL));
}
tool-tip:is([tip-position="top"], [tip-position="block-start"], :not([tip-position]))::after {
--_tip: var(--_bottom-tip);
inset-block-end: calc(var(--_triangle-size) * -1);
border-block-end: var(--_triangle-size) solid transparent;
}
تراز سمت راست و درون خطی
tool-tip:is([tip-position="right"], [tip-position="inline-end"]) {
inset-inline-start: calc(100% + var(--_p-inline) + var(--_triangle-size));
inset-block-end: 50%;
--_y: 50%;
}
tool-tip:is([tip-position="right"], [tip-position="inline-end"])::after {
--_tip: var(--_left-tip);
inset-inline-start: calc(var(--_triangle-size) * -1);
border-inline-start: var(--_triangle-size) solid transparent;
}
tool-tip:is([tip-position="right"], [tip-position="inline-end"]):dir(rtl)::after {
--_tip: var(--_right-tip);
}
تراز پایین و بلوک انتهایی
tool-tip:is([tip-position="bottom"], [tip-position="block-end"]) {
inset-inline-start: 50%;
inset-block-start: calc(100% + var(--_p-block) + var(--_triangle-size));
--_x: calc(50% * var(--isRTL));
}
tool-tip:is([tip-position="bottom"], [tip-position="block-end"])::after {
--_tip: var(--_top-tip);
inset-block-start: calc(var(--_triangle-size) * -1);
border-block-start: var(--_triangle-size) solid transparent;
}
تراز چپ و خطی-شروع
tool-tip:is([tip-position="left"], [tip-position="inline-start"]) {
inset-inline-end: calc(100% + var(--_p-inline) + var(--_triangle-size));
inset-block-end: 50%;
--_y: 50%;
}
tool-tip:is([tip-position="left"], [tip-position="inline-start"])::after {
--_tip: var(--_right-tip);
inset-inline-end: calc(var(--_triangle-size) * -1);
border-inline-end: var(--_triangle-size) solid transparent;
}
tool-tip:is([tip-position="left"], [tip-position="inline-start"]):dir(rtl)::after {
--_tip: var(--_left-tip);
}
انیمیشن
تا کنون ما فقط قابلیت مشاهده راهنمای ابزار را تغییر داده ایم. در این بخش ابتدا کدورت را برای همه کاربران متحرک می کنیم، زیرا این یک انتقال حرکت کاهش یافته به طور کلی ایمن است. سپس موقعیت تبدیل را متحرک می کنیم تا راهنمای ابزار از عنصر والد خارج شود.
یک انتقال پیشفرض امن و معنادار
عنصر tooltip را به شکل کدورت انتقال و تبدیل کنید، مانند این:
tool-tip {
opacity: 0;
transform: translateX(var(--_x, 0)) translateY(var(--_y, 0));
transition: opacity .2s ease, transform .2s ease;
}
:has(> tool-tip):is(:hover, :focus-visible, :active) > tool-tip {
opacity: 1;
transition-delay: 200ms;
}
اضافه کردن حرکت به انتقال
برای هر یک از اضلاع، یک راهنمای ابزار میتواند روی آن ظاهر شود، اگر کاربر با حرکت مشکلی ندارد، کمی ویژگی translateX را با فاصله کمی برای طی کردن آن قرار دهید:
@media (prefers-reduced-motion: no-preference) {
:has(> tool-tip:is([tip-position="top"], [tip-position="block-start"], :not([tip-position]))):not(:hover):not(:focus-visible):not(:active) tool-tip {
--_y: 3px;
}
:has(> tool-tip:is([tip-position="right"], [tip-position="inline-end"])):not(:hover):not(:focus-visible):not(:active) tool-tip {
--_x: -3px;
}
:has(> tool-tip:is([tip-position="bottom"], [tip-position="block-end"])):not(:hover):not(:focus-visible):not(:active) tool-tip {
--_y: -3px;
}
:has(> tool-tip:is([tip-position="left"], [tip-position="inline-start"])):not(:hover):not(:focus-visible):not(:active) tool-tip {
--_x: 3px;
}
}
توجه داشته باشید که این حالت "out" را تنظیم می کند، زیرا حالت "in" در translateX(0)
است.
جاوا اسکریپت
به نظر من جاوا اسکریپت اختیاری است. این به این دلیل است که برای انجام یک کار در UI شما نباید خواندن هیچ یک از این نکات ابزار ضروری باشد. بنابراین اگر راهنمای ابزار کاملاً از کار بیفتد، نباید مشکل بزرگی باشد. این همچنین به این معنی است که ما میتوانیم نکات ابزار را بهطور تدریجی بهبود ببخشیم. در نهایت همه مرورگرها از :has()
پشتیبانی می کنند و این اسکریپت می تواند کاملاً از بین برود.
اسکریپت polyfill دو کار انجام می دهد و این کار را فقط در صورتی انجام می دهد که مرورگر از :has()
پشتیبانی نکند. ابتدا پشتیبانی :has()
را بررسی کنید:
if (!CSS.supports('selector(:has(*))')) {
// do work
}
سپس، عناصر والد <tool-tip>
s را پیدا کنید و به آنها یک نام کلاس بدهید تا با آنها کار کنند:
if (!CSS.supports('selector(:has(*))')) {
document.querySelectorAll('tool-tip').forEach(tooltip =>
tooltip.parentNode.classList.add('has_tool-tip'))
}
در مرحله بعد، مجموعه ای از سبک ها را تزریق کنید که از نام کلاس استفاده می کنند و انتخابگر :has()
دقیقاً برای همان رفتار شبیه سازی می کنند:
if (!CSS.supports('selector(:has(*))')) {
document.querySelectorAll('tool-tip').forEach(tooltip =>
tooltip.parentNode.classList.add('has_tool-tip'))
let styles = document.createElement('style')
styles.textContent = `
.has_tool-tip {
position: relative;
}
.has_tool-tip:is(:hover, :focus-visible, :active) > tool-tip {
opacity: 1;
transition-delay: 200ms;
}
`
document.head.appendChild(styles)
}
تمام شد، اکنون همه مرورگرها با خوشحالی نکات ابزار را نشان می دهند اگر :has()
پشتیبانی نشود.
نتیجه گیری
popup
که میدانید من چگونه این anchor
را انجام دادم ، چطور؟ تا آن زمان، من نکات ابزار را خواهم ساخت.
بیایید رویکردهای خود را متنوع کنیم و همه راههای ساخت در وب را بیاموزیم.
یک نسخه نمایشی ایجاد کنید، پیوندها را برای من توییت کنید ، و من آن را به بخش ریمیکس های انجمن در زیر اضافه می کنم!
ریمیکس های انجمن
هنوز چیزی برای دیدن اینجا وجود ندارد.
منابع
- کد منبع در Github