Простая семантичная карточка-ссылка
Постановка задачи
Представим себе элемент дизайна - карточку. Карточку товара, или карточку поста в блоге. Суть в том, что по клику в любое место карточки нужно переходить на страницу этого товара или поста.
Первая реализация
Логично предположить, что этот элемент должен быть сделан тегом a
. Как-то так:
<a href="/item">
<h3>Item header</h3>
<p>Item looong description</p>
<img src="/item_img.png"/>
</a>
Проблема
Во-первых это не семантично. Ссылка предполагает наличие внутри себя текста, который описывает, куда она ведет. Помещать туда что-то кроме него - значит путать пользователей со скринридерами и поисковых роботов.
Во-вторых внутри карточки могут предполагаться другие ссылки. Например, на автора статьи или категорию товара:
<a href="/item">
<h3>Item header</h3>
<p>Item looong description</p>
<a href="/author">Ivan Ivanovich</a>
<img src="/item_img.png"/>
</a>
Такая верстка не будет работать. Ссылки нельзя вкладывать внутрь других ссылок. Поведение в таком случае не определено, поэтому браузеры избегают такой верстки и превращают ее в такую:
<a href="/item">
<h3>Item header</h3>
<p>Item looong description</p>
</a><a href="/author">Ivan Ivanovich</a>
<img src="/item_img.png" />
Реализация получше
Все же стоит сделать карточку подходящим для нее тегом, в данном случае article
, а главную
ссылку положить в заголовок:
<article>
<h3><a href="/item">Item header</a></h3>
<p>Item looong description</p>
<img src="/item_img.png"/>
</article>
Теперь верстка семантична и будет нормально прочитана скринридерами, роботами и другими разработчиками.
Как же сделать переход по ссылке по клику в любое место (кроме ссылки на автора), а не только в заголовок? Существует неочевидное решение на CSS для таких случаев.
<article>
<h3><a href="/item">Item header</a></h3>
<p>Item looong description</p>
<img src="/item_img.png"/>
</article>
<style>
article {
position: relative;
}
h3 > a::before {
position: absolute;
content: '';
inset: 0;
}
</style>
Эти стили создали для ссылки внутри заголовка псевдо-элемент размером во всю карточку, который лежит поверх контента. Теперь клик в любое место карточки является кликом в этот псевдо-элемент, то есть кликом в ссылку и вызовет переход на страницу товара или поста.
"Вложенные" ссылки
Чтобы сделать внутри этой карточки ссылку на автора или категорию товара, положим нужные элементы поверх
псевдо-элемента ссылки. Или же опустим псевдо под другие элементы. Значит нужно свойство
z-index
для управления контекстом наложения и pointer-events
для управления
событием клика.
<article>
<h3><a href="/item">Item header</a></h3>
<p>Item looong description</p>
<a href="/author">Ivan Ivanovich</a>
<img src="/item_img.png"/>
</article>
<style>
article {
position: relative;
z-index: 0;
}
h3 > a::before{
position: absolute;
content: '';
inset: 0;
z-index: -1;
}
p, img {
pointer-events: none;
}
</style>
Разберем:
- добавили
z-index: -1
для псевдо-элемента, чтобы убрать его под все остальные элементы в карточке - добавили
z-index: 0
для всей карточки, чтобы создать новый локальный контекст наложения, иначе бы псевдо был в глобальном контексте наложения и убрался под элементbody
- добавили
pointer-events: none;
для описания и картинки, чтобы сделать их прозрачными для событий мыши, теперь клики в них считаются кликами в элемент ровно под ними, то есть в псевдо - элементы внутри карточки, которым мы не указали
pointer-events
(ссылка на автора в данном случае) будут лежать поверх псевдо и не пропускать через себя события мыши, это можно использовать для красивых ховер-эффектов
Более сложные ховер-эффекты
В примере выше по наведению мыши мы только меняем цвет текста для заголовка-ссылки. Это сделано с помощью
псевдокласса :hover
на теге a
. Что делать, если по ховеру нам нужно менять цвет
фона карточки?
Первая мысль: сделать селектор article:hover
и в нем прописать новый цвет фона.
Почему это плохо? Bo-первых, потому что этот псевдокласс предназначен для интерактивных элементов. article
не интерактивный. Во-вторых, этот эффект будет срабатывать, когда пользователь наводит мышку на "вложенные"
ссылки. А должен только при наведении на "главную" ссылку.
Правильнее будет использовать функциональный псевдокласс :has()
и написать так:
article:has(h3 > a:hover) {
background-color: var(--hover-bg);
}
От такого решение нас пока останавливает только поддержка
:has()
в браузерах 😦
Поэтому используем article:hover
, но помним, что это не очень правильное решение.
Нерешенные проблемы
Это решение не идеальное с точки зрения UX. Например, нельзя выделить текст в карточке, не зажав кнопку Alt (option на MacOS). Эта проблема осталась нерешенной.