Mission statement:
Decoupling from styling for UI-components should be on similar level as in native HTML/CSS (HTML element is decoupled from its styling by exposing an API to inject styling via
class
attribute orclassName
property).
Requirements:
- UI component is decoupled from its styling. Styling is injected into the component via its API.
- UI component has its default, minimal styling, in case no styling is injected.
- Default styling may be overwritten by custom styling via styling-API.
- Default styling absent from the styling-API, can be also overwritten (although "unsafely", with regards to SemVer).
- Styling can be set for the UI-component and its children (and grand-children etc.)
- It's possible to customise the styling for the whole application in one run (styling theme).
Additional requirements (no steps backwards):
- UI components styles are isolated (no name-conflicts, styles don't leak-in and out)
- No implicit dependencies to styles
- UI components should be decoupled from each other, as far as possible
To simplify examples, global CSS-classes are used.
- Native element with default styling:
<textarea />
- UI-Component with default styling:
<MyButton />
- Native element with custom styling (pass a CSS-class):
<textarea className="center" />
- UI-Component with custom styling (pass an object containing CSS-classes):
<MyButton classes={rootPosition: "center", content: "fixed-width"} />
It's up to the component itself to expose its styling-API.
- Higher-level component (nested objects containing CSS-classes to overwrite children styles):
<App classes={title: "big", okButton: {rootPosition: "left"}} />
- Install dependencies:
npm install
- Run dev server:
npm start
- Go to: http://localhost:3000
- Styling API - TypeScript interface
- Styling injected into parameter
- Implemented default styling and injected styling
- Component variants can be implemented in various ways - here is the simplest - as a separate class
MyButton
usage- Styling API - contains also children styling API
- Nested component's styling is customised using
__
markup in class-names (in otherwise flat classes structure)
- Themes based on
App
component default styling - Theme A, Theme B - Reset to component's default setting (ignore any mid-level component customisations)
- Any level of customisation is possible, it's just CSS
- Theme used
- To make nested component customisation possible with CSS Modules, the
nested()
function is used in components to construct nested-object styling-API from flat CSS files. CSS Modules don't support nested component's customization yet - see issue. - Overwriting component's default styles is done "per class" (instead of "per CSS-property" like natively in CSS). In case there is need to alter just one CSS-property, it's possible to use composition like here in
app
component. - native CSS overwriting solution by CSS Cascade is not used (unless composition is used). CSS-classes are not combined, but simply replaced if customised.
- when composition is applied, CSS Modules does not copy the styles but concatenates selectors to extend styles