widgets
widgets copied to clipboard
Documentation: Theme Compose
Document the use of theme compose
My notes reading through the code and tests.
The function is used when a widget is created within a parent widget's source.
-
baseCss
: The embedded widget's CSS -
css
: The CSS of the widget which is returning the vdom -
prefix
: A string to use as a prefix
The return value should be passed to the embedded widget's theme
property.
Benefit:
For the widget author, prefixed classes can be written that will completely replace that class in the embedded widget.
For the widget user, instead of writing a classes
object that targets a descendent widget by type, rules are added to the parent widget type by writing the class name as it appears in the targeted widget using the assigned prefix. Should these prefixes be documented somewhere to make it clear what classes can be used?
How it works
TLDR
- If a prefix is passed, we check to se if there is a prefixed version of the embedded widget's keys in either the theme or the primary widget's CSS. If there isn't, we use the original styles (and passed
classes
) for the embedded widget. If there is, we use the theme's prefixed versions, followed by the prefixed version in the primary widget's CSS. In both cases, we tack on the prefixed key from theclasses
for the primary widget.
Details
- If a prefix is passed, the original keys from the theme and
baseCss
will be replaced:- If a prefixed key does not appear in the theme or
css
for the primary widget and one of the following does exist:- the original key from
baseCss
for the embedded widget - followed by the original key from
classes
for the embedded widget - followed by the prefixed key from the
classes
for the primary widget
- the original key from
- If a prefixed key appears in the theme or
css
for the primary widget:- the prefixed key from the theme for the primary widget
- if not in the theme, the prefixed key from
css
for the primary widget - followed by the prefixed key from
classes
for the primary widget
- If a prefixed key does not appear in the theme or
- With no
prefix
, keys from the theme andbaseCss
will be replaced with:- If a key exists in the theme or
css
for the primary widget:- the key from
css
for the primary widget - followed by the key from
classes
- the key from
- Otherwise, if the key exists in
baseCss
for the primary widget with one of the following:- the key from
baseCss
for the embedded widget - followed by the key from
classes
for the embedded widget - followed by the key from
classes
for the primary widget
- the key from
- If a key exists in the theme or
We have different use cases to consider here
Widget Authors
- As a widget author, you initially write the default theme and use child widgets to create your widget.
- if you need to provide structural styles to a child widget or wish to provide a means to theme the child via a prefix class you use
theme.compose
rather than just passingtheme
. - the styles in the default theme will be additive to the child widgets styling
Theme Authors
- theme modules using the prefixed
theme.compose
classes will override the base widgets theme. - authors wishing to use a prefixed class name whilst maintaining base styles can use
composes
.
/* loginForm.m.css with a submit button using `submit` prefix */
.submitRoot {
composes: root from './button.m.css';
}
.root .submitRoot {
/* overrides go here, nest under root selector for local specifity */
margin: 10px;
}
Widget users
- a widget user wanting to override the above submit button root can use
classes
to target the submit button without needing to know what type of widget the login form is using. ie.
// with theme.compose use
classes: {
'@dojo/login-form': { submitRoot: [ myAdditionalClass ]}
}
// without theme.compose use
classes: {
'@dojo/unknown/button/type': { root: [myAdditionalClass ] }
}
Widget Authoring
Many widgets embed imported widgets in their returned vdom. Often, changes must be made to the styling of these embedded widgets both to support its placement within the parent widget as well as supporting any stylistic changes users and theme authors may want to make.
Using theme.compose
allows all rules for embedded widgets to be written in your widget's CSS file or in a theme with a prefix rather than having to make alterations in code using classes
. It also allows, in cases where you are embedding two widgets of the same type, for a specific instance to be easily targeted.
When to Use classes
If a class should be added situationally, they should appear situationally in the embedded widget's classes
. Feel free to use both theme.compose
and classes
.
<Button
classes={{
'@dojo/widgets/button': {
root: [isActive ? themeCss.activeButton : null]
}
}}
theme={theme.compose(buttonCss, css, 'button')}
>
</Button>
Overriding or Composing
When a prefixed class appears in the imported CSS or theme, it will override that class in the embedded widget—that node no longer will have the resolved class applied to it which will also remove any nested styles depending on it.
Often, this is intentional, but if your goal as a user or theme author is to augment the existing styles, you will want to use the CSS Modules composes
functionality.
By using composes
, both the new resolved class and the original resolved class will be applied to that node in the embedded widget.
From the CSS Modules documentation:
Note that when composing multiple classes from different files the order of appliance is undefined.
What this means is that your rules and the original rules will be of equal specificity and dependent on the order the style sheets are imported. For this reason, composed rules should have the following format:
.buttonDisabled {
composes: disabled from './button.m.css';
}
.root .buttonDisabled {
opacity: 0.5;
}
Example
widget.tsx
<div classes={root}>
<Button theme={theme.compose(buttonCss, css, 'cancelButton')>Cancel</Button>
<Button theme={theme.compose(buttonCss, css, 'saveButton')>Save</Button>
</div>
widget.m.css
/* modifies the .disabled class in the Button with prefix 'cancelButton' */
.cancelButtonDisabled {
opacity: 0.5;
}
/* modifies the .disabled class in the Button with prefix 'saveButton' */
.saveButtonDisabled {
text-decoration: bold;
}