Pull to refresh
1991.96
Timeweb Cloud
То самое облако

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

Reading time10 min
Views13K


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


В данной заметке я расскажу вам о некоторых полезных возможностях, предоставляемых современным 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;
}

Сейчас это можно делать с помощью псевдокласса ::placeholder (спасибо dynamicult):


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

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


: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!




Tags:
Hubs:
Total votes 21: ↑21 and ↓0+21
Comments11

Articles

Information

Website
timeweb.cloud
Registered
Founded
Employees
201–500 employees
Location
Россия
Representative
Timeweb Cloud