Sage and Color Schemes ("light" and "dark" version, for example)

Hey,
I was inspired by @WebDragon’s post over here to actually sit down and try to figure out how to do selectable color-schemes in Sage9.

I have a client that has a small network of sites (non-multisite) with largely the same theme, and different colors. Up until now I’ve been manually merging updates across each of the sites as changes are made. This will let me keep a single copy of the theme and pull it in as a dependency, then change the “scheme” per site. Pretty cool.

Here’s what I came up with:

First we alter config.json to generate more stylesheets for us:
theme-name/resources/assets/config.json

{
  "entry": {
    "main": [
      "./scripts/main.js",
      "./styles/main.scss"
    ],
    "light": [
      "./styles/light.scss"
    ],
    "dark": [
      "./styles/dark.scss"
    ],
    "customizer": [
      "./scripts/customizer.js"
    ]
  },
  "publicPath": "/app/themes/theme-name",
  "devUrl": "http://mysite.dev",
  "proxyUrl": "http://localhost:3000",
  "cacheBusting": "[name]_[hash:8]",
  "watch": [
    "app/**/*.php",
    "config/**/*.php",
    "resources/controllers/**/*.php",
    "resources/views/**/*.php"
  ]
}

Each of these .scss files should be fully stand-alone, including all the styles necessary for the site. An example:
theme-name/resources/assets/styles/light.scss

@import "~bootstrap/scss/functions";
@import "common/variables-light";
@import "~bootstrap/scss/variables";

/** Import everything from autoload */
@import "./autoload/**/*";

/**
 * Import npm dependencies
 *
 * Prefix your imports with `~` to grab from node_modules/
 * @see https://github.com/webpack-contrib/sass-loader#imports
 */
// @import "~some-node-module";

/** Import theme styles */
@import "common/global";
@import "components/buttons";
@import "components/comments";
@import "components/forms";
@import "components/wp-classes";
@import "layouts/header";
@import "layouts/sidebar";
@import "layouts/footer";
@import "layouts/pages";
@import "layouts/posts";
@import "layouts/tinymce";

Then let’s register a Customizer setting to select the sub-theme:
theme-name/app/customizer.php

<?php

namespace App;

add_action( 'customize_register', function() {

  global $wp_customize;

     // Theme variation setting

     		class Select_Control extends \WP_Customize_Control {
     			public $type = 'select';

     			public function render_content() {
            $themes = array(
              'light' => 'Light',
              'dark' => 'Dark',
            );
          ?>
     			<label>
     				<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
     				<select <?php $this->link(); ?>>
              <?php foreach($themes as $theme => $name) { ?>
                <option value="<?=$theme?>" <?php if($this->value() == $theme) echo 'selected="selected"'; ?>><?=$name?></option>
              <?php } ?>
     				</select>
     			</label>
     		<?php
     			} //render_content()
     		} //Select_Control()

     		$wp_customize->add_section('theme_colors', array('title'=>'Sub Theme'));

     		$wp_customize->add_setting('subtheme_setting', array('default'=>'light'));

     		$wp_customize->add_control( new Select_Control($wp_customize, 'subtheme_setting', array(
     			'label' => 'Color scheme',
     			'section' => 'theme_colors',
     			'setting' => 'subtheme_setting'
     		) ) );
});

Don’t forget to add customizer to the includes array in functions.php

Finally let’s edit setup.php to include our selected sub-theme’s stylesheet rather than the default:
theme-name/app/setup.php

/**
 * Theme assets
 */
add_action('wp_enqueue_scripts', function () {
    wp_enqueue_style('sage/main.css', asset_path('styles/' . get_theme_mod( 'subtheme_setting' )  . '.css'), false, null);
    wp_enqueue_script('sage/main.js', asset_path('scripts/main.js'), ['jquery'], null, true);
}, 100);

Now just go to Customizer and select the sub-theme you want to use!

I hope some of you find this helpful.

15 Likes

I also found a way to conditionally load controllers, so I can change things like the logo, site name, and other controller-like things. Check it out:

theme-name/app/controllers/App.php

<?php

namespace App;

use Sober\Controller\Controller;

class App extends Controller {

  /**
   * Scheme specific traits
   */

   use Light;
   use Dark;

/**
* Global controller stuff goes here
*/

}

theme-name/app/controllers/partials/light.php

<?php

namespace App;

use Sober\Controller\Controller;

if(get_theme_mod( 'subtheme_setting') == 'light' ) {
  trait Light {

    /**
     * Logo
     * @return Logo URL
     */
    public function logo() {
      return asset_path('images/logos/logo-light.png');
    }

  }

} else {
  trait Light { }
}
5 Likes

Interesting use of Traits here! Killer post.

Hi, good post. I was wondering about multiple colour schemes on one site. Currently I have an ACF field output a css class into one of my site’s main containers and from there I use that class as a parent selector to colour code specific elements on the page.

<main class="main {{ get_field('page_colour_coding') }}">
 <element></element>
</main>
.red {
  element {
    background: red;
  }
}

.green {
  element {
    background: green;
  }
}

This feels quite hacky though considering what Sage 9 provides out of the box. Is there a more sober (pun intended) way of doing this?

Could just use one trait I suppose. And swap images the same way @MWDelaney swapped stylesheets.

namespace App\Traits;

trait ColorScheme {

    /**
     * Logo
     * @return Logo URL
     */
    public function logo() {
      $scheme = get_theme_mod( 'subtheme_setting');
      return asset_path("images/logos/logo-{$scheme}.png");
    }
}

This is a good method if all you’re doing is swapping a logo or stylesheet. I wanted to create enough flexibility for the future if I needed to change something more fundamental via controller, and not have to wrap the guts of each function with the same logic.

1 Like

This is awesome but won’t work with “yarn run start”. The light and dark stylesheets aren’t loading (no styling at all). Is there a fix for this?

1 Like

I followed your instructions as specified but when I switch options from light to dark. no changes occur. Could you help me what might be the problem?

None of the my styles i.e. light or dark is running after selecting the option.

I have implemented “light” and “dark” theme. and it’s working perfectly fine except the CSS of “light” theme (which is the default theme) doesn’t load automatically until I re-select the theme from “customizer” and then it loads the CSS.

How to I make the CSS load automatically from the box?

Because css gets injected through webpack (and no actual css file is loaded), you see webpack creating .js files for every css file, for example dark.js. To address this I added the following to setup.php:

if (env('WP_ENV') !== 'production') {
    wp_enqueue_script('sage/subtheme.js', asset_path('scripts/' . $subtheme . '.js'), ['sage/main.js'], null, true);
}

It’s only needed in development. Don’t know if this is the cleanest way but it works.

3 Likes

Thanks so much for sharing this, it works great!

Would anyone be able to provide me a clue on how to switch the default values for options dependent on the selected light/dark value?

For example, I have a setting for the primary color. I’d like the default to be something like #333333 if the dark option is selected and #FFFFFF if the light option is selected and to refresh each time.

I tried to see if there was a way to add the values via the array, but it’s not working as I hoped.

    $wp_customize->add_setting(
     'text_color',
     $anArray = array(
    'sanitize_callback' => 'sanitize_hex_color',
    'transport'  => 'postMessage'
  ))
    if(get_theme_mod( 'subtheme_setting') == 'light' ) {
        $anArray['default'] = '#333333';
    }
   );

@MWDelaney-- thanks for this!

I’m having trouble. When I switch the sub theme in the customizer, all the new styles load just fine within the customizer previewer but when I load up the local dev url, the stylesheet doesn’t appear to be adding the subtheme_setting:

/app/themes/project/dist/styles/.css

You might need to run yarn build if you haven’t done that already.

Hi thanks. Yes, I’ve done that and it only applies the changes using the customizer preview.