1

I have code like this:

<theme-component theme="dark">
  <my-component></my-component>
</theme-component>

Is it possible for my-component to know it is within theme-component with a theme of dark? Or do I also need to pass theme=dark manually into my-component?

my-component has styles defined like this:

  static darkStyle = css`
    :host {
      --my-bkg: #535353;
      --my-items: #474747;
      --my-gutter: #4a4a4a;
      --my-border: #474747;
      --my-item-border: #535353; /* on dark themes only this is the same color as the background instead of the footer border */
      --my-divider: #636363;
    }
  `;

  static lightStyle = css`
    :host {
      --my-bkg: #b8b8b8;
      --my-items: #c7c7c7;
      --my-gutter: #ababab;
      --my-border: #adadad;
      --my-item-border: #adadad; /* on light themes only this is the same color as the footer border */
      --my-divider: #9e9e9e;
    }
  `;

  static styles = [this.darkStyle, sheet];

I would love to have some kind of ternary to switch between this.darkStyle or this.lightStyle. Or is there some kind of CSS I can write that is like

static styles = [css`
theme-component[theme=dark] :root {
  --my-bkg: #535353;
}
`, sheet];

I see documentation about SuperElement.styles, but it's not clear to me how to use that when I am relying on variables based on an attribute here. I'm not trying to share a style as much as use the attribute to determine some new variables.

Dave Stein
  • 8,653
  • 13
  • 56
  • 104

1 Answers1

0

Edit after clarifications:

A component could technically find out the attribute on a direct parent component by imperatively doing the following:

this.parentElement.getAttribute('theme').

I am not sure how that could be done in CSS. Below this edit I've outlined a common approach to solving the issue of theming using CSS custom-properties which have the default behavior of inheriting through the shadow DOM. Also see video by Lit team: "How to style your Lit elements" for more context.

Original answer:

I think what you're looking for is the :host() CSS pseudo-class function.

Your setup is correct where the theme-component is providing CSS custom properties that inherit to all children. my-component does not need any changes.

The only change from your example that should be needed is:

  static darkStyle = css`
    :host([theme="dark"]) {
      --my-bkg: #535353;
      --my-items: #474747;
      --my-gutter: #4a4a4a;
      --my-border: #474747;
      --my-item-border: #535353;
      --my-divider: #636363;
    }
  `;

This will select and apply the dark theme CSS custom properties when the theme-component has the theme attribute set to value "dark". These custom properties will then inherit into the children.

Runnable sample:

<script type="module">
import {html, css, LitElement} from "https://cdn.jsdelivr.net/gh/lit/dist@2/core/lit-core.min.js";

class ThemeEl extends LitElement {
  static styles = css`
    :host {
      --my-bkg: green;
      display: block;
      border: 2px dotted var(--my-bkg);
    }
    :host([theme="dark"]) {
      --my-bkg: gray;
    }
  `;
    
  render() { return html`<slot></slot>`; }
}

class ChildEl extends LitElement {
  static styles = css`
    :host {
      display: block;
      background-color: var(--my-bkg);
    }
  `;
  
  render() { return html`<p>Child El</p>`; }
}

customElements.define('theme-el', ThemeEl);
customElements.define('child-el', ChildEl);
</script>

<theme-el>
  <p>Default Theme</p>
  <child-el></child-el>
</theme-el>

<theme-el theme="dark">
  <p>Dark Theme</p>
  <child-el></child-el>
</theme-el>

This technique can also be used such that a custom element can change its own host styling. By setting attributes on itself and using :host().

If the child component also needs to know what theme is set from JavaScript, that could also be communicated via a CSS custom property and queried with window.getComputedStyle(this).getPropertyValue(<custom property>).

YouCodeThings
  • 590
  • 3
  • 13
  • host here would refer to ``. I believe my ask is impossible without the parent exposing some method? – Dave Stein Nov 02 '22 at 18:57
  • I'm maybe not clear on what `` needs from the ``. I wrote the answer assuming that `` only needed the inherited CSS custom properties. `` could also know which theme is set from only the custom properties. My answer allows `` to change which custom properties are propagated down to `` (via CSS custom property inheritance) depending on which `theme` attribute is set. I could update the runnable sample with an example of the child knowing which theme is set (from JavaScript) if you want? – YouCodeThings Nov 02 '22 at 19:00
  • Ah whoops - I missed that the styles are on ``. Could they be moved into the `` and then inherit into ``? Otherwise I'm not clear what the purpose of `` is? – YouCodeThings Nov 02 '22 at 19:30
  • It seems I should be using variables that are injected into `:root` when defining new variables within `` and that is the solve. It's bad I was trying to define one vs the other in my post – Dave Stein Nov 02 '22 at 19:50
  • Awesome! Additionally, with custom properties on `:root`, you can also intercept and modify those custom properties using your `` (allowing the possibility of multiple themes on a page scoped to that component and its children). It's also possible to rename custom properties within components as well if there is a need. E.g. (in my-component): `--my-component-color: var(--my-items)` – YouCodeThings Nov 02 '22 at 19:58
  • Updated my answer with a section at the top specifically answering "Can Lit or Web Components know attribute of parent component?" and providing more context on the solution underneath. Thanks for your patience and clarifications. – YouCodeThings Nov 02 '22 at 20:10