Component style sheets
Components and their tree are styled through style sheets created with the createComponentStyles()
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.
- Component
- Elements
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!
#
Defining stylesThe 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
vendorPrefixer
setting to enable this automatically. - Unit values that default to the
defaultUnit
(typicallypx
) setting can be written as literal numbers. - Values that require quotes in the CSS output must manually handle the quotes within the string.
#
SelectorsThere 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.
- Component
- Elements
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
with TypeScript.
- Component
- Elements
#
Media and feature queriesMedia 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.
- Component
- Elements
Both
@media
and@supports
may be nested within itself and each other.
You can utilize the design system token's for consistent media query breakpoints.
- Component
- Elements
#
Font facesFonts 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 src
property.
- Component
- Elements
#
KeyframesAnimations 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.
- Component
- Elements
#
VariantsVariants 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
passed to cx()
.
- Component
- Elements
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_
and-
. The type must start with a letter.
#
Applying variantsHow 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!
- Component
- Elements
When using
createThemeStyles
, the only selector available is "element".
#
Handling defaultsWhen 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.
- Component
- Elements
#
Compound variantsWhen you need to set variant styles based on a combination of other variants, you can combine them
using a +
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 palette
is
brand, we would do the following:
- Component
- Elements
You can combine as many variants as you'd like! Just be sure the variant names are properly combined with
+
.
#
VariablesDefines 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.
- Component
- Elements
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.
#
Expanded propertiesCSS has a concept known as
shorthand properties, where
multiple properties and their values can be declared with a single property. For example,
border-width
, border-style
, and border-color
can be combined in border
.
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: animation
,
background
, border
, borderBottom
, borderLeft
, borderRight
, borderTop
, columnRule
,
flex
, font
, listStyle
, margin
, offset
, outline
, padding
, textDecoration
, and
transition
;
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:
- Component
- Elements
#
Overriding stylesWhile 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 schemeUse the 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 levelUse the 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.
#
By themeAnd 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
registerTheme()
orregisterDefaultTheme()
.
#
Rendering CSSRendering 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 renderComponentStyles()
method.
This method requires the style sheet instance as the 1st argument, and returns an object of class names mapped to their selector.