Перейти к содержанию

Стили

Шаблон вашего компонента отображается на его теневой корень. Стили, которые вы добавляете в компонент, автоматически скопируются в корень тени и влияют только на элементы в корне тени компонента.

Shadow DOM обеспечивает надежную инкапсуляцию стилей. Если бы Lit не использовал Shadow DOM, вам пришлось бы быть очень осторожным, чтобы случайно не стилизовать элементы вне вашего компонента, либо предков, либо дочерние элементы компонента. Это может привести к написанию длинных и громоздких имен классов. Используя Shadow DOM, Lit гарантирует, что любой написанный вами селектор будет применяться только к элементам в корневой тени вашего компонента Lit.

Добавление стилей в компонент

Вы определяете стили в статическом поле класса styles, используя тегированную шаблонную литеральную функцию css. Такое определение стилей обеспечивает наиболее оптимальную производительность:

Стили, которые вы добавляете в свой компонент, скопируются с помощью shadow DOM. Для краткого обзора смотрите Shadow DOM.

Значением статического поля класса styles может быть:

  • Один тегированный шаблонный литерал.

    1
    static styles = css`...`;
    
  • Массив помеченных литералов шаблона.

    1
    static styles = [ css`...`, css`...`];
    

Статическое поле класса styles — это почти всегда лучший способ добавить стили в компонент, но есть некоторые случаи, с которыми нельзя справиться таким образом — например, настройка стилей для каждого экземпляра. Об альтернативных способах добавления стилей см. в разделе Определение масштабируемых стилей в шаблоне.

Использование выражений в статических стилях

Статические стили применяются ко всем экземплярам компонента. Любые выражения в CSS оцениваются один раз, а затем повторно используются для всех экземпляров.

Для настройки стилей на основе дерева или для каждого экземпляра используйте пользовательские свойства CSS, чтобы позволить элементам быть тематическими.

Чтобы предотвратить оценку потенциально вредоносного кода компонентами Lit, тег css позволяет использовать только вложенные выражения, которые сами являются строками или числами с тегом css.

1
2
3
4
5
const mainColor = css`red`;
// ...
static styles = css`
  div { color: ${mainColor} }
`;

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

Если вам необходимо использовать выражение в литерале css, который сам не является литералом css, и вы уверены, что выражение получено из полностью доверенного источника, такого как константа, определенная в вашем собственном коде, то вы можете обернуть выражение функцией unsafeCSS:

1
2
3
4
5
const mainColor = 'red';
// ...
static styles = css`
  div { color: ${unsafeCSS(mainColor)} }
`;

Используйте тег unsafeCSS только с доверенным вводом

Внедрение несанированного CSS представляет собой риск для безопасности. Например, вредоносный CSS может "позвонить домой", добавив URL-адрес изображения, который указывает на сторонний сервер.

Наследование стилей от суперкласса

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

Вы также можете использовать super.styles для ссылки на свойство styles суперкласса в JavaScript. Если вы используете TypeScript, мы рекомендуем избегать super.styles, поскольку компилятор не всегда корректно преобразует его. Явная ссылка на суперкласс, как показано в примере, позволяет избежать этой проблемы.

При написании компонентов, предназначенных для подклассификации на TypeScript, поле static styles должно быть явно типизировано как CSSResultGroup, чтобы пользователи могли гибко переопределять styles с помощью массива:

1
2
3
// Prevent typescript from narrowing the type of `styles` to `CSSResult`
// so that subclassers can assign e.g. `[SuperElement.styles, css`...`]`;
static styles: CSSResultGroup = css`...`;

Совместное использование стилей

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

1
2
3
4
5
6
7
8
9
export const buttonStyles = css`
    .blue-button {
        color: white;
        background-color: blue;
    }
    .blue-button:disabled {
        background-color: grey;
    }
`;

Затем ваш элемент может импортировать стили и добавить их в свое статическое поле класса styles:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import { buttonStyles } from './button-styles.js';

class MyElement extends LitElement {
    static styles = [
        buttonStyles,
        css`
            :host {
                display: block;
                border: 1px solid black;
            }
        `,
    ];
}

Использование эскейпов юникода в стилях

Юникодная последовательность CSS — это обратный слеш, за которым следуют четыре или шесть шестнадцатеричных цифр: например, \2022 для символа пули. Это похоже на формат устаревших экранирующих последовательностей JavaScript octal, поэтому использование таких последовательностей в литерале шаблона с тегом css приводит к ошибке.

