Theming allows a user to provide styles to the components in this UI library. This section is mostly for developers making components to leverage themes along with solid-ui components, or those who are simply curious about how this system functions.
A theme is made up of the following sections:
- Foundational tokens
- alpha
- animation
- color
- fonts (font files)
- layout (margins, gutters, column counts)
- radius
- spacer (padding)
- stroke
- typography (text styles)
- Component config (component-specific styles)
Theme files contain tokens describing the design decisions specific to that theme. These are high-level things like corner radius values, color palettes, and typography styles. These tokens are global in scope and used by all of the components.
For example, there may be a radius.md
token which represents the value of a medium corner radius, and used by a range of components including buttons, cards, and tiles.
Themes may also contain a set of component-specific styles. Unlike the global foundations, these styles are specific to a single component and not shared.
For example, a component config might contain tokens specifically for a Button's radius
property. This might normally use the radius.md
property, but setting the Button's radius
directly allows a user to change only the appearance of the button without affecting any other components that might use that same global radius.md
token.
The componentConfig
section of a theme file is an object whose keys are the names of components in PascalCase (aka ClassCase). The values of this componentConfig
object are style objects, described in more detail below.
componentConfig: Object {
[ComponentName: string]: Object {
tone?: 'base' | 'neutral' | 'inverse';
mode?: 'focus' | 'disabled';
style?: Object {
[componentProperty?: string]: any;
tone?: Object {
[componentProperty?: string]: any;
mode?: Object {
[componentProperty?: string]: any;
}
}
mode?: Object {
[componentProperty?: string]: any;
}
}
}
}
Themes have two main ways of categorizing component variants, which are referred to as Tones and Modes. Tones describe built-in visual variants of a component. Each solid-ui component supports three tones:
- neutral (the default appearance, using neutral colors)
- inverse (for providing contrast when using neutral colors against an opposite color background)
- brand (meant to call branded focus to an element)
For each component in the componentConfig
definition, you can specify a different default tone for that component by defining the tone
property as a string of one of the three tone values: neutral
, inverse
, or brand
. This will cause that given component to render in that tone by default without requiring a user to explicitly specify the tone on each instance.
For example:
componentConfig: {
ProgressBar: {
tone: 'brand';
}
}
The other way of categorizing component variants in solid-ui is called Modes, which describe the interactive state of a component. Themes and components support a range of modes including:
- none (the default, unfocused state of a component)
- focus (when the component is actively being interacted with)
- disabled (when a user should not be able to interact with a component)
Beyond just setting new defaults for tone
and mode
, the component config object can be used to define component-specific styles in a targeted way—allowing users to re-skin specific components without worrying about the effect on other components' appearances.
For each component in the componentConfig
object, you can include a style
object which contains overrides for that component's default appearance. Each theme property of a component can be defined in the style
object, and that value will be used for all tones and modes of that component, in place of any of its defaults.
For example, this definition will set the foreground color of all progress bars to green:
componentConfig: {
ProgressBar: {
style: {
progressColor: 0x00ff00ff;
}
}
}
In addition, the style
object can also contain mode- and tone-specific values for that component. Any property that is a direct child of the style
object will be applied to every mode and tone of the object—maybe desirable for values like type styles or corner radius values, but potentially less desirable for values like opacity or color.
The style
object also supports tone
and mode
properties, with the value of each being a style object of its own that supports any of a component's theme properties. The values in these nested objects will only be applied to its relevant tone or mode.
For example, this definition will have the following effects:
- All progress bars will have square corners (radius of 0)
- Disabled progress bars for all tones will have an opacity of 25%
- Brand-tone progress bars will be green, but neutral and inverse progress will retain their default colors
componentConfig: {
ProgressBar: {
radius: 0,
style: {
mode: {
disabled: { alpha: 0.25 }
},
tone: {
brand: { progressColor: 0x00ff00ff }
}
}
}
}
In certain circumstances, you may need to set the style of a component for a specific combination of mode and tone, like adjusting the text color of an inverse button when it is disabled.
Within the mode
or tone
property of the style
object, you may also specify one level deeper with the other variant.
For example, this configuration will set the "brand" tone progress bar's color to green for most modes, or else to a darker green when the component is disabled:
componentConfig: {
ProgressBar: {
style: {
tone: {
brand: {
progressColor: 0x00ff00ff,
mode: {
disabled: { progressColor: 0x003300ff }
},
}
}
}
}
}
Themes are loaded at compile time by the app bundler (we recommend vite which is built on top of rollup). In your bundler config, you'll need to add an import alias. We're working on a plugin to clean this up a little but for now:
the app will not run without this configuration
in vite for example:
// vite.config.js
resolve: {
alias: {
theme: '@lightningjs/l3-ui-theme-base',
}
},
or if you have your own local theme:
// vite.config.js
resolve: {
alias: {
theme: path.resolve(__dirname, 'path/to/your/theme.js'),
}
},
Each themeable element in the Component has a similar style property setup. Making use of SolidJS styling features, we can provide a set of styles that are merged together. This allows us to provide a base set of styles that are then overridden in specific instances.
<View style={combineStyles(styles.Container[tone], styles.Container.base)} />
returns an object containing each component element's styles, and the tone. something like
{
tone: 'neutral',
Container: containerStyles,
Text: textStyles
}
Each style file contains a config which will get passed to makeComponentStyles
and has the following properties:
note: the makeComponentStyles
function handles merging our incoming theme and our style file-defined properties
themeKeys
(required)- informs which values we should expect a given componentConfig to contain, and the solid style props to which those keys map
base
(required)- the default styles of a component
themeStyles
(required)- these come in from the theme, the setup looks the same for each component
tones
- style overrides which get applied to a component based on it's tone property
modes
- style overrides which get applied to a component based on it's state
modeKeys
- an array of valid modes for a given component, defaults to
['focus', 'disabled']
- an array of valid modes for a given component, defaults to
toneKeys
- an array of valid tones for a given component, defaults to
['brand', 'inverse', 'neutral']
- an array of valid tones for a given component, defaults to
Tones, Modes, and ToneModes are defined either in the component style file or the theme's component config.
- tones - overrides to a component's default styles
- usually an alternate color pallette
- modes - styles that are applied during the component lifecycle, can be thought of as states (focus, disabled, etc)
- toneModes - variations on mode styles that are applied when a component with a specific tone receives that mode
- ie. when a Button with tone "brand" receives the mode "focus", the styles defined in
brand-focus
would be applied
- ie. when a Button with tone "brand" receives the mode "focus", the styles defined in