Rendering concepts
To understand Aesthetic and the rendering process, there are a few key concepts around styling and its structure. All of the concepts below assume a web based CSS-in-JS integration is being used.
#
StylesAll styling is defined through style objects, which are plain JavaScript objects that map CSS properties to values, also known as a CSS declaration. Properties can be defined in either camel-case or kebab-case.
There are multiple subtypes of style objects that provide additional functionality, and can be found below.
#
RulesA rule (or ruleset) is a collection of multiple CSS declarations, with optional support for infinitely nested rules and selectors, and are known as rule objects.
Rules can be rendered with the renderRule() method, while individual declarations can be rendered with renderDeclaration().
#
Pseudo selectorsPseudo classes and pseudo elements are a highly important features of CSS, and as such, are supported in rule objects by defining nested rule objects, where the key is the selector in question.
For pseudos to be recognized, they must start with :
or ::
.
#
Attribute selectorsLike pseudos,
attribute selectors
are an important feature and are defined in a similar fashion, but must start with [
.
#
Combinator selectorsAnd finally, all
combinator selectors, excluding
descendent, are supported in @selectors
. Nested rules must start with >
, ~
, +
, or |
.
#
Media queriesMedia queries
are useful when you want to render styles based on device viewport. They are represented by the
@media
at-rule and can be defined using nested rules.
Media queries can nest itself and other selectors.
#
Feature queriesFeature queries
are useful when you want to render styles based on features that are currently supported by the
browser. They are represented by the @supports
at-rule and can be defined using nested rules.
Feature queries can nest itself and other selectors.
#
VariablesVariables, also known as custom properties, are a feature that allows for specific values to be reused throughout the CSS document. CSS variables defined within a rule are scoped to the element in which the class is applied to, not the document root.
Variables can be defined in @variables
, with or without the leading --
. Furthermore, values are
not processed like properties are, so any suffixes (like px
) will need to be explicitly defined.
Root level variables can be defined with the setRootVariables() method.
#
Font facesFont faces allow for custom fonts to
be used, are represented by the @font-face
at-rule, and are defined using a font face object.
Font face objects only support a subset of properties, primarily font related ones, and the src
property, which points to a source font file (relative or absolute to the document root).
Font faces can be rendered with the renderFontFace() method.
#
ImportsImports are used to include external
CSS style sheets, not CSS-in-JS, with
the @import
at-rule. Aesthetic abstracts all the complexity away, so only the URL should be
provided.
Imports can be rendered with the renderImport() method.
#
KeyframesKeyframes are CSS animation sequences
that are represented by the @keyframes
at-rule, and are defined using a keyframes object. A
keyframes object maps frames to rules, where each frame is one of to
, from
, or a
percentage.
Keyframes can be rendered with the renderKeyframes() method.
#
CaveatsBecause both atomic CSS and CSS-in-JS is utilized, there are a few caveats to be aware of while using this library.
#
Properly quoted valuesAny property value that requires quotes must be properly quoted or escaped on the JavaScript side.
This primarily applies to the content
property and @import
at-rule, but is useful in other
contexts.
#
Mixing of shorthand and longhand propertiesOne of the biggest disadvantages to atomic CSS is that shorthand properties make it incredibly difficult to apply styles correctly. Since a shorthand property is the combination of multiple properties, but is represented as a single class name, how can we ensure the correct specificity order? Take the following for example.
If this example was rendered as-is, the border color would be white, since borderColor
is ordered
after border
. However, when used in an application with hundreds of rules, we can't ensure this
order, as borderColor
may have been rendered much earlier than border
, or vice-versa. Now
compound this problem with the other properties involved, like borderStyle
and borderWidth
.
Which should take precedence? And how?
To work around this, we suggest always using the longhand properties, since it's far more explicit. This suggestion does require more code and overhead, but is better in the long-term for maintainability.
In the future, we will provide an ESLint rule to help mitigate this issue.