Эта статья — перевод оригинальной статьи "An introduction to @scope in CSS".

Также я веду телеграм канал “Frontend по-флотски”, где рассказываю про интересные вещи из мира разработки интерфейсов.

Вступление

В январе 2019 года я поднял вопрос в W3C CSS GitHub под названием Please bring back scoped styles. Когда-то существовал атрибут HTML scoped, но он был устаревшим. В CSS он был заменен на @scope. Поддержка должна появиться в Chrome 117. Safari так же занимает положительную позицию по спецификации.

У @scope есть два преимущества: стилизация на основе близости и установка нижней границы для селектора.

Стилизация на основе близости

Вместо того чтобы полагаться на порядок и специфику источника, теперь у нас есть возможность переопределять стили на основе близости. Угадайте, какого цвета будет кнопка в следующих примерах:

@scope (.blue) {
    button {
        background-color: blue;
    }
}
    
@scope (.green) {
    button {
      background-color: green;
    }
}
    
@scope (.red) {
    button {
      background-color: red;
    }
}

Пример 1:

<div class="red">
    <div class="green">
        <div class="blue">
            <button>Click</button>
        </div>
    </div>
</div>

Пример 2:

<div class="blue">
    <div class="green">
        <div class="red">
            <button>Click</button>
        </div>
    </div>
</div> 

Ответ: просто посмотрите на ближайшего предка. В примере 1 кнопка синяя. В примере 2 кнопка красная. (Если вы находитесь в Chrome Canary, вы можете посмотреть пример CodePen).

Давайте рассмотрим реальный пример использования. @scope решает проблему, с которой я столкнулся, работая в британской телефонной сети giffgaff. У нас было то, что мы называли "темами" - не светлые и темные темы, которые менялись в зависимости от предпочтений пользователя, а скорее классы для стилизации различных разделов страницы с определенной цветовой схемой. Чтобы обеспечить достаточный цветовой контраст для легкой читаемости, цвет текста ссылки был темно-синим на светлом фоне и светло-синим на темном. Это избавило нас от необходимости устанавливать класс для каждой отдельной ссылки, что было бы утомительно и чревато непоследовательностью.

.theme-white {
  background-color: white;
  color: black;
  }

.theme-white a {
  color: #00528a;
}

.theme-black {
  background-color: black;
  color: white;
}

.theme-black a {
  color: #35adce;
}

Это работало достаточно хорошо, но была одна проблема: вложенность. CSS не смотрит на ближайшего предка HTML, чтобы узнать, какой стиль применить - он просто ориентируется на исходный порядок в вашем CSS-файле. В зависимости от порядка, в котором вы определили стили, если вы вложите белую секцию в черную или черную секцию в белую, ссылка уже не будет иметь правильный цвет. До появления @scope решения этой проблемы не было.

<div class="theme-white">
  <a href="example.com">This link is the correct color</a>
</div>
    
<div class="theme-black">
  <a href="example.com">This link is the correct color</a>
    
  <div class="theme-white">
    <a href="example.com">This link is the wrong color</a>
  </div>
         
</div>

CodePen

С помощью @scope мы можем решить эту проблему:

.theme-white {
   background-color: white;
   color: black;
 }
    
.theme-gray {
   background-color: #f5f5f5;
   color: black;
 }
    
@scope (.theme-white, .theme-gray) {
  a {
    color: #00528a;
  }
}
    
.theme-black {
  background-color: black;
  color: white;
}
    
@scope (.theme-black) {
  a {
    color: #35adce;
  }
}

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

По желанию мы можем переписать предыдущий CSS, чтобы использовать псевдокласс :scope, который ссылается на корень текущей области видимости. В следующем примере :scope будет выбирать любой элемент, имеющий класс .theme-black.

@scope (.theme-black) {
   :scope {
     background-color: black;
     color: white;
   }         
   a {
     color: #35adce;
   }
}

:scope не является новым псевдоклассом. Он используется в браузерах уже много лет, но раньше был довольно бессмысленным при использовании в CSS, потому что вне блока @scope он всегда означал то же самое, что и :root (который выбирает корневой элемент документа - <html> элемент ).

Установка нижней границы для селектора

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

Мириам Сюзанн, соавтор спецификации scope, несколько лет назад выступила на подкасте Syntax, чтобы рассказать о @scope: "Компонент табов имеет все эти дыры, куда бы вы ни поместили содержимое вкладки. Вы не хотите, чтобы компонент вкладок стилизовал содержимое, вы просто хотите, чтобы он стилизовал вкладки... У нас есть селектор потомков, где вы можете указать что угодно внутри вкладок, но это не то, что нам нужно. Мы хотим, чтобы внутри вкладок было все, что угодно, пока вы не доберетесь до содержимого вкладки. Мы хотим иметь возможность установить нижнюю границу для этого селектора".

Давайте посмотрим на синтаксис:

@scope (.component) to (.content) {
  p {
    color: red;
  }
}

Второй селектор устанавливает нижнюю границу - т.е. остановка стилизации с этой точки.

<div class="component">
    
  <p>In scope.</p>
  
  <div class="content">
    <p>Out of scope.</p>
  </div>
  
</div>

Если у вас есть параграф внутри .content, он не будет выбран (если у вас есть браузер, поддерживающий scope, вы можете посмотреть пример CodePen).

В @scope может быть столько "дыр", сколько вы захотите:

@scope (.component) to (.content, .slot, .child-component) {
  p {
    color: red;
  }
}

Комментарии (0)