How to use Discourse core variables in your theme

Discourse is incredibly customizable!

The goal of this topic is to show you how make use of all the amazing variables that are available to you as a theme developer.

You know… so that you don’t have to reinvent the wheel :smile:

Variables? What variables?

Variables cover a large number of things, from font-size to colors and even z-index values. There are two types of variables, ones that are already injected into the theme editor, and ones that we have to import.

Pre-injected variables:

As far as I can tell, the only set of pre-injected variables are the basic color-scheme colors. If you’re not sure what that is, here’s what I mean:

These injected variables can be used by calling them like so:


Because these variables are pre-injected, you can use them right away without any other requirements. This is all I’m going to say about them because I don’t understand the voodoo involved that makes this possible :sweat_smile:

So how can I use them?

Create a new theme

Notice that the current color scheme is set to “Light scheme” (same color scheme in the screenshot above)

Let’s say I want to set the <body> background to match the $highlight color variable of the current color scheme. Well, I can do that like so:

body { 
    background: $highlight;

And the color would be pulled automatically from the current color scheme, like so:

(This background color is not recommended :sweat_smile:)

Now, let’s say the current color scheme is not set to “Light scheme” for the the current theme. Instead, let’s say it’s set to the default “Simple Dark” which looks like this:

The same code would yield a different result because the variable is different.

You can see how the <body> background follows the current $highlight variable whatever it may be for the current theme.

That covers the basic color variables. We can now move on to something a little bit more advanced.

Advanced Variables

Take a look at this file in the repo:


This is where most of the variables are defined. Just like the basic colors, these variables are magically available for you to use in theme stylesheets.

I will try to break down the content of the file below:

Width variables:
$small-width: 800px !default;
$medium-width: 995px !default;
$large-width: 1110px !default;

these are very handy if you want your theme to match native Discourse behavior, especially in media queries for example take a look at the relevant parts of the pre-compiled scss for .user-info:

.user-info {
  &.medium {
    flex: 0 0 32%;
    margin: 0 2% 4vh 0;
    @media screen and (max-width: $small-width) {
      flex: 0 0 48%;
      margin-right: 0;

Notice how the media query is fed a variable to determine a cut-off point for small screens and apply different styles.

Brand color variables
$google: #5b76f7 !default;
$instagram: #125688 !default;
$facebook: #3b5998 !default;
$cas: #70BA61 !default;
$twitter: #00bced !default;
$yahoo: #810293 !default;
$github: #6d6d6d !default;

These are pretty straightforward, you use those variables for brand-colors just like we did with color scheme variables.

For example if I want to make all the buttons in Discourse use the Google brand color I could use this:

.btn {
    background: $google;

And the result would be:

This is obviously not recommended :grin:

Moving on:

Font sizes
$base-font-size: 14px !default;
$base-font-family: Helvetica, Arial, sans-serif !default;

// Font-size defintions, multiplier ^ (step / interval) 
$font-up-6: 2.296em;
$font-up-5: 2em;
$font-up-4: 1.7511em;
$font-up-3: 1.5157em;
$font-up-2: 1.3195em;
$font-up-1: 1.1487em; // 2^(1/5) 
$font-0: 1em;
$font-down-1: .8706em; // 2^(-1/5)
$font-down-2: .7579em; // Smallest size we use based on the 1em base
$font-down-3: .6599em;
$font-down-4: .5745em;
$font-down-5: .5em;
$font-down-6: .4355em;

This is also pretty straightforward, Discourse has an awesome font-scaling system and so you should make use of it.

The units used are in em and so would be relative to $base-font-size which is used on the <html> element.


.btn {
    font-size: $font-up-6;

gets compiled into:

($base-font-size * $font-up-6) or (14px * 2.296) = 32.14px

Or this:

Line heights:
$line-height-small: 1;
$line-height-medium: 1.2; // Headings or large text
$line-height-large: 1.4; // Normal or small text

These variables can be used just like the previous example. Do note that they are unitless values. Here’s what that means:

z-index values:
$z-layers: (
  "max":              9999, 
  "fullscreen":       1700,
  "modal": (
    "tooltip":        1600,   
    "popover":        1500,
    "dropdown":       1400,
    "content":        1300,
    "overlay":        1200,
  "mobile-composer":  1100,
  "header":           1000,
  "tooltip":          600,
  "composer": (
      "popover":      500,
      "content":      400,
  "dropdown":         300,  
  "usercard":         200,
  "timeline":         100,
  "base":             1

These can be used as discussed here:

div {
    z-index: z("header");

“or when nested, like this:”

div {
    z-index: z("modal", "dropdown");

“You can also do basic math as you’d expect:”

div {
    z-index: z("base") + 1;

The code-blocks above are part of the quote but I moved them out for formatting

Now here’s an example:

Put a box above the header:

.zbox {
    position: fixed;
    top: 0;
    left: 0;
    height: 200px;
    width: 200px;
    background: red;
    z-index: z("header") + 1;

Put a box below the header:

.zbox {
    position: fixed;
    top: 0;
    left: 0;
    height: 200px;
    width: 200px;
    background: red;
    z-index: z("header") - 1;

$box-shadow: (
  "modal":        0 8px 60px rgba(0, 0, 0, 0.6),
  "composer":     0 -1px 40px rgba(0, 0, 0, 0.12),
  "menu-panel":   0 6px 14px rgba(0, 0, 0, 0.15),
  "card":         0 4px 14px rgba(0, 0, 0, 0.15),
  "dropdown":     0 2px 3px 0 rgba(0, 0, 0, 0.2),
  "header":       0 2px 4px -1px rgba(0, 0, 0, 0.25),
  "kbd":         (0 2px 0 rgba(0, 0, 0, 0.2), 0 0 0 1px dark-light-choose(#fff, #000) inset),
  "focus":        0 0 6px 0 $tertiary,
  "focus-danger": 0 0 6px 0 $danger

As you can sort of see, most of the work has been done for you when it comes to variables, and you only need to utilize what’s already there in most cases.

The use for box shadows looks like this:

.d-header {
    box-shadow: shadow("focus");

This should make the header use the focus box-shadow. And if we check to confirm:

Color transformation:
$primary-very-low: dark-light-diff($primary, $secondary, 97%, -80%);
$primary-low: dark-light-diff($primary, $secondary, 90%, -65%);
$primary-low-mid: dark-light-diff($primary, $secondary, 70%, -45%);
$primary-medium: dark-light-diff($primary, $secondary, 50%, -35%);
$primary-high: dark-light-diff($primary, $secondary, 30%, -10%);

$header_primary-low: dark-light-diff($header_primary, $secondary, 90%, -65%);
$header_primary-medium: dark-light-diff($header_primary, $secondary, 50%, -20%);
$header_primary-high: dark-light-diff($header_primary, $secondary, 20%, 20%);

$secondary-low: dark-light-diff($secondary, $primary, 70%, -70%);
$secondary-medium: dark-light-diff($secondary, $primary, 50%, -50%);
$secondary-high: dark-light-diff($secondary, $primary, 30%, -35%);

$tertiary-low: dark-light-diff($tertiary, $secondary, 85%, -65%);
$tertiary-medium: dark-light-diff($tertiary, $secondary, 50%, -45%);
$tertiary-high: dark-light-diff($tertiary, $secondary, 20%, -25%);

$quaternary-low: dark-light-diff($quaternary, $secondary, 70%, -70%);

$highlight-low: dark-light-diff($highlight, $secondary, 70%, -80%);
$highlight-medium: dark-light-diff($highlight, $secondary, 50%, -55%);
$highlight-high: dark-light-diff($highlight, $secondary, -50%, -10%);

$danger-low: dark-light-diff($danger, $secondary, 50%, -40%);
$danger-medium: dark-light-diff($danger, $secondary, 30%, -60%);

$success-low: dark-light-diff($success, $secondary, 80%, -60%);
$success-medium: dark-light-diff($success, $secondary, 50%, -40%);

$love-low: dark-light-diff($love, $secondary, 85%, -60%);

Using a standardized group of colors not only makes your theme visually pleasant, it also makes it maintainable.
Instead of using hard-coded colors, or the same color-variable everywhere, use these color transformations

The use for these looks like this:

Expand code-block
.zbox {
	height: 200px;
	width: 200px;

#box-0 {
	background: $primary;

#box-1 {
	background: $primary-very-low;

#box-2 {
	background: $primary-low;

#box-3 {
	background: $primary-low-mid;

#box-4 {
	background: $primary-medium;

#box-5 {
	background: $primary-high;

#box--1 {
	background: $header_primary;

#box--1 {
	background: $header_primary-low;

#box--2 {
	background: $header_primary-medium;

#box--3 {
	background: $header_primary-high;

#box---1 {
	background: $secondary;

#box---1 {
	background: $secondary-low;

#box---2 {
	background: $secondary-medium;

#box---3 {
	background: $secondary-high;

#box----0 {
	background: $tertiary;

#box----1 {
	background: $tertiary-low;

#box----2 {
	background: $tertiary-medium;

#box----3 {
	background: $tertiary-high;

#box-----0 {
	background: $quaternary;

#box-----1 {
	background: $quaternary-low;

#box-----0 {
	background: $highlight;

#box-----1 {
	background: $highlight-low;

#box-----2 {
	background: $highlight-medium;

#box-----3 {
	background: $highlight-high;

#box------0 {
	background: $danger;

#box------1 {
	background: $danger-low;

#box------2 {
	background: $danger-medium;

#box-------0 {
	background: $success;

#box-------1 {
	background: $success-low;

#box-------2 {
	background: $success-medium;

#box--------0 {
	background: $love;

#box--------1 {
	background: $love-low;

To get something like this:

All these color pre-calculated variations are readily available for use, you just have to import the variables.scss file and you’re good to go.

And because the source of these is the color scheme variables, they will reflect the current color scheme as we discussed before, here’s the same code with the dark color scheme:

This post is a wiki so edit as needed :wink:


Doesn’t it also work if you @import "theme_variables"; or something like that?

I have found that the variables work in the Discourse theme editor even if you don’t import the style sheet.

In @Sam’s words and I paraphrase:

They’re there magically

I’m not sure if they would work in a plugin stylesheet without importing the color variable sheet or not.
I’m also not sure what the contents of theme_colors are but I found that the contents of

//stylesheets/app/assets/stylesheets/common/foundation/colors - repo link


$primary:           #222222 !default;
$secondary:         #ffffff !default;
$tertiary:          #0088cc !default;
$quaternary:        #e45735 !default;
$header_background: #ffffff !default;
$header_primary:    #333333 !default;
$highlight:         #ffff4d !default;
$danger:            #e45735 !default;
$success:           #009900 !default;
$love:              #fa6c8d !default;

I would love to be able to also import some of the other default functions like:

color: dark-light-choose($primary, $primary-low-mid);

but I have not been able to do so yet.

There are also other files that are magically added that I would like to get my hands on like here:

// These files don't actually exist. They're injected by Stylesheet::Compiler. 
// --------------------------------------------------

@import "theme_variables";
@import "plugins_variables";
@import "common/foundation/math";


So, I finally had enough time to update this and add a few things I’ve learned since I first created it, give it another read and let me know if you have any questions. :grinning:


Great post :slight_smile:

Yup, they all work in a plugin stylesheet without an import, including functions.


this is excellent. I noticed I can import variables.scss to the Common CSS section of my theme and it will be accessible in the Desktop CSS and Mobile CSS as well.

Unfortunately, defining my own variables and mixins in Common CSS doesn’t carry over to Desktop and Mobile.

1 Like

Are you referring to the error that shows up on top of the editor? If so, you can safely ignore that for now. Check if the styles are actually being applied, which they should, regardless of the error.


Yes, The error is still there, but the styles are in fact applied. Hope this will be addressed.
Thanks so much @Johani.


As of Discourse 2.2, the error is no longer safe to ignore. A stylesheet with an error will not load at all.


In the latest version of Discourse (anything after this commit), the variables.scss file is imported automatically for theme stylesheets. That makes it even easier to use all the handy variables for colors, fonts and more! (cc @Johani @awesomerobot)

I’ve updated the OP here to reflect the changes.

Boring backwards compatibility information

For compatibility with the stable branch, you still need to include the old import line.

@import "common/foundation/variables";

Importing the variables twice won’t cause any issues, so it will continue working in the latest version of Discourse as well


Trying to adjust the theme via git. But I guess I missunderstood something.

I try changing the variables adding following to the common/common.scss to remove all shadows in the theme.

// Box-shadow
// -------------------------------------------------

@import "common/foundation/variables"

$box-shadow: (
  "modal": none,
  "composer": none,
  "menu-panel": none,
  "card": none,
  "dropdown": none,
  "header": none,
  "kbd": (
  "focus": none,
  "focus-danger": none

What is it I am doing wrong?

You can’t override our variables (by the time a theme accesses them they’re already compiled), you can only use them as they are or override styles individually.

So for example, if you want to remove the box-shadow from the header in a theme you’d have to do:

.d-header {
  box-shadow: none;

or, if you wanted to change the box-shadow using an existing value in the system you could do this

.d-header {
  box-shadow: shadow("modal");

Is there a way to specify hyperlink colours?

Disregard, found the solution here

1 Like