How to use Discourse core variables in your theme

themes

(Joe) #1

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:

$primary
$secondary
$tertiary
$quaternary
$header_background
$header_primary
$highlight
$danger
$success
$love

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 pretty much covers pre-injected variables. We can now move on to something a little bit more advanced.


Importable Variables

Take a look at this file in the repo:

variables.scss

This is where most of the variables are defined. However, using any of these variables will lead to an error because you’d be using an undefined variable.

This happens because these variables are not pre-injected into the them editor. They have to be imported. The fix is very simple. Add this line at the top of your theme:

@import "common/foundation/variables";

And that should be enough to import all those goodies and make them ready for use.

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:

@import "common/foundation/variables";

.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.

So:

@import "common/foundation/variables";

.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:

@import "common/foundation/variables";

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

Put a box below the header:

@import "common/foundation/variables";

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


Box-shadows:
$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:

@import "common/foundation/variables";

.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
$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
$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
$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
$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
$quaternary-low: dark-light-diff($quaternary, $secondary, 70%, -70%);

//highlight
$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
$danger-low: dark-light-diff($danger, $secondary, 50%, -40%);
$danger-medium: dark-light-diff($danger, $secondary, 30%, -60%);

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

//love
$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
@import "common/foundation/variables";

.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:


Functions and Variables
Global theme settings
How do you set alternating colors in the list of topics
Changing group flair to match current theme
When a Wiki Post is edited then have problems maintaining the correct width on mobile devices
Discourse Vincent theme
Beginner's guide to using Discourse themes
Developer’s guide to Discourse Themes
How to change category h3 font size?
(Kane York) #2

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


(Joe) #3

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

are:

$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";


(Joe) #4

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:


(Angus McLeod) #5

Great post :slight_smile:

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


#6

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.


Feature proposal: Import user-defined SCSS files
(Joe) #7

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.


#8

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


#9

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