Import Class
In the Runtime chapter, we saw how a Stylable stylesheet's API can be imported into a JavaScript module, and how that API can be used. In this chapter, we'll see how our game example can be divided into linked stylesheet modules to provide a better development experience.
Up to this point, we've talked mostly about a single stylesheet for our project (game.st.css
). Now we'll explore how to organize our code across multiple stylesheets.
Current status:
.board { /* ... */ }
.player {
-st-states: registered,
ranking(enum(first, second, third));
background: blue;
}
Stylesheet root
Let's refactor the player
class from out of our game.st.css
to be in its own stylesheet.
.root {
-st-states: registered,
ranking(enum(first, second, third));
background: blue;
}
We created a new player.st.css
file above, and in it, created a new .root
class. Then we moved the contents of the .player
class rules to our new root
class.
In Stylable, each component stylesheet is represented by a root
class that is automatically created, even if not explicitly defined by the user. This root class is expected to be applied to the root element of the component.
We can see that when we moved the player
class, we also included its styling declarations. This might cause additional work in the future when trying to customize this class from the outside.
Stylable recommends that a component's stylesheet include minimal styling - only what is crucial for the functionality of the component or defines its structure. To follow this best practice, we need a way to import the player.st.css
stylesheet over to game.st.css
, and perform our styling customizations there.
Default import
Keep in mind that, as we saw earlier in the Namespacing chapter, classes in Stylable are automatically namespaced. This means that if we wish to target a class from another stylesheet in a selector, we need to import it to link to it.
Below, we import the player.st.css
stylesheet and customize it by using the @st-import
at-rule, and providing it with a local name for the default export (beginning with a capital letter) and a request to the target stylesheet. Then we move any customizations out of player.st.css
into game.st.css
.
@st-import Player from "./player.st.css";
.board { /* ... */ }
Player {
background: blue; /* moved from player.st.css */
}
Namespacing output
@st-import Player from "./player.st.css";
/* namespaced locally */
.game__board { /* ... */ }
/* namespaced by player.st.css */
.player__root {
background: blue;
}
Stylesheet root selector
It's important to note that a default import of a Stylable stylesheet represents a component root, and is in fact pointing to the root
class of that stylesheet.
By importing the default export of a stylesheet and then styling it, we are potentially customizing every instance of the component across our project. It is therefore strongly recommended to scope any selector that originates from a different namespace (stylesheet) or includes native elements with a local class.
@st-import Player from "./player.st.css";
/* report unscoped warnings */
Player {}
div {}
/* no warnings */
.root Player {}
.root div {}
Class named import
For the next step, we would like to add avatar
and username
parts to player.st.css
. These will be applied to the inner elements of the component, and are assumed to be nested under the root
class.
.root {
-st-states: registered,
ranking(enum(first, second, third));
}
.avatar {}
.username {}
We then proceed to bind these new classes to their matching elements in the component.
import { st, classes } from "./player.st.css";
const Player = ({ className }) => {
return (
<div className={st(classes.root, { /* states */ }, className)}>
<image className={classes.avatar} />
<span className={classes.username}></span>
</div>
)
}
Now that we have these new classes, we can customize them externally through game.st.css
. We do this by adding two named imports
, inside square brackets, that link to our classes.
@st-import Player, [avatar, username] from "./player.st.css";
Player {
/* paint the Player component background blue */
background: blue;
}
Player .avatar {
/* circle avatar */
border-radius: 50%;
}
Player .username {
/* all caps username */
text-transform: uppercase;
}
Namespacing output - selectors only
@st-import Player, [avatar, username] from "./player.st.css";
.player__root {}
.player__root .player__avatar {}
.player__root .player__username {}
Validations
One of the nice things that we gain by using these Stylable imports is that requested symbols are validated, and we'll be notified if any of the imported parts are not found.
/* report an error about an unknown "missingImport" symbol */
@st-import [missingImport] from "./player.st.css";
Exported by default
Any classes and other symbols defined in a stylesheet are automatically exported and available for import.
We are in the process of developing explicit export support, and will expand this section once available.
Further import use-cases
Up to this point, we've only needed to import classes between stylesheets. In later chapters of this guide, we will learn how to import and use additional symbols, as well as additional import use cases from JavaScript to our stylesheet.
Check out the API cheatsheet to see various @st-import examples.
In this chapter, we've seen how to divide a stylesheet to separate modules, and how to customize those externally. We approached doing this by manually importing each class (default or named), and customizing them together using a selector.
In the Next chapter, we'll learn how Stylable can extend stylesheets, exposing their inner parts as an API to make our lives easier.