Last updated May 5, 2013. Created by JohnAlbin on January 14, 2013.
Edited by tim.plunkett, izus, carwin, manish_mics. Log in to edit this page.
Note: This document describes a file organization and aggregation strategy that has not yet been implemented in Drupal 8. See this core issue for that on-going work: http://drupal.org/node/1921610
File Structure
Rulesets should be grouped into logical files that enforce the separation of concerns within the CSS, that can be aggregated efficiently and that can be easily overridden by themers.
Drupal 8 follows a SMACSS-style categorization of its CSS rules:
- Base — CSS reset/normalize plus HTML element styling.
- Layout — macro arrangement of a web page, including any grid systems.
- Component — discrete, reusable UI elements.
- State — styles that deal with client-side changes to components.
- Skin — purely visual styling (“look-and-feel”) for a component.
For more information about Drupal 8’s use of SMACSS, see the “Separate Concerns” section of the CSS Architecture page.
These categories help us keep CSS styles with different purposes from getting intertwined with one another. However, they don’t directly dictate file structure. Drupal 8 recommends the following guidelines for structuring CSS across files:
CSS files for Drupal modules
All of a module's styles should be placed in a css/ sub-directory and broken into one or more of the following files:
module_name.module.css: This file should hold the minimal styles needed to get the module's functionality working. This includes layout, component and state styles. Any needed RTL styling would go in a file named module_name.module-rtl.css.
module_name.theme.css: This file should hold extra styles to make the module's functionality aesthetically pleasing. This usually just consists of theme styles. Any needed RTL styling would go in a file named module_name.theme-rtl.css.
module_name.admin.css: This file should hold the minimal styles needed to get the module's admin screens working. This includes layout, component and state styles. On admin screens, the module may choose to load the *.module.css in addition to the *.admin.css file. Any needed RTL styling would go in a file named module_name.admin-rtl.css.
module_name.admin.theme.css: This file should hold extra styles to make the module's admin screens aesthetically pleasing. This usually just consists of theme styles. Any needed RTL styling would go in a file named module_name.admin.theme-rtl.css.
Note: Modules should never have any base styles. Drupal core's modules do not have any base styles. Instead Drupal core uses the Normalize.css library augmented with a drupal.base.css library.
If a module attaches a CSS file to a template file, the CSS file should be named the same as the template file, e.g. the system-plugin-ui-form.html.twig CSS file should be named system-plugin-ui-form.css
CSS files for Drupal themes
- Always separate Base, Layout, and Component styles into their own files. (Drupal will aggregate these separate files into one file, so there is no performance problem with this practice.)
- For complex themes, consider placing each component or component family in its own file.
- State rules, including media queries, should be included with the component to which they apply.
- Theme rules may or may not have their own file(s). Starter themes are encouraged to separate their theme CSS into separate files. Otherwise, include theme rules with their corresponding component.
Most themes will have at least the following:
+ css
- base.css
- layout.css
- components.cssOptionally, these can be further broken out. Below the base/layout/component level, naming is up to the module or theme. A more complex file structure using normalize.css and a mobile-first approach might look like this:
+ css
+ base
- normalize.css
- elements.css
+ layout
- layout.css
- layout--medium.css
- layout--wide.css
+ components
- button.css
- dropdown.css
- pagination.css
- tabs.css
…
+ theme
- theme--light.css
- theme--dark.cssAggregating CSS
In Drupal 7 and earlier, the “group” option and the “every page” option of drupal_add_css() controlled which files would be aggregated together. The groups were CSS_SYSTEM, CSS_DEFAULT (for Drupal modules), and CSS_THEME (for Drupal themes) and the every_page option was a simple boolean flag. This meant there were up to 6 aggregated files.
In Drupal 8, themes now have the ability to override stylesheets without affecting the “every page” option of the original stylesheet. See http://drupal.org/node/1876600. This means conditionally-loaded CSS remains conditionally loaded.
This also means it’s now simple for themes to affect conditionally-loaded styles, so all the theme-added CSS no longer needs to load after all conditionally-loaded styles. This greatly simplifies the default grouping strategy that Drupal 8 can employ. Instead of 6 aggregated files, we can just have 2:
-
The “Every page” aggregate. Includes:
- Base styles from libraries (like Normalize.css and drupal.base.css) and then Drupal themes.
- Layout styles from Drupal modules and then Drupal themes.
- Component styles (and their associated states and themes) from Drupal modules and then Drupal themes.
- State styles that are not included with components, from Drupal modules and then Drupal themes.
- Theme styles that are not included with components, from Drupal modules and then Drupal themes.
-
The conditionally-loaded aggregate. Includes:
- Layout styles from Drupal modules and then Drupal themes.
- Component styles (and their associated states and theme) from Drupal modules and then Drupal themes.
- State styles that are not included with components, from Drupal modules and then Drupal themes.
- Theme styles that are not included with components, from Drupal modules and then Drupal themes.
Note: there should never be conditionally-loaded base styles.
The order of styles within an aggregate are determined by the “weight” option of drupal_add_css() which will be:
/**
* The default weight for CSS rules that style HTML elements ("base" styles).
*/
const CSS_BASE = -200;
/**
* The default weight for CSS rules that layout a page.
*/
const CSS_LAYOUT = -100;
/**
* The default weight for CSS rules that style design components (and their associated states and themes.)
*/
const CSS_COMPONENT = 0;
/**
* The default weight for CSS rules that style states and are not included with components.
*/
const CSS_STATE = 100;
/**
* The default weight for CSS rules that style theme and are not included with components.
*/
const CSS_SKIN = 200;The aggregated file itself is determined by the "group" option and the "every page" flag of drupal_add_css(). The group option defaults to the CSS_AGGREGATE_DEFAULT constant. Drupal core does not provide any other aggregate constants, but contrib can define their own aggregate groups.
Comments
Should this be aligned with
Should this be aligned with the BAT / BATMAN organization of CSS files? http://drupal.org/node/1536682
Yep!
You are absolutely right! The BAT organization strategy is a great strategy that is already incorporated into D8. We need to update this document to show how these ideas work together. I've marked the document as "incomplete" in the meantime.
- John (JohnAlbin)
At Drupalcon Sydney, we
At Drupalcon Sydney, we worked out the new CSS file organization strategy. It turned out we just needed to rename the BAT strategy file names, but the original D8 BAT idea was solid.
- John (JohnAlbin)
Module/State/Theme grouping
John-
I've spoken with Jonathan a bit about Modules, States and Theme blocks - and I think his intent is to actually keep the states and themes grouped with each module (along with media queries, but that's not really relevant here). In this way you would keep all the possible variations of a particular module (say a list of news headlines) together, rather than splitting them into separate files. For example:
/* MODULE: News Headlines */
.news-headlines {
attribute: value;
}
/* STATE: News Headlines in sidebar */
.sidebar .news-headlines {
modified-attribute: other-value;
}
If you start splitting States & Themes into separate files it gets to be really burdensome to edit - especially if you end up adding media queries into the mix.
Hi there. I believe that
Hi there.
I believe that example perhaps not inline with the definitions listed about. A state should not be a css rule which changes the appearance of an module due to its location. To do that the correct way I believe you would have a separate class which you would group under theme that targets the specific attributes you are altering for the module.
As state is described as " JavaScript-dependent styles" state classes would be things like .is_error { border: 1px solid red;) to apply to form field errors, or .is_hidden {
display: none;} to apply to elements through JavaScript to hide them.
Agreed.
Yep. Selectors that are location-specific can't be reused either; you'd need a new selector to re-use the same group of styles.
- John (JohnAlbin)
You're right!
You are correct. Modules, states and themes don't need to be in separate files. That means we'll need to update the document so that these are arbitrarily separated.
- John (JohnAlbin)