Following on from the last Feature Focus post, "Look and feel customisation - Site theme settings", we now have a look at the "Asset Injector" as a way of customising the look and feel of Council Platform sites.
The "Asset Injector" is functionality with a site's admin system that allows users with the "Site Administrator" role to add CSS style rules and JavaScript snippets to the site. The application of these rules can be controlled by a variety of options, such as the relative path of the page being viewed, or content type. By default, rules are applied site wide.
As a warning, the Asset Injector requires a basic level of website development knowledge, in particular CSS. We've found this can be picked up quite quickly by following online tutorials.
The Asset Injector can be reached by browsing to "Configuration -> Development -> Asset Injector" in the admin menu:

The CSS Injector is used much more frequently than the JS Injector. Typically, we only see the JS Injector being used by clients to integrate third party systems such as analytics platforms or chatbots with sites. It's best to think of the CSS Injector as away to adjust visual appearance and the JS Injector as a way to modify site behaviour.
Visiting the CSS Injector page shows a list of all CSS Injector rules for a site:

Let's look at the "Global variables" rules now as it segues nicely into demonstrating a powerful facet of Council Platform's theming system, CSS custom variables.
Council Platform's CSS custom variables
CSS custom variables are a way for website developers to assign a CSS value, such as a colour hex code, to a variable name, and then to reference that variable throughout the rest of their style rules. By doing this, they can change the variable's value once, and it immediately takes effect in all CSS rules. Council Platform style rules have been coded so that CSS variables are used extensively. This makes it much easier to change the global look and feel of sites.
In the last Feature Focus post, you'll recall we showed how you can change the colours of Council Platform sites using the admin system:

