Заметка о полезных возможностях современного CSS

—

от автора

Привет, друзья!

В данной заметке я расскажу вам о некоторых полезных возможностях, предоставляемых современным CSS. Также мы немного поговорим о полезных «фичах», которые ждут нас в ближайшие 2 года.

«Полезный» означает, что я либо часто использую фичу в своих проектах, либо с нетерпением жду такой возможности.

Существующие возможности

Начнем с существующих возможностей, поддерживаемых большинством браузеров.

:not()

Функция-псевдокласс :not() позволяет стилизовать элементы, которые не совпадают ни с одним селектором из указанного списка. Список может состоять как из одного, так и из нескольких селекторов, разделенных запятыми. Селекторы могут быть как простыми, так и сложными.

Синтаксис

:not(selector1, selector2, ...selectorN) {}

Поддержка — 93.04%.

Пример

Предположим, что у нас имеется такой инпут:

<input type="text" placeholder="Enter some text..." required />

Мы хотим, чтобы в невалидном состоянии границы этого поля были красного цвета. «Невалидность» поля можно стилизовать с помощью псевдокласса :invalid. При этом, стилизация невалидности не должна влиять на стилизацию наведения и фокусировки. Это можно реализовать следующим образом:

input:invalid:not(:hover, :focus) {   border-color: red; }  /* отключаем подсветку */ :invalid {   box-shadow: none; }  :-moz-submit-invalid {   box-shadow: none; }  :-moz-ui-invalid {   box-shadow: none; }

Лирическое отступление: стилизация placeholder

К сожалению, на сегодняшний день заменители текста приходится стилизовать так:

