Skip to header Skip to main navigation Skip to main content Skip to footer

Main navigation

  • Home
  • Drupal Theming
  • Web Designer In Austin
  • Drupal Development Portfolio (opens in new tab)
  • Blog
  • Videos
  • Contact
Web Designer In Austin
Designing Drupal, Defining Distinction

SMACSS Categories in Drupal .libraries.yml: Weights, Groups and Load Order

Alaa Haddad, professional Drupal developer based in Austin, TX   Drupal Care
  10:38 PM CDT, Mon September 14, 2026
Share

The SMACSS category you nest a stylesheet under in .libraries.yml is not documentation — it sets a numeric weight that decides load order, and Drupal adds any weight you also declare on top of it. Most theme developers know the five category names. Far fewer know that the weights are additive, and almost nobody knows that the aggregation group is compared before the weight, which is why a module's "theme" stylesheet still loads before your theme's "base" one.

Get those three facts straight and a whole category of cascade bugs stops happening. Every figure and behaviour below was read out of Drupal 11.4.6 core on disk.

The five categories and their weights

SMACSS is a way of classifying stylesheets by what kind of job they do. Drupal adopted the vocabulary and attached a number to each name, defined as constants in core/includes/common.inc:

Drupal's SMACSS categories, their constants and their default weights
Category keyConstantWeightWhat belongs here
baseCSS_BASE-200Rules that style bare HTML elements: resets, typography defaults
layoutCSS_LAYOUT-100Page-level structure and grids
componentCSS_COMPONENT0Discrete reusable pieces: a card, a button, a pager
stateCSS_STATE100State variations not shipped with the component itself
themeCSS_THEME200Purely visual skin applied over a component

Nesting is not optional. Drupal asserts that every CSS entry in a library sits under one of these keys, and it derives the constant name mechanically by upper-casing the key you wrote. A misspelled category does not fall back to a default — there is no constant to look up.

The part that is not in the tutorials: the weights add

When Drupal parses a library it takes the weight you declared on the file — zero if you declared none — and adds the category constant to it. It does not replace it.

global-styling:
  css:
    layout:
      css/layout.css: {}              # weight becomes -100
      css/layout-wide.css: { weight: 10 }   # weight becomes -90
    theme:
      css/skin.css: { weight: 999 }   # weight becomes 1199

This is genuinely useful once you know it. Ordering two files inside the same category is a small positive or negative number, and the category keeps them in the right band relative to everything else. It is also a trap: a large weight, copied from an example somewhere, can push a file clean out of its category's band and past files it was never meant to overtake.

The part that surprises everyone: group beats weight

Before Drupal compares weights at all, it compares aggregation groups. Module CSS is placed in the default aggregate group; theme CSS — along with single-directory component stylesheets — is placed in the theme aggregate group, which sorts later. The comparison is group first, weight second, and only then does insertion order break remaining ties.

The consequence is worth stating plainly, because it contradicts what the weights alone imply: every stylesheet a theme declares loads after every stylesheet any module declares, whatever category either of them used. A module file nested under theme at weight 200 still loads before your theme's file nested under base at weight -200. The 400-point difference in weight never gets consulted.

So SMACSS categories order assets within your theme. They are not a mechanism for beating a module. If you need to override module CSS, you already win on order by virtue of being a theme, and if you are still losing, the cause is specificity rather than order — which is a different diagnosis with a different fix, covered in the sub-theme CSS diagnostic order.

Structuring a theme's libraries this way

The pattern that holds up on real projects is one global library carrying only what genuinely appears on every page, plus small named libraries attached where they are needed.

global-styling:
  css:
    base:
      css/base/reset.css: {}
      css/base/typography.css: {}
    layout:
      css/layout/page.css: {}
    component:
      css/components/button.css: {}
      css/components/card.css: {}
    theme:
      css/theme/colors.css: {}

article-styling:
  css:
    component:
      css/components/article.css: {}

Declare the global one in your theme's .info.yml under libraries:, and attach the conditional ones where they apply — from a preprocess function via $variables['#attached']['library'][], or directly in a Twig template with {{ attach_library('mytheme/article-styling') }}. The point is not tidiness for its own sake: a page that never renders an article should not download the article stylesheet.

For a worked example of a theme that does this at scale, the Solo theme's library file declares 288 named libraries with per-file weights inside categories, which is what lets it load fonts, menu variants and colour schemes only where they are used. The evaluation checklist for free Drupal themes covers how to judge that kind of structure when you are picking a base.

What SMACSS categorisation does not do

  • It does not fix specificity. Order and specificity are independent. A later file with a weaker selector still loses.
  • It does not reduce the number of requests. That is aggregation, a separate setting under Configuration → Development → Performance.
  • It does not enforce anything about the contents of the file. Nothing stops you putting a colour in base. The categories are a promise you make to your future self.
  • It does not let you order across extensions. As above — that is the group, and you do not control it from a category key.

Common questions

What happens if I misspell a category?

In development, with assertions active, Drupal tells you directly that the category is invalid and points at the documentation. Assertions are normally compiled out in production, and the code path then tries to read a constant that does not exist, so treat a category typo as something to catch before deployment rather than something that degrades gracefully. Test the exact behaviour on your own build if it matters — needs checking for your PHP configuration.

Can I skip categories and just use weights?

No. Drupal asserts that CSS is nested under a category, and the parser iterates the category keys rather than the file list, so an un-nested file is not merely unweighted — it is not read as a stylesheet at all.

Are the weights the same in Drupal 10 and 11?

The constants read from core on disk here are from 11.4.6 and are unchanged from the values Drupal has used since 8.x. If you are targeting Drupal 12, read them in your own copy of core rather than trusting this page.

Does this apply to JavaScript too?

Not in the same way. JS has its own groups and, notably, Drupal throws an exception if a library declares a positive weight on a JS file — the message tells you to declare accurate dependencies instead. Ordering scripts is a dependency problem, not a weight problem.

Getting this right once

The payoff for categorising properly is not aesthetic. It is that six months later, when a rule is not applying, you can reason about why in thirty seconds instead of adding an !important and hoping.

If you are building or inheriting a theme and the library file is already a mess, untangling it is a contained piece of work with a clear finish line. That is Drupal theming; if it comes with a broader front-end rebuild, the Drupal developer side of it covers the preprocess and template work that usually comes with it.

Drupal Theme

Footer menu

  • About
  • Privacy Policy
  • Terms & Conditions
  • Flash Web Center, LLC (opens in new tab)
  • Drupal Care (opens in new tab)
  • Log in
  • Contact

Copyright © 2026 Flash Web Center, LLC | All rights reserved

Developed & Designed by Alaa Haddad