Search code examples
web-componentstenciljs

Stenciljs: dynamically load styleUrl


I'm building a web component with stenciljs and i want to be able to load the css theme file based on the proprety called theme.

@Component({
  tag: 'pm-header',
  styleUrl: 'pm-header.scss',
  shadow: true
})

export class PmHeader {

  @Prop() theme: string = 'default';

  ...

  render() {
    return (<nav class={this.theme}></nav>)
  }
}

Solution

  • I know this is late, but this should hopefully help others as it is not documented and I just spent a lot of time figuring it out. The following code allows your custom components to use a "mode" attribute to determine which style is loaded.

    Step 1: Define multiple styleUrls (aka. "modes") in your component definition. I am using "dark" and "default" - but you can define as many as you want. For example, ionic uses "ios" and "md".

    @Component({
        tag: 'my-component',
        styleUrls: {
            default: 'my-component.default.pcss',
            dark: 'my-component.dark.pcss',
        },
    })
    export class MyComponent { ... }
    

    Step 2: Create the style files (usually with shared common styles):

    • my-component.common.css

        :host { display: block }
      
    • my-component.default.css

        @import './my-component.common.css';
        :host { color: black }
      
    • my-component.dark.css

        @import './my-component.common.css';
        :host { background: black; color: white }
      

    Step 3: Update your stencil.config.ts to use a globalScript:

    export const config: Config = {
        namespace: 'mycomponent',
        globalScript: './src/globals/global.ts',
        ...
    }
    

    Step 4: Create the global script and define a "setMode" function like this:

    import { setMode } from '@stencil/core';
    
    setMode(elm => {
        // NOTE: you can write whatever you want here - it's up to you. This
        // function must return one of the style "modes" defined in step 1.
        return (elm as any).mode || elm.getAttribute('mode') || 'default';
    });
    

    Step 5: Use your custom component like this:

    <!-- default mode -->
    <my-component />
    <my-component mode="default" />
    
    <!-- dark mode -->
    <my-component mode="dark" />
    

    You can customize the setMode function for determining the mode - for example you could look for a property on the window, or check for a className on the element. It's up to you. The above is just a simple example which allows you to use a "mode" attribute on the element... falling back to "default" if nothing is specified.