Styl Shadow DOM

Stínový DOM může obsahovat obě <style> a <link rel="stylesheet" href="…"> značky. V druhém případě jsou šablony stylů uloženy v mezipaměti HTTP, takže se znovu nestahují pro více komponent, které používají stejnou šablonu.

Obecně platí, že místní styly fungují pouze uvnitř stínového stromu a styly dokumentů fungují mimo něj. Ale existuje několik výjimek.

:hostitel

:host selector umožňuje vybrat stínového hostitele (prvek obsahující stínový strom).

Vyrábíme například <custom-dialog> prvek, který by měl být vycentrován. K tomu potřebujeme nastylovat <custom-dialog> samotný prvek.

Přesně to je :host dělá:

<template id="tmpl">
  <style>
    /* the style will be applied from inside to the custom-dialog element */
    :host {
      position: fixed;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      display: inline-block;
      border: 1px solid red;
      padding: 10px;
    }
  </style>
  <slot></slot>
</template>

<script>
customElements.define('custom-dialog', class extends HTMLElement {
  connectedCallback() {
    this.attachShadow({mode: 'open'}).append(tmpl.content.cloneNode(true));
  }
});
</script>

<custom-dialog>
  Hello!
</custom-dialog>

Kaskádové

Stínový hostitel (<custom-dialog> sám) se nachází v lehkém DOM, takže je ovlivněn pravidly CSS dokumentu.

Pokud existuje vlastnost ve stylu :host lokálně a v dokumentu, pak má přednost styl dokumentu.

Pokud jsme například v dokumentu měli:

<style>
custom-dialog {
  padding: 0;
}
</style>

…Poté <custom-dialog> by bylo bez vycpávky.

Je to velmi pohodlné, protože v jeho :host můžeme nastavit „výchozí“ styly komponent pravidlo a poté je v dokumentu snadno přepište.

Výjimkou je případ, kdy je místní vlastnost označena !important , pro takové vlastnosti mají přednost místní styly.

:hostitel(selektor)

Stejné jako :host , ale použije se pouze v případě, že stínový hostitel odpovídá selector .

Například bychom chtěli vycentrovat <custom-dialog> pouze pokud má centered atribut:

<template id="tmpl">
  <style>
    :host([centered]) {
      position: fixed;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      border-color: blue;
    }

    :host {
      display: inline-block;
      border: 1px solid red;
      padding: 10px;
    }
  </style>
  <slot></slot>
</template>

<script>
customElements.define('custom-dialog', class extends HTMLElement {
  connectedCallback() {
    this.attachShadow({mode: 'open'}).append(tmpl.content.cloneNode(true));
  }
});
</script>


<custom-dialog centered>
  Centered!
</custom-dialog>

<custom-dialog>
  Not centered.
</custom-dialog>

Nyní jsou další styly centrování aplikovány pouze na první dialog:<custom-dialog centered> .

Abychom to shrnuli, můžeme použít :host -rodina selektorů pro stylování hlavního prvku komponenty. Tyto styly (pokud není !important ) lze přepsat dokumentem.

Upravování stylů obsahu s bloky

Nyní se podívejme na situaci se sloty.

Drážkované prvky pocházejí z lehkého DOM, takže používají styly dokumentu. Místní styly neovlivňují obsah ve slotech.

V níže uvedeném příkladu s drážkou <span> je tučné, podle stylu dokumentu, ale nebere background z místního stylu:

<style>
  span { font-weight: bold }
</style>

<user-card>
  <div slot="username"><span>John Smith</span></div>
</user-card>

<script>
customElements.define('user-card', class extends HTMLElement {
  connectedCallback() {
    this.attachShadow({mode: 'open'});
    this.shadowRoot.innerHTML = `
      <style>
      span { background: red; }
      </style>
      Name: <slot name="username"></slot>
    `;
  }
});
</script>

Výsledek je tučný, ale ne červený.

Pokud bychom chtěli v naší komponentě upravit styl štěrbinových prvků, máme dvě možnosti.

Nejprve můžeme upravit styl <slot> a spoléhají na dědičnost CSS:

<user-card>
  <div slot="username"><span>John Smith</span></div>
</user-card>

<script>
customElements.define('user-card', class extends HTMLElement {
  connectedCallback() {
    this.attachShadow({mode: 'open'});
    this.shadowRoot.innerHTML = `
      <style>
      slot[name="username"] { font-weight: bold; }
      </style>
      Name: <slot name="username"></slot>
    `;
  }
});
</script>

Zde <p>John Smith</p> se zobrazí tučně, protože dědičnost CSS je účinná mezi <slot> a její obsah. Ale v CSS samotném nejsou všechny vlastnosti zděděny.