Есть два способа добавить в стили юникод:

  • Добавить второй обратный слеш (например, \\2022).
  • Использовать escape-последовательность JavaScript, начинающуюся с \u (например, \u2022).
1
2
3
4
static styles = css`
  div::before {
    content: '\u2022';
  }

Обзор стилей теневого DOM

В этом разделе приводится краткий обзор теневых стилей DOM.

Стили, которые вы добавляете к компоненту, могут влиять на:

Стилизация теневого дерева

Lit-шаблоны по умолчанию выводятся в теневое дерево. Стили, применяемые к теневому дереву элемента, не влияют на основной документ и другие теневые деревья. Аналогично, за исключением наследуемых CSS-свойств, стили уровня документа не влияют на содержимое теневого дерева.

Когда вы используете стандартные селекторы CSS, они соответствуют только элементам в теневом дереве вашего компонента. Это означает, что вы часто можете использовать очень простые селекторы, поскольку вам не нужно беспокоиться о том, что они случайно стилизуют другие части страницы; например: input, * или #my-element.

Стилизация самого компонента

Вы можете стилизовать сам компонент с помощью специальных селекторов :host. (Элемент, который является владельцем или "хозяином" теневого дерева, называется хост-элементом).

Чтобы создать стили по умолчанию для элемента host, используйте CSS-псевдокласс :host и функцию CSS-псевдокласса :host().

  • :host выбирает элемент-хост.
  • :host(selector) выбирает принимающий элемент, но только если принимающий элемент соответствует selector.

Обратите внимание, что на элемент host могут влиять стили, находящиеся вне дерева теней, поэтому стили, заданные в правилах :host и :host(), следует рассматривать как стили по умолчанию, которые могут быть переопределены пользователем. Например:

1
2
3
my-element {
    display: inline-block;
}

Стилизация дочерних компонентов

Ваш компонент может принимать дочерние элементы (например, элемент <ul> может иметь дочерние элементы <li>). Чтобы отобразить дочерние элементы, ваш шаблон должен включать один или несколько элементов <slot>, как описано в Render children with the slot element.

Элемент <slot> выступает в качестве заполнителя в теневом дереве, где отображаются дочерние элементы основного элемента.

Используйте CSS-псевдоэлемент ::slotted() для выбора дочерних элементов, которые включены в ваш шаблон через <slot>.

  • ::slotted(*) соответствует всем элементам с прорезью.
  • ::slotted(p) соответствует абзацам с прорезью.
  • p ::slotted(*) соответствует элементам со слотами, где <slot> является потомком элемента абзаца.

Обратите внимание, что только прямые дочерние элементы слота могут быть стилизованы с помощью ::slotted().

1
2
3
4
5
6
7
<my-element>
    <div>Stylable with ::slotted()</div>
</my-element>

<my-element>
    <div><p>Not stylable with ::slotted()</p></div>
</my-element>

Кроме того, дочерние элементы могут быть стилизованы вне дерева теней, поэтому следует рассматривать стили ::slotted() как стили по умолчанию, которые могут быть переопределены.

1
2
3
my-element > div {
    /* Outside style targetting a slotted child can override ::slotted() styles */
}

Ограничения в полифилле ShadyCSS вокруг содержимого слотов

Смотрите Ограничения ShadyCSS для получения подробной информации о том, как использовать синтаксис ::slotted() в удобном для полифилла виде.

Определение масштабируемых стилей в шаблоне

Мы рекомендуем использовать статическое поле класса styles для оптимальной производительности. Однако иногда вам может понадобиться определить стили в шаблоне Lit. Существует два способа добавления стилей в шаблон:

Каждый из этих методов имеет свой набор преимуществ и недостатков.

В элементе стиля

Обычно стили помещаются в статическое поле класса styles; однако статические стили элемента оцениваются один раз для каждого класса. Иногда может потребоваться настройка стилей для каждого элемента. Для этого мы рекомендуем использовать свойства CSS для создания тематических элементов. Также вы можете включить элементы <style> в шаблон Lit. Они обновляются для каждого экземпляра.

1
2
3
4
5
6
7
8
render() {
  return html`
    <style>
      /* updated per instance */
    </style>
    <div>template content</div>
  `;
}

Ограничения полифилла ShadyCSS в отношении стилизации по экземплярам

Стилизация по экземплярам не поддерживается полифиллом ShadyCSS. Подробности см. в разделе Ограничения ShadyCSS.

Выражения и элементы стиля

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
render() {
  return html`
    <style>
      :host {
        /* Warning: this approach has limitations & performance issues! */
        color: ${myColor}
      }
    </style>
    <div>template content</div>
  `;
}

Ограничения в полифилле ShadyCSS для выражений

Выражения в элементах <style> не будут обновляться для каждого экземпляра в ShadyCSS, что связано с ограничениями полифилла ShadyCSS. Кроме того, узлы <style> не могут быть переданы в качестве значений выражений при использовании полифилла ShadyCSS. Дополнительную информацию см. в разделе Ограничения ShadyCSS.

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

Чтобы снизить эти затраты, отделите стили, требующие оценки для каждого экземпляра, от тех, которые этого не делают.

1
2
3
4
5
6
static styles = css`/* ... */`;

render() {
    const redStyle = html`<style> :host { color: red; } </style>`;
    return html`${this.red ? redStyle : ''}`
}

Импорт внешней таблицы стилей (не рекомендуется)

Хотя вы можете включить внешнюю таблицу стилей в свой шаблон с помощью <link>, мы не рекомендуем использовать этот подход. Вместо этого стили следует помещать в статическое поле класса styles.

Предупреждения о внешних таблицах стилей

  • Полифилл ShadyCSS не поддерживает внешние таблицы стилей.
  • Внешние стили могут вызвать вспышку нестилизованного содержимого (FOUC) во время их загрузки.
  • URL в атрибуте href является относительным к главному документу. Это нормально, если вы создаете приложение и URL-адреса ваших активов хорошо известны, но избегайте использования внешних таблиц стилей при создании многократно используемого элемента.

Динамические классы и стили

Один из способов сделать стили динамическими — добавить выражения к атрибутам class или style в вашем шаблоне.

Lit предлагает две директивы, classMap и styleMap, для удобного применения классов и стилей в HTML-шаблонах.

Более подробную информацию об этих и других директивах можно найти в документации по встроенным директивам.

Чтобы использовать styleMap и/или classMap:

  1. Импортируйте classMap и/или styleMap:

    1
    2
    import { classMap } from 'lit/directives/class-map.js';
    import { styleMap } from 'lit/directives/style-map.js';
    
  2. Используйте classMap и/или styleMap в шаблоне элемента:

Дополнительную информацию см. в classMap и styleMap.

Темизация

Используя CSS-наследование и CSS-переменные и пользовательские свойства вместе, можно легко создавать тематические элементы. Применяя селекторы css для настройки пользовательских свойств CSS, можно легко применять тематизацию на основе деревьев и отдельных экземпляров. Вот пример:

Наследование CSS

Наследование CSS позволяет родительским и основным элементам передавать определенные CSS-свойства своим потомкам.

Не все свойства CSS наследуются. К наследуемым свойствам CSS относятся:

  • color
  • font-family и другие свойства font-*.
  • Все пользовательские свойства CSS (--*)

Дополнительную информацию см. в CSS Inheritance on MDN.

Вы можете использовать наследование CSS для установки стилей элемента-предка, которые наследуются его потомками:

1
2
3
4
5
6
<style>
    html {
        color: green;
    }
</style>
<my-element> #shadow-root Will be green </my-element>

Пользовательские свойства CSS

Наследует все пользовательские свойства CSS (--custom-property-name). Вы можете использовать это, чтобы сделать стили вашего компонента настраиваемыми извне.

В следующем компоненте цвет фона задается переменной CSS. CSS-переменная использует значение --my-background, если оно было задано селектором, соответствующим предку в дереве DOM, а в противном случае по умолчанию принимает значение yellow:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class MyElement extends LitElement {
    static styles = css`
        :host {
            background-color: var(--my-background, yellow);
        }
    `;
    render() {
        return html`<p>Hello world</p>`;
    }
}

Пользователи этого компонента могут установить значение --my-background, используя тег my-element в качестве CSS-селектора:

1
2
3
4
5
6
<style>
    my-element {
        --my-background: rgb(67, 156, 144);
    }
</style>
<my-element></my-element>

--my-background настраивается для каждого экземпляра my-element:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<style>
    my-element {
        --my-background: rgb(67, 156, 144);
    }
    my-element.stuff {
        --my-background: #111111;
    }
</style>
<my-element></my-element>
<my-element class="stuff"></my-element>

Дополнительную информацию см. в CSS Custom Properties on MDN.

Комментарии