DEV Community

Cover image for Dynamic styling of Shadow Host - craftkit code pattern
CraftkitJS
CraftkitJS

Posted on • Edited on

Dynamic styling of Shadow Host - craftkit code pattern

Usually, it is enough to define style only for the .root and its children. But sometimes, you want to edit style for host element. Here is patterns for this.

Define Shadow Host style

To define Shadow Host style, just write CSS in :host as usual.

class Example extends Craft.UI.View {
    style(componentId){
        return `
            :host {
                padding-top: env(safe-area-inset-top);
                padding-left: env(safe-area-inset-left);
                padding-right: env(safe-area-inset-right);
            }
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

Direct Host style modification

You can directly update Host element styling via this.shadow.host.style[*].

class Example extends Craft.UI.View {
    updateShadowHostWidth(){
        this.shadow.host.style.width = '200px';
    }
    style(componentId){
        return `
            :host {
                width: 100px;
            }
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

Swap Shadow Host CSS class

If you would like to change style of Host element on the fly, your target class should be defined in the parent component to be able to affect the shadow element.

class Example extends Craft.UI.View {
    viewDidLoad(callback){
        this.appendView(new ExampleCore());
        if(callback){ callback(); }
    }
    style(componentId){
        return `
            .light { color:#333; background-color: #fff; }
            .dark { color:#fff; background-color: #333; }
        `;
    }
}

class ExampleWrapped extends Craft.UI.View {
    constructor(options){
        super(options);
        this.data = { mode:0 };
    }
    toggleMode(){
        if( this.data.mode++ % 2 ){
            this.shadow.host.classList.add('light')
            this.shadow.host.classList.remove('dark')
        }else{
            this.shadow.host.classList.add('dark');
            this.shadow.host.classList.remove('light')
        }
    }
    style(componentId){
        return `
            :host { color:#333; background-color: #fff; }
            .root { 
                width:100px; marign-left:auto; marign-right:auto;
            }
        `;
    }
    template(componentId){
        return `
            <div id="root" class="root" 
            onclick="${componentId}.toggleMode()">
                Hello!
            </div>
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

Discouraged pattern

You can write something like the following code, but the code goes back and forth. So, this pattern should be avoided.

class ExampleWrapper extends Craft.UI.View {
    constructor(options){
        super(options);
        this.data = { mode:1 };
        this.views = { example:null };
    }
    viewDidLoad(callback){
        this.views.example = new ExampleWrapped({delegate:this});
        this.appendView(this.views.example);
        if(callback){ callback(); }
    }
    toggleMode(){
        if( this.data.mode++ % 2 ){
            this.views.example.darkMode();
        }else{
            this.views.example.lightMode();
        }
    }
    style(componentId){
        return `
            .light { color:#333; background-color: #fff; }
            .dark { color:#fff; background-color: #333; }
        `;
    }
}

class ExampleWrapped extends Craft.UI.View {
    constructor(options){
        super(options);
        this.delegate = options.delegate;
    }
    lightMode(){
        this.shadow.host.classList.add('light')
        this.shadow.host.classList.remove('dark')
    }
    darkMode(){
        this.shadow.host.classList.add('dark');
        this.shadow.host.classList.remove('light')
    }
    style(componentId){
        return `
            :host { color:#333; background-color: #fff; }
            .root { 
                width:100px; marign-left:auto; marign-right:auto;
            }
        `;
    }
    template(componentId){
        return `
            <div id="root" class="root" 
            onclick="${componentId}.delegate.toggleMode()">
                Hello!
            </div>
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

NOTE

Above examples are runnable on playground.

var view = new Example();
view.loadView();
Craft.Core.Context.getRootViewController().appendSubView(view);
Enter fullscreen mode Exit fullscreen mode

🛺 Try CraftKit Playground:
https://github.com/craftkit/craftkit-playground

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up