Looking at the CSS variables in Council Platform's base theme, we see there are lots of them:
:root {
--govuk-colour-red: #d4351c;
--govuk-colour-yellow: #ffdd00;
--govuk-colour-green: #00703c;
--govuk-colour-blue: #1d70b8;
--govuk-colour-dark-blue: #003078;
--govuk-colour-light-blue: #5694ca;
--govuk-colour-purple: #4c2c92;
--govuk-colour-black: #0b0c0c;
--govuk-colour-dark-grey: #626a6e;
--govuk-colour-mid-grey: #b1b4b6;
--govuk-colour-light-mid-grey: #e4e4e4;
--govuk-colour-light-grey: #f3f2f1;
--govuk-colour-white: #ffffff;
--govuk-colour-light-purple: #6f72af;
--govuk-colour-bright-purple: #912b88;
--govuk-colour-pink: #d53880;
--govuk-colour-light-pink: #f499be;
--govuk-colour-orange: #f47738;
--govuk-colour-brown: #b58840;
--govuk-colour-light-green: #85994b;
--govuk-colour-turquoise: #28a197;
--govuk-body-background-colour: var(--govuk-colour-white);
--govuk-border-colour: var(--govuk-colour-mid-grey);
--govuk-brand-colour: var(--govuk-colour-blue);
--govuk-brand-secondary-colour: var(--govuk-colour-dark-blue);
--govuk-brand-tertiary-colour: #37accf;
--govuk-brand-pop-colour: #ffdd00;
--govuk-canvas-background-colour: var(--govuk-colour-light-grey);
--govuk-error-colour: var(--govuk-colour-red);
--govuk-focus-width: 3px;
--govuk-input-border-colour: var(--govuk-colour-black);
--govuk-print-text-colour: #000000;
--govuk-secondary-text-colour: var(--govuk-colour-dark-grey);
--govuk-text-colour: var(--govuk-colour-black);
--govuk-header-link-active: #1d8feb;
--govuk-header-border-width: 10px;
--govuk-button-colour: var(--govuk-colour-green);
--govuk-button-hover-colour: #005a30;
--govuk-button-text-colour: var(--govuk-colour-white);
--govuk-link-colour: var(--govuk-colour-blue);
--govuk-link-hover-colour: var(--govuk-colour-dark-blue);
--govuk-link-visited-colour: var(--govuk-colour-purple);
--govuk-link-active-colour: var(--govuk-colour-black);
--govuk-focus-colour: var(--govuk-colour-yellow);
--govuk-focus-text-colour: var(--govuk-colour-black);
--govuk-hover-colour: var(--govuk-colour-mid-grey);
--govuk-accordion-link-colour: var(--govuk-colour-blue);
--govuk-tag-colour: var(--govuk-colour-white);
--govuk-tag-background-colour: var(--govuk-brand-colour);
--cp-border-radius: 0;
--cp-border-radius--button: var(--cp-border-radius);
--cp-border-radius--featured-content: var(--cp-border-radius);
--cp-border-radius--group: var(--cp-border-radius);
--cp-border-radius--image: var(--cp-border-radius);
--cp-border-radius--header: var(--cp-border-radius);
--cp-brand-colour-dark-10: #1a65a6;
--cp-focus-background-colour: var(--govuk-focus-colour);
--cp-focus-box-shadow: 0 -2px var(--govuk-focus-colour),0 4px var(--govuk-focus-text-colour);
--cp-focus-box-shadow__input: 0 0 0 5px var(--govuk-focus-text-colour);
--cp-focus-colour__input: var(--govuk-focus-colour);
--cp-focus-outline__input: var(--govuk-focus-width) solid var(--cp-focus-colour__input);
--cp-focus-outline-offset: 0;
--cp-footer-canvas-background-colour: var(--govuk-canvas-background-colour);
--cp-header-background-light-5: #171919;
--cp-header-background: var(--govuk-colour-black);
--cp-header-background__open: var(--cp-header-background);
--cp-header-background__dropdown: var(--cp-header-background);
--cp-header-canvas-background-colour: var(--govuk-canvas-background-colour);
--cp-header-filter: none;
--cp-header-focus-background-colour: var(--govuk-focus-colour);
--cp-header-focus-colour: var(--govuk-focus-text-colour);
--cp-header-text--open: var(--govuk-brand-colour);
--cp-header-text: var(--govuk-colour-white);
--cp-header--transparent-logo-filter: brightness(0) invert(1);
--cp-header--transparent-focus-colour: var(--govuk-focus-text-colour);
--cp-header--transparent-link-colour: var(--govuk-colour-white);
--cp-header--transparent-link-background-colour: var(--govuk-colour-white);
--cp-inverted-text-colour: var(--govuk-colour-white);
--cp-social-media-icon-colour: var(--govuk-brand-colour);
--cookie-banner-background: var(--govuk-colour-black);
--cookie-banner-color: var(--govuk-colour-white);
--cookie-banner-link-color: var(--cookie-banner-color);
--feedback-form-background: var(--govuk-brand-colour);
--feedback-focus-color: var(--govuk-colour-black);
--feedback-text-color: var(--cp-inverted-text-colour);
--gutenberg-block-bottom-margin--mobile: 15px;
--gutenberg-block-bottom-margin: 20px;
--gutenberg-block-bottom-margin__button: var(--gutenberg-block-bottom-margin);
--gutenberg-block-bottom-margin__buttons: 0;
--gutenberg-block-bottom-margin__caption: var(--gutenberg-block-bottom-margin);
--gutenberg-block-bottom-margin__column: var(--gutenberg-block-bottom-margin);
--gutenberg-block-bottom-margin__columns: 0;
--gutenberg-block-bottom-margin__content-embed: 0;
--gutenberg-block-bottom-margin__cover: var(--gutenberg-block-bottom-margin);
--gutenberg-block-bottom-margin__gallery: var(--gutenberg-block-bottom-margin);
--gutenberg-block-bottom-margin__group: var(--gutenberg-block-bottom-margin);
--gutenberg-block-bottom-margin__image: var(--gutenberg-block-bottom-margin);
--gutenberg-block-bottom-margin__leaflet: var(--gutenberg-block-bottom-margin);
--gutenberg-block-bottom-margin__list: var(--gutenberg-block-bottom-margin);
--gutenberg-block-bottom-margin__pullquote: var(--gutenberg-block-bottom-margin);
--gutenberg-block-bottom-margin__table: var(--gutenberg-block-bottom-margin);
--gutenberg-block-bottom-margin__toc: var(--gutenberg-block-bottom-margin);
--gutenberg-block-padding__column: 0;
--gutenberg-block-padding__cover: 1em;
--gutenberg-block-padding__group: 20px 20px 0 20px;
--gutenberg-block-width__separator: 100px;
--cp-breakpoint-mobile: 320px;
--cp-breakpoint-mobile-lg: 480px;
--cp-breakpoint-tablet: 641px;
--cp-breakpoint-desktop: 769px;
--cp-breakpoint-mega-menu-toggle: 769px;
--cp-breakpoint-responsive-menu-toggle: 769px;
--cp-breakpoint-desktop-md: 900px;
--cp-breakpoint-fullcalendar-list: 900px;
--cp-breakpoint-desktop-lg: 1200px;
--cp-timeline-border-colour: var(--govuk-brand-colour);
--action-link-arrow-colour: var(--govuk-colour-green);
--site-alert-background__success: #c6ece9;
--site-alert-background__info: #dbeff9;
--site-alert-background__warning: #f7e7d3;
--site-alert-background__danger: var(--govuk-colour-light-grey)
}
Hopefully the names of these are fairly intuitive. "govuk" is the prefix for the govuk design system variables, "cp" for Council Platform and "gutenberg" is the WYSIWYG content editor library used by the Platform.
When performing initial customisation of sites, we mainly find ourselves changing:
- Brand and component colour variables.
- Component spacing using "--gutenberg-block-bottom-margin".
- Border radius using "--cp-border-radius".
Changing these along with changing a site's font can make big inroads into customisation.
Circling round to the Asset Injector these variables can be changed at will by using a new Asset Injector rule:

And the resulting change on the frontend is:

BY default CSS Asset Injector rules apply globally, but say we only wanted to change it for a specific page, we can add a "condition" to the CSS Asset Injector rule so it only applies in that circumstance:

That rule will now only apply to the page with the URL "/page-with-red-social-media-icons". This condition also shows up in the list of CSS Asset Injector rules to help you manage them:

More advanced CSS Asset Injector rules
The CSS Asset Injector supports any valid CSS code, as such there's no real limit to how much customisation you want to make with it. It even provides helpful validation tooltips!

In the above, you can see that we're using a responsive breakpoint to target styling for specific viewport widths. We're also using pseudo selectors and CSS custom variables, so that rule is relatively advanced.
With the CSS Asset Injector, you're layering styles on top of the default ones in the Platform's theme. In the next Feature Focus article, we'll go through how you change these "at source".