Themes
A theme is a container of variants. A variant a set of overrides that can be applied to a component.
You can define as many themes as you want, each containing as many variants as you want.
All of that dynamically, whenever you want, using the VueThemes
class.
VueThemes
VueThemes
a simple static class that you can import anywhere in your project:
import { VueThemes } from "@banquette/vue-typescript";
If contains the following methods:
Method | Description |
---|---|
Shorthand to define multiple variants at once, as an object. | |
Get the current global theme. | |
Get a specific theme by name and create it if it doesn't exist yet. | |
Check if a theme is defined. | |
Remove a theme. | |
Subscribe to an event that will trigger each time a new theme is added. | |
Subscribe to an event that will trigger each time the current theme changes. |
The Define
method is the one you should consider using to define a theme because that's the more convenient. Here is an example that illustrates all the possible options:
// We define a theme for all components named "my-button".
VueThemes.Define('my-button', {
// The key is the name of the theme for which we will define variants.
light: [
// Then we define a list of variants, each having a "match" selector
// and a set of rules to apply.
{
id: 'outline', // Unique id of the variant (optional), used by "apply".
// The css variables to inject if the variant matches.
cssVars: {
backgroundColor: 'none'
},
},
{
id: 'danger',
match: 'danger', // The rules to match.
cssVars: {
backgroundColor: 'red'
},
// The default value to assign to the props.
props: {
type: 'primary' // Assign "primary" as default value for the prop "type".
},
// Ids of other variants to apply if this one matches.
apply: ['outline'],
// An object where each key is the name of a selector exposed by the component
// and the value is a key/value pair of CSS rules/values.
cssSelectors: {
root: {
// Here you can put ANY css rule you want.
// What you put here is directly converted into CSS code.
// Each key must be a camelCased version of the CSS rule.
zIndex: 100,
fontFamily: 'Arial',
borderRadius: 'var(--base-border-radius)'
}
},
// Raw css code that will be scoped automatically
// to only apply to the targeted component in the current theme and variant.
cssCode: '.title { color: blue }'
},
{
match: '*' // Matches everything, always applies.
}
],
dark: [
...
],
// Wildcard theme, always applies.
'*': [
...
]
});
TIP
You can put *
as theme name or variant name to say that you want it to apply to every theme or variant, no matter their name.
It's a wildcard.
Theme matching
The current theme can be set in two ways:
- globally, using the
VueThemes
class - locally, using the
bt-theme
component.
To set it globally:
import { VueThemes } from "@banquette/vue-typescript";
// You can call this anywhere, anytime.
// All components will automatically adjust.
VueThemes.SetCurrent('dark');
TIP
You can even call SetCurrent
before creating the theme or before defining its variants.
It will apply as soon as it can.
To set it locally:
You can set a theme for just a part of your page using the bt-theme
component:
Variant matching
Each variant you define in your theme has a match
attribute that defines the rules the component must match for it to be applied.
This is called a VariantSelector
, and offers the following criteria:
Match on variant
{
match: {variant: 'danger outline'},
[...]
}
Each variant is separated by a space, the component will have to have all of them for the variant to apply. The order in which they are defined doesn't matter.
TIP
variant
is the default option for the variant selector, so the following is equivalent:
{
match: 'danger outline',
[...]
}
Match on parent
You can filter your variants based on their parent:
{
// The rules will apply if the component is inside a `bt-button`.
match: {parent: 'bt-button'},
[...]
}
<bt-button>
<!-- Will match -->
<my-component></my-component>
</bt-button>
<bt-button>
<other>
<!-- Will match too -->
<my-component></my-component>
</other>
</bt-button>
<!-- Will not -->
<my-component></my-component>
WARNING
This only works with components created with VueTypescript
because for other components there is no way to know
their name from the $parent
attribute.
But there is more. parent
is actually a VariantSelector
, so everything you can do with match
, you can also with parent
:
{
match: {
// The rules only apply if the component is inside a `bt-button`
// with a "danger" variant.
parent: {name: 'bt-button', variant: 'danger'}
},
[...]
}
NOTE
Just like for variant
, the default option of parent
is its name, that's why you can do:
{
// The following:
match: {parent: 'bt-button'},
// is equivalent to:
match: {parent: {name: 'bt-button'}}
}
Match on props
You can also match on the props of the component:
{
// The rules will apply if the component has a prop `disabled` with the value `true`.
match: {props: {disabled: true}},
}
<my-component :disabled="true"></my-component>
Match on attributes
The same goes for HTML attributes:
{
// The rules will apply if the component has an HTML attribute
// `data-custom` with the value `value`.
match: {attrs: {'data-custom': 'value'}},
}
<my-component data-custom="value"></my-component>
Props and attrs callbacks
Both props
and attrs
can take a callback as parameter, so you can test more complex testing that juste equality:
{
match: [{props: {progress: (v) => v < 40}}],
[...]
}
The callback takes the current value of the prop or attribute being tested and returns a boolean
.
Putting it all together
Here is an example of all we've discussed so far working together: