Easily scope/namespace theme and block CSS using Less and Sass to prevent CSS conflicts

This is a community-contributed tutorial. This tutorial is over a year old and may not apply to your version of Concrete CMS.
Mar 31, 2017

This tutorial is an updated legacy how-to by JohntheFish.

Themes, jQuery plugins and all sorts of widgets from outside of Concrete CMS often come with CSS that interferes with other aspects of a site or the Concrete dashboard. You can easily use a Less or Sass compiler to quickly limit the scope of such CSS. Even if you have not spotted any conflicts yet, limiting the scope of CSS is generally a good design practice that can help avoid trouble in the future. Limiting the scope of CSS is actually recommended in Concrete theme and block making guidelines.

When deciding on what CSS selector to scope your theme CSS to, an easy choice is to use .ccm-page. The .ccm-page wrapper class is conveniently part of every theme because of the required getPageWrapperClass() method.

<!-- Concrete and theme header stuff -->
<body>
    <div class="<?php echo $c->getPageWrapperClass(); ?>">
        <!-- page type layout for theme -->
    </div>
    <!-- Concrete footer stuff -->
</body>

That is just background. If you look through the documentation and tutorials there is plenty more to read about structuring themes.

Suppose you already have a mass of CSS, from a theme you are importing or from a framework you are building a theme from. It may contain hundreds or thousands of styles. You don’t really want to have to go through all those styles one-by-one and prefix them with .ccm-page. It’s a tedious and error prone task that can only become a maintenance nightmare.

The trick is to use Less or Sass to add scope. Less and Sass are extensions to CSS that, when processed by a respective compiler, output pure CSS. Take all the CSS you want to scope, wrap it in a single scope declaration and apply the Less or Sass compiler. The output will be fully scoped CSS.

There are many ways to implement the details. If the theme is already built from Less or Sass, you can usually just create another file with the scope declaration that includes all the various component files within the scope declaration. Or you could edit the outermost file to add a line to open the scope at the top and another close it at the end. You may also need to change the extension of all the files you are compiling to end in .less or .scss so the compiler will know to process them.

Do some Googling and you will soon find Less and Sass compilers that you can run from the desktop, run online from a web site or build directly into your site to compile on demand. Starting with Concrete 5.7, a Less compiler is included with Concrete and allows you to use .less files in your theme.

Let’s begin with the trivial case of a simple style sheet for the "Monster Font" theme that inconveniently globally over-sizes all paragraphs. That may be what the theme wants, but it turns out some parts of the dashboard are also suffering monster sizing.

p {
    font-size: 50px;
}

To limit the scope you can wrap it in your scope class:

.ccm-page {
    p {
        font-size: 50px;
    }
}

When you then compile the Less or Sass, the resulting CSS will be:

.ccm-page p {
    font-size: 50px;
}

The over-sized paragraph font is now constrained to only apply to paragraphs inside .ccm-page and cannot interfere with anything outside that container.

That was just a trivial example. The technique extends to wrapping many styles in one go. What you are aiming for is an outer wrapper for all the styles that looks like:

.ccm-page {
    // wrap all styles inside here
}

You aren't limited to wrapping individual styles, you can wrap entire files using @import:

Less

.ccm-page {
    @import "grid.less";
    @import "blocks.less";
    @import "typography.less";
}

Sass

.ccm-page {
    @import "grid";
    @import "blocks";
    @import "typography";
}

After compiling, all the styles in the imported files would be scoped to .ccm-page.

The same approach for scoping theme CSS is used for scoping block CSS, the only difference is the class you use for scoping. A general recommendation for naming the block scope class is to use the class prefix of "ccm-block-" followed by the block handle. For example, if your block handle was "my_block_name", then your block scope class would be .ccm-block-my-block-name. This class would then be applied to the element that wraps your block HTML, or depending on the type of block and structure, it would be applied to parent elements in your block HTML.

When you compile the Less or Sass, the output will be CSS that is all instantly scoped to your container. Styles that argue with the dashboard, with other themes, other block templates or other jQuery widgets should be a thing of the past.

Recent Tutorials
Customize locale icons
Oct 29, 2024
By myq.

How to customize locale (language region) flags

Concrete CMS Caching Guide
Oct 16, 2024

An overview of types of caching in Concrete and considerations when using them.

Redirect all requests to HTTPS
Oct 9, 2024
By myq.

How to follow best practices for a secure web

Upgrade Concrete versions 9.3.1 and 9.3.2
Sep 10, 2024
By myq.

How to get past a bug in versions 9.3.1 and 9.3.2 that prevents upgrading the Concrete core through the Dashboard

How to use Composer with Marketplace extensions
Aug 22, 2024

Composer can be used to manage third-party extensions from the marketplace

Controlling Google Tag Manager Tags Based on Concrete CMS Edit Toolbar Visibility
Aug 13, 2024

This document provides a step-by-step guide on how to control the firing of Google Tag Manager (GTM) tags based on the visibility of the Concrete CMS edit toolbar. It explains how to create a custom JavaScript variable in GTM to detect whether the edit toolbar is present on a page and how to set up a trigger that ensures GTM tags only fire when the toolbar is not visible. This setup is particularly useful for developers and marketers who want to ensure that tracking and analytics tags are not activated during content editing sessions, thereby preserving the accuracy of data collected.

Improvements?

Let us know by posting here.