With our basic Hello Enact! app built and running, we can start to expand
it by adding some styling. The first stop is defining and managing CSS classes in React followed by exploring CSS modules.
CSS classes are the primary tool for adding visual styling to applications. Classes are assigned
to components using the className property.
<divclassName="customClass">Content</div>
… which renders into the DOM as the rather similar …
<divclass="customClass">Content</div>
You might expect JSX to use class to mirror HTML but that isn’t possible since JSX is ultimately
transpiled to JavaScript, in which class is a reserved word. See DOM
Elements for other instances where
JSX deviates from HTML and the Document Object Model (DOM).
You might be tempted to use hyphenated class names (custom-class),
but CSS Modules, which will soon be covered,
recommends using camelCased naming for local class names (see Naming).
For simple applications, global class names are easy to use and understand. For more complex applications,
you will likely want a way to organize your CSS to improve maintainability and reuse. There are
several methodologies (e.g. Object-Oriented CSS (OOCSS), Block Element
Modifier (BEM), and a host of others) and pre-processors (e.g. SASS,
Stylus, LESS) that offer
different solutions to this problem.
CSS Modules offers another option that works well with other tools and methodologies by focusing on
a narrow concern — modularization. This is the method that the Enact team recommends.
CSS Modules is a specification that allows authors to
write CSS (or SASS or LESS or …) using short, meaningful class names without being concerned about
naming conflicts that may arise when using multiple global stylesheets.
All classes defined in a CSS Module are local by default. In practice, that means that they are
renamed at compile-time to a unique string. In order to use these generated class names, the CSS
Module exports a map of authored names to generated names. For example, the following CSS Module:
.customClass {
background: red;
color: white;
}
would export an object similar to the following:
{
customClass: '_13LGdX8RMStbBE9w-t0gZ1'
}
It’s also possible to declare classes be global using the :global pseudo-selector, which prevents
the name mangling and makes the class reusable across components using the authored name.
:global .customClass {
background: red;
color: white;
}
Would export:
{
customClass: 'customClass'
}
We discourage using global classes with CSS modules because it creates an implicit dependency
between your component and the CSS source file containing the global class. Implicit dependencies
are not tracked by the build tools and may be omitted if the resources explicitly depending
upon them are no longer included.
Let’s create a ./src/App/App.module.less file for our fantastic styling.
The webpack config provided by enact create includes support for the LESS preprocessor, so we’ve
used that file extension here, even though we’re only using standard CSS syntax.
From your component’s perspective, a CSS module is treated like any other module. You can import
it to make it a dependency of your component and to obtain a reference to the class name map.
By convention, we import a component’s stylesheet into the css variable. In addition to the
consistency benefit, it also provides a minor efficiency boost we’ll see later.
You might have noticed that the JSX in the sample above deviates from standard HTML. Notably, the use of the curly brackets ({}) around our css variable. We can’t use
the string, "app", because that would reference a global CSS class rather than the locally-scoped
class name from our CSS module. Instead, we’re using a JSX expression which allows us to embed any
valid JavaScript expression within our JSX markup. The following will evaluate the expression,
css.app, and pass the result as the value of the className property for the <div>.
<divclassName={css.app}>
JSX expressions can be used for property values or entire elements but not component names or
property names.
Valid
<divclassName={a?b:c} />// ✅ property value
{a?<span>A</span>:<span>!A</span>} // ✅ entire child of <div>