Další možností je použít ::slotted(selector) pseudotřída. Přiřazuje prvky na základě dvou podmínek:

  1. To je štěrbinový prvek, který pochází z lehkého DOM. Název slotu nehraje roli. Prostě jakýkoli štěrbinový prvek, ale pouze prvek samotný, ne jeho potomci.
  2. Prvek odpovídá selector .

V našem příkladu ::slotted(div) vybere přesně <div slot="username"> , ale ne jeho potomci:

<user-card>
  <div slot="username">
    <div>John Smith</div>
  </div>
</user-card>

<script>
customElements.define('user-card', class extends HTMLElement {
  connectedCallback() {
    this.attachShadow({mode: 'open'});
    this.shadowRoot.innerHTML = `
      <style>
      ::slotted(div) { border: 1px solid red; }
      </style>
      Name: <slot name="username"></slot>
    `;
  }
});
</script>

Poznámka:::slotted volič nemůže klesnout dále do slotu. Tyto selektory jsou neplatné:

::slotted(div span) {
  /* our slotted <div> does not match this */
}

::slotted(div) p {
  /* can't go inside light DOM */
}

Také ::slotted lze použít pouze v CSS. Nemůžeme jej použít v querySelector .

Háčky CSS s vlastními vlastnostmi

Jak stylizujeme vnitřní prvky komponenty z hlavního dokumentu?

Selektory jako :host použít pravidla na <custom-dialog> prvek nebo <user-card> , ale jak v nich stylizovat stínové prvky DOM?

Neexistuje žádný selektor, který by mohl přímo ovlivnit stínové styly DOM z dokumentu. Ale stejně jako vystavujeme metody pro interakci s naší komponentou, můžeme vystavit proměnné CSS (vlastní vlastnosti CSS), abychom ji stylizovali.

Vlastní vlastnosti CSS existují na všech úrovních, ve světle i ve stínu.

Například ve stínovém DOM můžeme použít --user-card-field-color Proměnná CSS pro pole stylu a vnější dokument může nastavit svou hodnotu:

<style>
  .field {
    color: var(--user-card-field-color, black);
    /* if --user-card-field-color is not defined, use black color */
  }
</style>
<div class="field">Name: <slot name="username"></slot></div>
<div class="field">Birthday: <slot name="birthday"></slot></div>

Poté můžeme tuto vlastnost deklarovat ve vnějším dokumentu pro <user-card> :

user-card {
  --user-card-field-color: green;
}

Vlastní vlastnosti CSS pronikají skrz stínový DOM, jsou viditelné všude, takže vnitřní .field pravidlo jej využije.

Zde je úplný příklad:

<style>
  user-card {
    --user-card-field-color: green;
  }
</style>

<template id="tmpl">
  <style>
    .field {
      color: var(--user-card-field-color, black);
    }
  </style>
  <div class="field">Name: <slot name="username"></slot></div>
  <div class="field">Birthday: <slot name="birthday"></slot></div>
</template>

<script>
customElements.define('user-card', class extends HTMLElement {
  connectedCallback() {
    this.attachShadow({mode: 'open'});
    this.shadowRoot.append(document.getElementById('tmpl').content.cloneNode(true));
  }
});
</script>

<user-card>
  <span slot="username">John Smith</span>
  <span slot="birthday">01.01.2001</span>
</user-card>

Shrnutí

Shadow DOM může obsahovat styly, jako je <style> nebo <link rel="stylesheet"> .

Místní styly mohou ovlivnit:

  • strom stínů,
  • stínový hostitel s :host a :host() pseudotřídy,
  • slotové prvky (pocházející z lehkého DOM), ::slotted(selector) umožňuje vybrat štěrbinové prvky samotné, ale ne jejich potomky.

Styly dokumentu mohou ovlivnit:

  • stínový hostitel (ve vnějším dokumentu)
  • prvky v štěrbinách a jejich obsah (stejně jako ve vnějším dokumentu)

Při konfliktu vlastností CSS mají obvykle přednost styly dokumentu, pokud vlastnost není označena jako !important . Pak mají přednost místní styly.

Vlastní vlastnosti CSS pronikají skrz stínový DOM. Používají se jako „háky“ pro stylování komponenty:

  1. Komponenta používá vlastní vlastnost CSS ke stylování klíčových prvků, jako je var(--component-name-title, <default value>) .
  2. Autor komponenty publikuje tyto vlastnosti pro vývojáře, jsou stejně důležité jako jiné metody veřejných komponent.
  3. Když chce vývojář upravit styl titulku, přiřadí --component-name-title Vlastnost CSS pro stínového hostitele nebo vyššího.
  4. Zisk!