Component style sheets
Components and their tree are styled through style sheets created with the
method, which styles multiple elements, and
createElementStyles(), which styles a single
element. Choose the pattern that is right for you!
For the purpose of this documentation, let's say we're building a button component that renders many elements and components, we would have a style sheet that looks something like the following.
When using this approach, an object can be provided instead of an object if the utilities argument (
css) is not being used.
In the examples above, the keys of the object are known as selectors, with each selector being a combination of element and optional modifier (separated by an underscore). This is similar to the popular BEM syntax, without the "block", as our style sheet is the block (since styles are isolated). Style sheets support as many selectors as needed!
The foundation of all styles are properties, where each key-value pair maps to a CSS declaration, and abides the following:
- Property names are camel cased versions of their CSS equivalent property.
- Vendor prefixed properties are not supported. Use the
vendorPrefixersetting to enable this automatically.
- Unit values that default to the
px) setting can be written as literal numbers.
- Values that require quotes in the CSS output must manually handle the quotes within the string.
There are 2 types of selectors, the first being basic selectors, which includes pseudo elements, pseudo classes, and HTML attributes that are deterministic and do not have permutations. They can be defined as nested style objects directly on the element's style object.
The other type is advanced selectors, which includes combinators, as well as pseudos and attributes that do have permutations. Furthermore, multiple selectors can be defined at once using a comma separated list.
Advanced selectors must be nested within a
@selectors object as they can not be properly typed
#Media and feature queries
Media and feature queries can be defined within a style object using media queries and supports respectively. Both types require an object that maps query expressions to nested style objects.
@supportsmay be nested within itself and each other.
You can utilize the design system token's for consistent media query breakpoints.
Fonts are special as they need to be defined on the document instead of an element, which should be
done with a theme style sheet. However, we provide some convenience through the
fontFamily property, which can accept one or many font face objects.
Unlike normal CSS font faces, a font face object supports a
srcPath property, with a list of
file paths, instead of a
Animations have the same semantics as fonts and should be defined on a document using a
theme style sheet, but also like fonts, we provide some convenience through the
animationName property, which accepts a single keyframes object.
Variants are a staple feature of many components -- especially commonly used ones like buttons, alerts, and labels -- and encompasses everything from sizing (small, large) to palettes (positive, negative, etc).
With that being said, the guiding principle behind variants is that only 1 may ever be active at a time. If you need to apply more than 1, then you should use the element-modifier syntax mentioned at the beginning of the chapter.
To utilize variants, we define a
@variants object on a per element basis that maps each variant
type:enum) using nested objects. Variant names are critically important as they must match what's
Variant names must be formatted correctly! Each name combines a type to an enumerated value with a
:. Both the type and enum support alphanumeric characters, while the enum also supports
-. The type must start with a letter.
How a variant gets applied is highly dependent on the integration you are using, but it basically boils down to the following class name generation. Pass an object of variants and their enumerations as the 1st argument!
createThemeStyles, the only selector available is "element".
When handling default styles for a variant, you must define it as a variant instead of defining it on the element directly. This is necessary as it avoids style collisions and specificity issues.
When you need to set variant styles based on a combination of other variants, you can combine them
+ operator. This synax should be familiar as it's based on CSS.
Using the example above, say we want to bold the text when the
size is large, and the
brand, we would do the following:
You can combine as many variants as you'd like! Just be sure the variant names are properly combined with
Defines element level CSS variables, by mapping variable names to their value. Names can be in camel
case or variable kebab case (prefixed with
--). Useful for overriding root and theme CSS variables
on a per element basis.
Variable values are not transformed in any way, so they must be explicit. For example, unitless values are not supported for values that require a unit suffix.
CSS has a concept known as
shorthand properties, where
multiple properties and their values can be declared with a single property. For example,
border-color can be combined in
However, in CSS-in-JS, shorthand properties cause issues when defined alongside their longhand
properties, so Aesthetic offers an expanded form for a handful of shorthand properties (cue irony)
through the @aesthetic/addon-properties
package. The current shorthand properties that support an expanded form are:
To utilize the expanded form, define an object where each property within maps to an equivalent longhand property. Using the border example above, the object would look like the following:
While we support variants per element, we also support overrides on the style sheet. When defined at this level, any override deemed active will be deeply merged into a single style sheet in the order of: base < color scheme < contrast level < theme.
This feature will override any selector, element, element at-rule (even their variants), or nested style object from the base style sheet! This makes it very powerful and very robust.
#By color scheme
addColorSchemeOverride() method for overrides depending on the "light" or "dark" color
scheme of the currently active theme. This is perfect for making slight changes to a theme between
the two modes.
This is equivalent to the native
prefers-color-scheme media query.
#By contrast level
addContrastOverride() method for overrides depending on the "low" or "high" contrast level
of the currently active theme. This is perfect for providing accessible themes.
This is equivalent to the native
prefers-contrast media query.
And finally, use the
addThemeOverride() method for overrides depending on the currently active
theme itself. This provides granular styles on a theme-by-theme basis, perfect for style sheets that
are provided by third-parties.
Theme names must match the names passed to
Rendering a style sheet into CSS and injecting into the document is typically handled by an
integration and abstracted away from the consumer (see
useStyles() in the React package). However,
if you would like to render styles manually, you may do so with the
This method requires the style sheet instance as the 1st argument, and returns an object of class names mapped to their selector.