Skip to Content


Render Sass

PHP March 16, 2017




(Optional) (Boolean) If checking child theme directory
Default: false

Request or provide clarification »

Additional Notes

This function is typically called automatically as needed and generally does not need to be called manually.

Was this page helpful? Yes No

    A feedback message is required to submit this form.

    Please check that you have entered a valid email address.

    Enter your email address if you would like a response.

    Thank you for your feedback!

    Source File

    Located in /libs/Utilities/Sass.php on line 174.

    4 Hooks

    Find these filters and actions in the source code below to hook into them. Use do_action() and add_filter() in your functions file or plugin.

    Need a new filter hook? Request one here.

    Need a new action hook? Request one here.

    Note: This function contains 4 to-do comments.

            public function render_scss($location_name=false, $location_paths=false, $force_all=false){
                $override = apply_filters('pre_nebula_render_scss', null, $location_name, $location_paths, $force_all);
                if ( isset($override) ){return;}
                global $sass_errors;
                if ( $this->get_option('scss') && !empty($location_name) && !empty($location_paths) ){
                    $this->timer('Sass (' . $location_name . ')', 'start', 'Sass');
                    //Require SCSSPHP
                    require_once get_template_directory() . '/inc/vendor/scssphp/'; //Run the autoloader. SCSSPHP is a compiler for SCSS 3.x
                    $this->scss = new \ScssPhp\ScssPhp\Compiler();
                    //Register import directories
                    if ( !empty($location_paths['imports']) ){
                        if ( !is_array($location_paths['imports']) ){
                            $location_paths['imports'] = array($location_paths['imports']); //Convert to an array if passes as a string
                        foreach ( $location_paths['imports'] as $imports_directory ){
                    //Set compiling options
                    $this->scss->setOutputStyle(\ScssPhp\ScssPhp\OutputStyle::COMPRESSED); //Minify CSS (while leaving "/*!" comments for WordPress).
                    //Source Maps
                    $this->scss->setSourceMap(1); //0 = No .map, 1 = Inline .map, 2 = Output .map file
                        'sourceMapBasepath' => ABSPATH, //Difference between file & URL locations, removed from all source paths in .map
                        'sourceRoot' => '/', //Added to source path locations if needed
                    $nebula_scss_variables = array(
                        'parent_partials_directory' => \ScssPhp\ScssPhp\ValueConverter::fromPhp(get_template_directory() . '/assets/scss/partials/'), //There must be a better way to call fromPhp() here...
                        'child_partials_directory' => \ScssPhp\ScssPhp\ValueConverter::fromPhp(get_stylesheet_directory() . '/assets/scss/partials/'), //There must be a better way to call fromPhp() here...
                        'template_directory' => \ScssPhp\ScssPhp\ValueConverter::fromPhp(get_template_directory_uri()), //There must be a better way to call fromPhp() here...
                        'stylesheet_directory' => \ScssPhp\ScssPhp\ValueConverter::fromPhp(get_stylesheet_directory_uri()), //There must be a better way to call fromPhp() here...
                        'this_directory' => \ScssPhp\ScssPhp\ValueConverter::fromPhp($location_paths['uri']), //There must be a better way to call fromPhp() here...
                        'primary_color' => \ScssPhp\ScssPhp\ValueConverter::parseValue($this->get_color('primary_color', false, '#0098d7')), //There must be a better way to call parseValue() here...
                        'secondary_color' => \ScssPhp\ScssPhp\ValueConverter::parseValue($this->get_color('secondary_color', false, '#95d600')), //There must be a better way to call parseValue() here...
                        'background_color' => \ScssPhp\ScssPhp\ValueConverter::parseValue($this->get_color('background_color', false, '#f6f6f6')), //There must be a better way to call parseValue() here...
                    $all_scss_variables = apply_filters('nebula_scss_variables', $nebula_scss_variables);
                    //Imports/Partials (find the last modified time)
                    $latest_import = 0;
                    foreach ( glob($imports_directory . '*') as $import_file ){
                        if ( filemtime($import_file) > $latest_import ){
                            $latest_import = filemtime($import_file);
                            if ( $latest_import > $this->latest_scss_mtime ){
                                $this->latest_scss_mtime = $latest_import;
                    $this->add_custom_scssphp_functions($this->scss); //Add custom PHP functions that can be used in Sass
                    do_action('nebula_before_sass_compile', $location_paths); //Allow modification of files before looping through to compile Sass
                    //Compile each SCSS file
                    foreach ( glob($location_paths['core'] . '*.scss') as $scss_file ){ //@TODO "Nebula" 0: Change to glob_r() but will need to create subdirectories if they don't exist.
                        $scss_file_path_info = pathinfo($scss_file);
                        $debug_name = str_replace(WP_CONTENT_DIR, '', $scss_file_path_info['dirname']) . '/' . $scss_file_path_info['basename'];
                        $this->timer('Sass File (' . $debug_name . ')');
                        //Skip file conditions (only if not forcing all)
                        if ( empty($force_all) ){
                            //@todo "Nebula" 0: Add hook here so other functions/plugins can add stipulations of when to skip files. Maybe an array instead?
                            $is_admin_file = (!$this->is_admin_page() && !$this->is_login_page()) && in_array($scss_file_path_info['filename'], array('login', 'admin', 'tinymce')); //If viewing front-end, skip WP admin files.
                            if ( $is_admin_file ){
                                $this->timer('Sass File (' . $debug_name . ')', 'end');
                        //If file exists, and has .scss extension, and doesn't begin with "_".
                        if ( is_file($scss_file) && $scss_file_path_info['extension'] === 'scss' && $scss_file_path_info['filename'][0] !== '_' ){
                            //Determine the .css output filepath
                            $output_directory = $location_paths['output']; //Default to the output directory
                            if ( ($location_name == 'parent' || $location_name == 'child') ){
                                if ( $scss_file_path_info['filename'] === 'style' ){
                                    $output_directory = $location_paths['directory'] . '/'; //Root directory for theme style.css
                            $css_filepath = $output_directory . $scss_file_path_info['filename'] . '.css';
                            wp_mkdir_p($location_paths['output']); //Create the output directory (in case it doesn't exist already)
                            //Update the last SCSS file modification time (if later than the latest yet)
                            if ( filemtime($scss_file) > $this->latest_scss_mtime ){
                                $this->latest_scss_mtime = filemtime($scss_file);
                            //If style.css has been edited after style.scss, save backup but continue compiling SCSS
                            if ( (is_child_theme() && $location_name !== 'parent' ) && ($scss_file_path_info['filename'] === 'style' && file_exists($css_filepath) && $this->get_data('scss_last_processed') != '0' && $this->get_data('scss_last_processed')-filemtime($css_filepath) < -30) ){
                                copy($css_filepath, $css_filepath . '.bak'); //Backup the style.css file to style.css.bak
                                if ( $this->is_dev() || current_user_can('manage_options') ){
                                    global $scss_debug_ref;
                                    $scss_debug_ref = $location_name . ':';
                                    $scss_debug_ref .= ($this->get_data('scss_last_processed')-filemtime($css_filepath));
                                    add_action('wp_head', array($this, 'scss_console_warning')); //Call the console error note
                            //If .css file doesn't exist, or is older than .scss file (or any partial), or is debug mode, or forced
                            if ( !file_exists($css_filepath) || filemtime($scss_file) > filemtime($css_filepath) || $latest_import > filemtime($css_filepath) || $this->is_debug() || $force_all ){
                                ini_set('memory_limit', '512M'); //Increase memory limit for this script. //@todo Nebula 0: Remove this when possible...
                                global $wp_filesystem;
                                $existing_css_contents = ( file_exists($css_filepath) )? $wp_filesystem->get_contents($css_filepath) : '';
                                //If the correlating .css file doesn't contain a comment to prevent overwriting
                                if ( !strpos(strtolower($existing_css_contents), 'scss disabled') ){ //@todo "Nebula" 0: Update strpos() to str_contains() in PHP8
                                    $this_scss_contents = $wp_filesystem->get_contents($scss_file); //Copy SCSS file contents
                                    //Catch fatal compilation errors when PHP v7.0+ to provide additional information without crashing
                                    try {
                                        $compiled_css = $this->scss->compileString($this_scss_contents, $scss_file)->getCss(); //Compile the SCSS
                                        $this->was_sass_processed = true;
                                    } catch (\Throwable $error){
                                        $unprotected_array = (array) $error;
                                        $prefix = chr(0) . '*' . chr(0);
                                        $sass_errors[] = array(
                                            'file' => $scss_file,
                                            'message' => $unprotected_array[$prefix . 'message']
                                        do_action('qm/error', $error);
                                        continue; //Skip the file that contains errors
                                    $enhanced_css = $this->scss_post_compile($compiled_css); //Compile server-side variables into SCSS
                                    $wp_filesystem->put_contents($css_filepath, $enhanced_css); //Save the rendered CSS.
                                    $this->update_data('scss_last_processed', time());
                        $this->timer('Sass File (' . $debug_name . ')', 'end');
                    $this->timer('Sass (' . $location_name . ')', 'end');
                return $this->was_sass_processed;


    To override this PHP function, use this hook in your child theme or plugin ("my_custom" can be changed):

    add_filter('pre_nebula_render_scss', 'my_custom_render_scss', 10, 2); //The last integer must be 1 more than the actual parameters
    function my_custom_render_scss($null, $child){ //$null is required, but can be ignored
        //Write your own code here
        return true; //Return true to prevent the original function from running afterwords

    You can completely disable this PHP function with a single line actions:

     add_filter('pre_nebula_render_scss', '__return_false');