@Themeable

Class Mandatory for theming

Putting @Themeable on your component's class makes it overridable by the theming system. It takes several parameters that allow you to control what is exposed to the outside world.

Options

Option Description
Name of the prop to use to define the variants to use when using the component. If not defined, the default name is "variant".
Object that defines what CSS variables and selectors to expose. Refer to the following section for more detail.

Override styles

Styles are the main elements you can override with a theme. There are three ways of doing it:

  • Using css variables exposed by the component,
  • Using selectors exposed by the component,
  • By writing custom css code.

Variables

That's the easiest way because the end-user that will use the component doesn't need to know the inner workings of the styles to configure it. But it requires some work to the creator of the component.

@Themeable({
    css: {
        vars: {
            backgroundColor: 'bg-color',
            textColor: 'text-color',
            ...
        }
    }
})
@Component()
class Button { 
    ...
}

TIP

If you expose many variables the object can quickly become troublesome. To make it more readable you could put it in a separate file:

    Now you can override the variables in your theme by doing:

    import { VueThemes } from "@banquette/vue-typescript";
    
    VueThemes.Define('my-button', {
        '*': [
            {
                match: '*',
                cssVars: {
                    backgroundColor: 'blue',
                    textColor: 'white'
                }
            }
        ]
    })
    

    Selectors

    You can also expose a selector directly:

    import { ThemeConfiguration } from './theme-configuration';
    
    @Themeable({
        selectors: {
            root: '&.bt-button',
            content: '.inner > span'
        }
    })
    @Component('my-button')
    class Button {
    ...
    }
    

    The end-user can then set any css property they want via a key/value pair notation:

    import { VueThemes } from "@banquette/vue-typescript";
    
    VueThemes.Define('my-button', {
        '*': [
            {
                match: 'danger',
                cssSelectors: {
                    content: {
                        fontWeight: 'bold',
                        color: 'red'
                    }
    
                    /** 
                     * Will result in the following CSS being generated:
                     * 
                     * .inner > span {
                     *    font-weight: bold;
                     *    color: red;
                     * }
                     */
                }
            }
        ]
    })
    

    TIP

    If the component has scoped styles, all generated styles will be scoped automatically, at runtime. So you can even modify the example above at runtime, dynamically.

    Custom CSS

    Finally you can write custom raw css, using cssCode:

    import { VueThemes } from "@banquette/vue-typescript";
    
    VueThemes.Define('my-button', {
        '*': [
            {
                match: 'danger',
                cssCode: `
                @media (max-width: 1250px) {
                    &.bt-button {
                        font-size: 0.8rem;
                    }
                }
                `
            }
        ]
    })
    

    TIP

    Like for the code generated using cssSelectors, the code you write in cssCode will be scoped automatically if the styles of the component are scoped.

    WARNING

    If you want to target the root element of the component, you must prefix your selector with &, like in the example above.

    Override props

    All props are overridable as soon as you put the @Themeable attribute on the component. You have nothing to do for that.

    There is currently no way to prevent a prop from being exposed, but it could be a future feature if requested.

    Limitations

    There is a limitation you should be aware of when it comes to overriding props. To illustrate we'll look at a real world example with bt-alert.

    bt-alert has a ttl props that controls the time it will remain visible:

    <bt-alert>I'll never disappear</bt-alert>
    <bt-alert :ttl="3000">I'll disappear after 3 seconds</bt-alert>
    

    With the theming, we could assign a ttl by default for each bt-alert of a certain type:

    VueThemes.Define('bt-alert', {
        '*': [
            {
                match: 'success',
                cssVars: {backgroundColor: 'green'},
                props: {ttl: 3000}
            },
            {
                match: 'warning',
                cssVars: {backgroundColor: 'orange'},
                props: {ttl: 5000}
            }
        ]
    });
    

    So now we can do:

    <bt-alert variant="success">I'll disappear after 3 seconds</bt-alert>
    

    But the theme can be overridden by a local value given to the prop:

    <bt-alert variant="success" :ttl="5000">I'll disappear after 5 seconds</bt-alert>
    

    So far so good. The problem arises when you try to get the ttl back to its default value (null in that instance):

    <bt-alert variant="success" :ttl="null">I'll still disappear after 3 seconds!</bt-alert>
    

    Here it doesn't work. The alert will still disappear after 3 seconds.

    That's because, internally, VueTypescript checks if the value still has its default value to decide if it will apply the value of the theme or not. So when you set it back to null, because its the same as the default value, it replaces it by the value of the theme (3000 here).

    I didn't find a way around this, so it will have to do until a solution is found.

    TIP

    A workaround this could be to split the variant into two:

    VueThemes.Define('bt-alert', {
        '*': [
            {
                id: 'success-base',
                match: 'success-no-ttl',
                cssVars: {backgroundColor: 'green'}
            },
            {
                match: 'success',
                apply: ['success-base'],
                props: {ttl: 3000}
            }
        ]
    });
    

    Using apply makes the success variant inherit from the overrides of success-base.

    You can then use it like this:

    <bt-alert variant="success">I'll disappear after 3 seconds</bt-alert>
    <bt-alert variant="success-no-ttl">I'll never disappear</bt-alert>