input.placeholder {   color: #0275d8;   font-size: 0.8rem;   opacity: 0.8; } input:-moz-placeholder {   color: #0275d8;   font-size: 0.8rem;   opacity: 0.8; } input::-moz-placeholder {   color: #0275d8;   font-size: 0.8rem;   opacity: 0.8; } input:-ms-input-placeholder {   color: #0275d8;   font-size: 0.8rem;   opacity: 0.8; } input::-webkit-input-placeholder {   color: #0275d8;   font-size: 0.8rem;   opacity: 0.8; }

В ближайшее время это вряд ли изменится.

:empty

Псевдокласс :empty позволяет стилизовать элементы, которые не имеют потомков. К потомкам относятся элементы, текст и (sic!) пробелы.

Синтаксис

:empty {}

Поддержка — 97.65%.

Пример

Предположим, что у нас имеется такой список ссылок:

<ul>   <li><a href="#">Link 1</a></li>   <li>     <a href="#" target="_blank" rel="noopener noreferrer">Link 2</a>   </li>   <li></li> </ul>

С такими стилями:

ul {   display: inline-flex;   flex-direction: column;   /* отступы между элементами списка */   gap: 0.5rem;   list-style: none; }

Допустим, что список формируется динамически и последний элемент по какой-то причине оказался пустым, например, в объекте не было url для ссылки. Тогда после второго элемента получим лишний отступ:

Скрываем пустые элементы списка с помощью :empty:

li:empty {   display: none; }

Лишнего отступа больше нет:

Лирическое отступление: невидимый контент

display: none; полностью скрывает элемент. Сделать элемент невидимым, но доступным для устройств чтения с экрана, можно следующим образом:

.sr-only {   background: none;   border: none;   color: none;   cursor: none;   height: 0;   margin: 0;   opacity: 0;   outline: none;   overflow: hidden;   padding: 0;   pointer-events: none;   position: absolute;   user-select: none;   visibility: hidden;   white-space: nowrap;   width: 0;   z-index: -1; }

:is() и :where()

Функции-псевдоклассы :is() и :where() позволяют стилизовать элементы, совпадающие с любым селектором из указанного списка. Список может состоять как из одного, так и из нескольких селекторов, разделенных запятыми. Селекторы могут быть как простыми, так и сложными.

Разница между :is() и :where() заключается в том, что :is() принимает специфичность самого конкретного селектора из списка, а специфичность :where() всегда равняется 0.

Синтаксис

:is(selector1, selector2, ...selectorN) {} :where(selector1, selector2, ...selectorN) {}

Поддержка :is() — 94.9%.
Поддержка :where() — 91.61%.

Пример

Предположим, что мы хотим стилизовать ссылки, находящиеся только в шапке или подвале страницы:

:is(header, footer) a:hover {   color: green; }

Для понимания того, насколько :is() и :where() могут уменьшить количество шаблонного кода, рекомендую взглянуть на этот пример.

:focus-within

Псевдокласс :focus-within позволяет стилизовать элементы, которые либо сами находятся в фокусе (в этом случае :focus-within аналогичен псевдоклассу :focus), либо имеют потомков, находящихся в фокусе.

Синтаксис

:focus-within {}

Поддержка — 95.32%.

Пример

Предположим, что у нас имеется такой инпут с подписью и иконкой:

<div class="form-field">   <label>     <span>Some text:</span>     <input type="text" placeholder="Enter some text..." required />   </label>   <svg viewBox="0 0 60 60">     <path       d="M48.014,42.889l-9.553-4.776C37.56,37.662,37,36.756,37,35.748v-3.381c0.229-0.28,0.47-0.599,0.719-0.951 c1.239-1.75,2.232-3.698,2.954-5.799C42.084,24.97,43,23.575,43,22v-4c0-0.963-0.36-1.896-1-2.625v-5.319 c0.056-0.55,0.276-3.824-2.092-6.525C37.854,1.188,34.521,0,30,0s-7.854,1.188-9.908,3.53C17.724,6.231,17.944,9.506,18,10.056 v5.319c-0.64,0.729-1,1.662-1,2.625v4c0,1.217,0.553,2.352,1.497,3.109c0.916,3.627,2.833,6.36,3.503,7.237v3.309 c0,0.968-0.528,1.856-1.377,2.32l-8.921,4.866C8.801,44.424,7,47.458,7,50.762V54c0,4.746,15.045,6,23,6s23-1.254,23-6v-3.043 C53,47.519,51.089,44.427,48.014,42.889z"       fill="currentColor"     />   </svg> </div>

Обратите внимание на значение атрибута fill элемента path.

И такими стилями:

.form-field {   color: darkslategray;   position: relative;   width: max-content; }  .form-field label {   align-items: center;   display: flex; }  .form-field input {   border: 2px solid darkslategray;   margin-left: 0.5rem;   outline: none;   padding: 0.5rem;   /* отступ для иконки - 20px + 5px + 5px */   padding-right: 30px; }  .form-field svg {   height: 20px;   position: absolute;   right: 5px;   top: 50%;   transform: translateY(-50%);   width: 20px; }

Допустим, что при наведении и фокусировке мы хотим менять цвет подписи, границы поля и заливки иконки.

С инпутом все просто:

.form-field input:hover {   border-color: deepskyblue; }  .form-field input:focus {   border-color: mediumseagreen; }

С наведением тоже:

.form-field:hover {   color: deepskyblue; }

Но следующее работать не будет, поскольку элемент div не является фокусируемым (focusable):

.form-field:focus {   color: mediumseagreen; }

Здесь на помощь приходит :focus-within:

.form-field:focus-within {   color: mediumseagreen; }

Scroll Snap

Scroll Snap позволяет реализовывать прокручиваемые слайдеры (scrollable sliders). Основными свойствами данной модели являются:

  • scroll-snap-type — определяет строгость привязки контейнера к контрольным точкам;
  • scroll-snap-align — определяет контрольные точки для прокрутки.

Синтаксис (основные значения)

scroll-snap-type: x | y | both [mandatory | proximity]; scroll-snap-align: start | center | end;

Поддержка scroll-snap-type — 94.98%.
Поддержка scroll-snap-align — 94.75%.

Пример

Прокручиваемый слайдер с тремя изображениями котиков:

<div class="slider">   <img     src="https://images.unsplash.com/photo-1529257414772-1960b7bea4eb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80"     alt=""   />   <img     src="https://images.unsplash.com/photo-1598188306155-25e400eb5078?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1548&q=80"     alt=""   />   <img     src="https://images.unsplash.com/photo-1517331156700-3c241d2b4d83?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1768&q=80"     alt=""   /> </div>

* {   margin: 0; }  body {   align-items: center;   display: flex;   height: 100vh;   justify-content: center;   overflow: hidden; }  .slider {   display: flex;   overflow-x: scroll;   position: relative;   /* ! */   scroll-snap-type: x mandatory;   /* стилизация скроллбара для Firefox */   scrollbar-color: hotpink whitesmoke;   scrollbar-width: thin;   width: 480px; }  .slider > img {   object-fit: cover;   /* ! */   scroll-snap-align: start;   width: 100%; }  /* стилизация скроллбара для Webkit */ .slider::-webkit-scrollbar {   height: 6px; }  .slider::-webkit-scrollbar-track {   background-color: whitesmoke; }  .slider::-webkit-scrollbar-thumb {   background-color: hotpink; }

scroll-behavior

Свойство scroll-behavior, как следует из названия, определяет поведение прокрутки.

Синтаксис (основные значения)

scroll-behavior: auto | smooth;

Поддержка — 91.01%.

Пример

Рассматриваемое свойство позволяет легко реализовать прокрутку к определенной позиции на странице без помощи JavaScript:

<!-- якорь --> <a id="top"></a>  <div class="page-content">   <p>1</p>   <p>2</p>   <p>3</p>   <p>4</p>   <p>5</p> </div>  <a href="#top" class="top-link">   <img src="https://cdn-icons-png.flaticon.com/512/892/892692.png" alt="" /> </a>

* {   margin: 0; }  html, body {   /* ! */   scroll-behavior: smooth; }  .page-content {   display: flex;   flex-direction: column; }  p {   display: grid;   font-size: 4rem;   height: 100vh;   place-content: center; }  .top-link {   bottom: 1rem;   left: 50%;   position: fixed;   transform: translateX(-50%); }  .top-link img {   width: 40px; }

accent-color

Свойство accent-color позволяет менять цвет таких элементов, как <input type="checkbox" />, <input type="radio" />, <input type="range" /> и <progress />.

Синтаксис

accent-color: auto | <color>;

Поддержка — 87.04%.

Пример

<input type="checkbox" checked /> <input type="radio" checked /> <input type="range" min="0" max="10" value="5" /> <progress max="100" value="50"></progress>

body {   /* ! */   accent-color: deepskyblue;   align-items: start;   display: flex;   flex-direction: column;   gap: 1rem; }

Теперь в простых случаях можно обходиться без создания кастомных чекбоксов, радио-кнопок и т.д.

Будущие возможности

Теперь поговорим о наиболее интересных возможностях, которые ждут нас в ближайшем будущем.

Вложенность

Вложенность — одна из самых ожидаемых возможностей, давно реализованных в таких CSS-препроцессорах, как Sass и Less.

Синтаксис похож на Sass, за исключением того, что использование символа & в качестве родительского селектора является обязательным.

Пример

<article class="article">   <h2 class="title">Title</h2>   <div class="info">     <p class="author">Author</p>     <time datetime="2022-08-01" class="date">01.08.2022</time>   </div>   <p class="summary">Summary</p> </article>

/* стилизуем информацию об авторе статьи */ .article {     & .info {         /* .article .info .author */         & .author {           /* ... */         }     } } /* в Sass можно опускать `&` */ .article {     .info {         .author {           /* ... */         }     } }

Еще одно отличие от Sass состоит в том, что, судя по всему, в CSS нельзя будет «склеивать» селекторы. Например, если у нас имеется такая разметка:

<article class="article">   <h2 class="article__title">Title</h2> </article>

В Sass стилизовать заголовок можно следующим образом:

.article {   &__title {     /* ... */   } }

Если вас интересует более подробная информация о вложенности, рекомендую взглянуть на эту статью.

:has()

Функция-псевдокласс :has() предназначена для стилизации элементов, совпадающих хотя бы с одним селектором из списка, переданного в качестве аргумента. Прелесть данной функции состоит в том, что она позволяет стилизовать родительские элементы.

Синтаксис

:has(selector1, selector2, ...selectorN) {}

Примеры

/* стилизуем ссылки, которые содержат изображения в качестве непосредственных потомков */ a:has(> img) {}  /* стилизуем `img`, которые находятся в `figure`, которые имеют `figcaption` */ figure:has(figcaption) img {}

color-mix()

Функция color-mix принимает 2 цвета и возвращает результат их смешивания в указанном цветовом пространстве и на указанный процент. Дефолтным цветовым пространством является LCH.

Синтаксис:

color-mix(in <colorspace>?, <color1> <percentage>?, <color2> <percentage>?)

Пример

<div class="box brand"></div> <div class="box darken"></div> <div class="box lighter"></div>

:root {   --brand: deepskyblue;   /* ! */   --darken: color-mix(var(--brand) 25%, #333);   --lighter: color-mix(var(--brand) 25%, #eee); }  .box {   width: 100px;   height: 100px; }  .box.brand {   background-color: var(--brand); }  .box.darken {   background-color: var(--darken); }  .box.lighter {   background-color: var(--lighter); }

scope

Директива @scope предназначена для определения области видимости стилей.

Синтаксис

@scope <selector> {   <stylesheet> }

Пример

Вернемся к примеру из раздела про вложенность:

.article {     & .info {         & .author {           /* ... */         }     } }

Если после этих стилей будет определено что-то вроде:

.section {     & .info {         & .author {           /* ... */         }     } }

То специфичность селекторов .article .info .author и .section .info .author будет одинаковой и стили, определенные в .section, перезапишут стили, определенные в .article. @scope позволяет решить данную проблему за счет инкапсуляции стилей в собственной области видимости:

@scope .article {   /* стили применяются только к элементам, находящимся в элементе с классом `article` */   & .info {       & .author {         /* ... */       }   } }  @scope .section {   /* стили применяются только к элементам, находящимся в элементе с классом `section` */   & .info {       & .author {         /* ... */       }   } }

Справедливости ради следует отметить, что на сегодняшний день существует еще одно средство для решения проблемы правильного порядка определения стилей — директива @layer, позволяющая определять так называемые каскадные слои стилей (cascade layers). Однако, на мой взгляд, эта директива больше подходит для использования в CSS-фреймворках, чем для локального применения. Например, @layer активно используется в таком фреймворке, как TailwindCSS.

selectmenu

Элемент selectmenu — это своего рода стилизуемый вариант элемента select.

Синтаксис

selectmenu::part(<part>) {}

Пример

<selectmenu class="select-menu">   <option>Option 1</option>   <option>Option 2</option>   <option>Option 3</option> </selectmenu>

.select-menu::part(button) {   background-color: #f00;   border-radius: 5px;   color: white;   padding: 5px; }  .select-menu::part(listbox) {   border-radius: 5px;   border: 1px solid red;   margin-top: 5px;   padding: 10px; }

anchor()

Последняя возможность, о которой я хочу рассказать, это функция anchor().

Данная функция является альтернативой позиционированию элементов с помощью position: relative и position: absolute. Она позволяет привязывать одни элементы к другим независимо от их места в иерархии DOM.

anchor() также будет предоставлять возможность определять границы позиционирования и другие интересные фичи. Перейдите по ссылке, если хотите получить более подробную информацию о рассматриваемом интерфейсе (да, это целый интерфейс, а не одна функция).

Подробнее о новых и ожидаемых возможностях CSS можно почитать в этой замечательной статье.

Пожалуй, это все, чем я хотел поделиться с вами в этой заметке. Надеюсь, вы узнали что-то новое и не зря потратили время.

Благодарю за внимание и happy coding!


ссылка на оригинал статьи https://habr.com/ru/company/timeweb/blog/681422/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *