Overriding or Providing Core JavaScript or CSS in a Theme

Now that you know how to require core assets in your theme, how do override them? There are a couple ways to do this – and its advised that only experienced Concrete5 developers undertake these. Make absolutely sure you know what you're doing, since Concrete5 requires these core libraries for a reason. Using a different version of a core library than the one that shipped with your version of Concrete5 could have strange consequences.

Overriding Core JavaScript and CSS Libraries

Overrides via application Directory

Concrete5 has long provided a way to override CSS and JavaScript through the usage of a root level directory. In 5.7, this directory has moved into application, but the principal is the same.

  1. Locate the file you wish to override (in this case, concrete/js/jquery.js)
  2. Disable Overrides Caching via Dashboard > System and Settings > Optimization > Cache & Speed Settings
  3. Place the new jquery.js in application/js/jquery.js.

That's all you need to do! The application-level jquery.js will be used.

Overrides via Asset System

If you're working with a package, the above method may not be available to you. Fortunately, the assets system allows for this type of asset reassignment (although again – be careful when utilizing this or when working with add-ons that do so.) Within your package controller, a simple on_start() method can reassign a core asset.

public function on_start()
{
    $al = \AssetList::getInstance();
   $al->register('javascript', 'jquery', '//ajax.googleapis.com/ajax/libs/jquery/2.0/jquery.min.js', array('local' => false, 'version' => '2.0'));
}

Overriding Core Block JavaScript and CSS Libraries

Does a core block provide an asset in its block directory (like view.js, view.css) that you wish to override at the application level? It's easy to do so:

  1. Locate the file you wish to override (in this case, core/blocks/date_navigation/view.css)
  2. Disable Overrides Caching via Dashboard > System and Settings > Optimization > Cache & Speed Settings
  3. Place the new view.css at application/blocks/date_navigation/view.css.

Registering The Assets that Your Theme Provides

Core Libraries

What if you don't want to override JavaScript or CSS, but instead you want to let Concrete5 know that the currently active theme provides certain CSS and JavaScript, so that Concrete5 doesn't load it? This isn't the same as using requireAsset, because presumably your theme is included this CSS or JavaScript in the HTML tag. These aren't assets. For example, take the Urbanic theme, which is built on Bootstrap. Concrete5 is also built on Bootstrap, and has registered multiple assets in the bootstrap namespace. These include:

Bootstrap JavaScript

  • bootstrap/dropdown
  • bootstrap/tooltip
  • bootstrap/popover
  • bootstrap/alert
  • bootstrap/button
  • bootstrap/transition

Bootstrap CSS

  • bootstrap/dropdown
  • bootstrap/tooltip
  • bootstrap/popover
  • bootstrap/alert
  • bootstrap/button
  • bootstrap/transition
  • bootstrap (this is generic bootstrap – the entire CSS library)

Since Urbanic is built on Bootstrap, we know that it's bootstrap.min.js file and bootstrap.css files effectively include all of the above assets. So we don't want Concrete5 to load its bootstrap assets – we can rely on Urbanic, since it's already doing it. We need to mark these assets as provided by the theme – so whenever the theme is rendered, Concrete5 won't load its own assets.

This is easy to do from within the PageTheme class. Instead of using Theme::requireAsset, use Theme::providesAsset. Here's our registerAssets function with the additions, as well as the previous requireAsset calls.

public function registerAssets()
{
    $this->requireAsset('css', 'font-awesome');
    $this->requireAsset('javascript', 'jquery');
    $this->providesAsset('javascript', 'bootstrap/*');
    $this->providesAsset('css', 'bootstrap/*');
}

Notice, we don't have to manually write out every full asset handle that the theme provides – we can use wildcards in our providesAsset call. This is helpful when a theme includes an entire library.

That's all you need to do. Now Concrete5 won't include its own copies of Bootstrap CSS or Bootstrap JavaScript, because the theme is taking care of it.

Block Assets

Concrete5 also makes it easy to mark certain block assets as provided by your theme. You can see this in particular with the Elemental theme that ships with Concrete5. In 5.7, we ship bare-bones CSS and JavaScript with the blocks that need them. For example, the Image Slider block includes a view.css file and a view.js. These are automatically added to the page header and footer, so that it will function on any site that it's added to.

Many blocks provided assets in this fashion. However, the Elemental theme also provides specific CSS for a number of these blocks, so that they'll a) look really nice in Elemental, and b) take full advantage of the Elemental customizable stylesheets. For example, the Concrete5 form block is marked up in standard Bootstrap 3 form styling, and since Elemental is built on Bootstrap 3, we know that the form block will look good in it; there's no need to include the form block's view.css. The Topic List and Date Navigation blocks have specific CSS support in the Elemental theme, so they don't need their view.css either. What about custom templates? The "Hover Description" custom template for the Feature block is mostly standard Bootstrap – so while it needs its view.js, it doesn't need view.css when rendered in Elemental.

Fortunately, the Assets system and providesAsset is helpful in these cases as well. To add support for a particular core block's autoloaded asset, just add the following to your registerAssets method:

$this->providesAsset('css', 'blocks/form');

If we wanted to add support for all the above blocks to our existing registerAssets method, it would end up looking like this:

public function registerAssets()
{
    $this->requireAsset('css', 'font-awesome');
    $this->requireAsset('javascript', 'jquery');
    $this->providesAsset('javascript', 'bootstrap/*');
    $this->providesAsset('css', 'bootstrap/*');
    $this->providesAsset('css', 'blocks/form');
    $this->providesAsset('css', 'blocks/date_navigation');
    $this->providesAsset('css', 'blocks/topic_list');
    $this->providesAsset('css', 'blocks/feature/templates/hover_description');
}

Miscellaneous Core CSS

Sometimes there are important bits of CSS that are used across multiple blocks or includes, that aren't really their own CSS libraries. Examples include Concrete5's pagination code and its error handling. When form block wants to display errors, it requires the following asset in its core code:

$this->requireAsset('css', 'core/frontend/errors');

This asset maps to concrete/css/frontend/errors.css and provides some minimal error styling. However, since these errors are simply built on standard Bootstrap 3 error displaying (specifically, using the class "alert alert-danger" on a particular DIV) we know that any Bootstrap 3 theme will already provide support for frontend/errors.css. So we need to make these styles as provided by Urbanic as well. You're probably already guessed how to do this. Yep, just add another couple providesAsset lines to your registerAssets method:

$this->providesAsset('css', 'core/frontend/errors');

Since all of the frontend CSS files are based on Bootstrap 3 markup, a Bootstrap 3 theme actually provides support for all of them. So this line will work as well (and work more efficiently):

$this->providesAsset('css', 'core/frontend/*');

More About Assets

There's more to the Assets system than just what's described here, but hopefully this provides a helpful starting point toward underdstanding CSS and JavaScript in Concrete5 5.7, and lets you write themes that are simultaneously more flexible and lighter weight.

Loading Conversation