DEV Community

Cover image for Style sharing and object oriented CSS - craftkit code pattern
CraftkitJS
CraftkitJS

Posted on • Edited on

Style sharing and object oriented CSS - craftkit code pattern

craftkit encapsulates its CSS by ShadowDOM. So you can use simple CSS class name for your component.

And the CSS is defined by style() instance method. So you can append definition to its super class.

This spec allows you to implement object oriented styling. Now the time to say goodbye to CSS name convention.

This post describes how to inherit style. In other words, how to share style sheet across Craft.UI.View in your application.

Style Method overriding

class Hello extends Craft.UI.View {
    style(componentId){
        return `
            .root {
                margin: 50px;
            }
            .text {
                color: black;
            }
        `;
    }
    template(componentId){
        return `
            <div id="root" class="root">
                <h1>Hello</h1>
                <p class="text">World!</p>
            </div>
        `;
    }
}

class HelloRed extends Hello {
    style(componentId){
        return super.style(componentId) + `
            .text {
                color: red;
            }
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

In HelloRed instance, margin:50px is cascaded, and color:red is applied as usual CSS rule.

class Aloha extends Craft.UI.View {
    style(componentId){
        return `
            .root {
                margin: 70px;
            }
            .text {
                color: blue;
            }
        `;
    }
    template(componentId){
        return `
            <div id="root" class="root">
                <h1>Aloha</h1>
                <p class="text">World!</p>
            </div>
        `;
    }
}

class AlohaRed extends Example {
    style(componentId){
        return super.style(componentId) + `
            .text {
                color: red;
            }
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

Hello and Aloha have same CSS class definition, but its instance can exist simultaneously without conflict.

Pseudo multiple inheritance

Craft.UI.View is also a plain JavaScript class. So you can't define multiple inheritance for you view class.

But style method is defined dynamically, and resolved by same principle of CSS. So you can implement pseudo multiple style inheritance something like this:

// file: HeaderStyling.js

export class HeaderStyling {
    static css(){
        return `
            h1 { color:red; }
        `;
    }
}

//file: ParagraphStyling.js

export class ParagraphStyling {
    static css(){
        return `
            p { line-height: 2em; }
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

Import and load them in your style method.

import { HeaderStyling } from 'HeaderStyling.js';
import { ParagraphStyling } from 'ParagraphStyling.js';

class Hello extends Craft.UI.View {
    style(componentId){
        return HeaderStyling.css() + ParagraphStyling.css() + `
            p { color: blue; }
        `;
    }
    template(componentId){
        return `
            <div id="root" class="root">
                <h1>Hello</h1>
                <p>World!</p>
            </div>
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

Dynamic style injection

Above example imports style classes statically. Of course, it can be injected dynamically something like this:

class HelloController extends Craft.UI.View {
    viewDidLoad(callback){
        this.appendSubView(new Hello({
            styles: [HeaderStyling,ParagraphStyling]
        }))
    }
}

class Hello extends Craft.UI.View {
    constructor(options){
        super(options);
        this.injectedStyles = options.styles;
    }
    style(componentId){
        return this.injectedStyles.map( css => css.css() ).join('') + `
            p { color: blue; }
        `;
    }
    template(componentId){
        return `
            <div id="root" class="root">
                <h1>Hello</h1>
                <p class="text">World!</p>
            </div>
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

Load external CSS

adoptedStyleSheets is no yet supported by Safari. To load external CSS file from remote, at first you have to append it to header, and then append it to this.shadow.

var link = document.createElement('link');
link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css';
link.rel = 'stylesheet';
document.head.appendChild(link)
Enter fullscreen mode Exit fullscreen mode

This is for console test. (You may preload in your index.html)

Then, define view that is appending CSS to its this.shadow in constructor.

class Hello extends Craft.UI.View {
    constructor(options){
        super();

        const link = document.createElement('link');
        link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css';
        link.rel = 'stylesheet';
        this.shadow.appendChild(link);
    }
    template(componentId){
        return `
            <div id="root" class="root">
                <h1>Hello</h1>
                <i class="fas fa-globe"></i>
            </div>
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

NOTE

Above examples are runnable on playground.

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

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

Top comments (0)