14

I'm trying to add custom CSS (set via theme options) to the TinyMCE visual editor in WordPress. On the front end, the theme generates this CSS and outputs it on the wp_head hook. The problem I'm running into is being able to add that CSS output to the editor.

This can't be done with add_editor_style( 'editor-style.css' ) because we need to use PHP to access the theme option.

As an example of how it works on the front end:

add_action( 'wp_head', 'my_custom_colors' );

function my_custom_colors() {
    $color_1 = get_theme_mod( 'color_1', 'cc4a00' );

    echo "<style type='text/css'>a { color: #{$color_1}; }";
}

I need a method for getting that custom style into the visual editor. Any help would be greatly appreciated.

9
  • 1
    Note: custom post-content CSS is Plugin Territory, and should not be put in a Theme. Otherwise, users will lose their custom CSS when switching to a different Theme. Commented Oct 31, 2013 at 19:15
  • @ChipBennett I only partly agree. This highly depends on what you're styling. Aside from that, it doesn't matter where it is (for that question).
    – kaiser
    Commented Oct 31, 2013 at 19:18
  • @ChipBennett This has to do with theme styles, not custom post content CSS. This is pretty standard stuff that you can do with the theme customizer. It's just applying these styles that has me stumped. It's meant to compliment editor-style.css, which is theme territory. Commented Oct 31, 2013 at 19:22
  • Wait: are you talking about dynamic custom editor style? As in: make the visual editor (TinyMCE) aware of Theme option-defined styles? If so, disregard my original comment, and +1 for the question. Commented Oct 31, 2013 at 19:26
  • Could you file an edit and show an example of a full set for the theme style? Would be easier to test then.
    – kaiser
    Commented Oct 31, 2013 at 19:52

5 Answers 5

7

Solution 1

This works as javascript solution:

Example:

tinyMCE.activeEditor.dom.addStyle('p {color:red; font-size:28px;}');

just open your js console and paste it for a quick test. To target a specific editor one should use:

tinyMCE.getInstanceById('##editorID##').dom.addStyle('p {color:red; font-size:28px;}');

This will inject the provided string into the editors iframe <head><style id="mceDefaultStyles"></style> ...

Solution 2

Use wp_ajax as callback handler to add dynamic styles on editor init by using a filter

add_filter('tiny_mce_before_init', 'dynamic_editor_styles', 10);

function dynamic_editor_styles($settings){
    // you could use a custom php file as well, I'm using wp_ajax as
    // callback handler for demonstration
    // content_css is a string with several files seperated by a comma
    // e.g. file1, file2, ... extend the string

    $settings['content_css'] .= ",".admin_url('admin-ajax.php') ."/?action=dynamic_styles";
    
    return $settings;
}

// add wp_ajax callback
add_action('wp_ajax_dynamic_styles', 'dynamic_styles_callback');
function dynamic_styles_callback(){
    echo "p {color:red} h1{font-size:48px;}";
}
6
  • 2
    I'm not entirely sure what to do with that. Commented Oct 31, 2013 at 20:07
  • Solution #2 actually makes a lot of sense to me. I'll give that a test run. Commented Oct 31, 2013 at 20:23
  • I went with solution #2. The difference is that I dropped the first function and used add_editor_style( add_query_arg( 'action', 'my_editor_styles', admin_url( 'admin-ajax.php' ) ) since this is the standard way for themes to add editor styles. Commented Oct 31, 2013 at 20:49
  • Just for my own edification, I'd be curious to know if mce_css worked for you (if you tried it). Commented Oct 31, 2013 at 21:18
  • I actually haven't tried it because I knew you could pass a non-CSS file to add_editor_style(), but it should work just fine looking at the code. Using mce_css is actually a better solution in the case that you need to hook in after styles added via add_editor_style(). One such reason in a theme is because full URLs added via add_editor_style() are output first, despite the order they're added. Commented Oct 31, 2013 at 21:40
11

WordPress provides a mce_css filter, that can be used to add custom stylesheets to the Visual Editor. According to the Codex:

The file can be a .php file, allowing dynamic generation of CSS rules for the content editor.

Example Codex filter callback, modified for a Theme:

function wpse120831_mce_css( $mce_css ) {
    if ( ! empty( $mce_css ) )
        $mce_css .= ',';

    $mce_css .= get_template_directory_uri() . '/dynamic-css.php';

    return $mce_css;
}

add_filter( 'mce_css', 'wpse120831_mce_css' );
3
  • Mr.Bennett was faster, just added that to my answer below as well :), but slightly different. Commented Oct 31, 2013 at 20:13
  • @ungestaltbar we're actually using two entirely different filters. :) Commented Oct 31, 2013 at 20:22
  • with the same result :) Commented Oct 31, 2013 at 20:25
4

I accepted the solution above by @ungestaltbar. However, I wanted to expand on this answer a bit with the full solution that I am using so that others could see how it works.

add_action( 'after_setup_theme', 'my_theme_setup' );

function my_theme_setup() {

    add_editor_style(
        array(
            'editor-style.css',
            add_query_arg( 'action', 'my_editor_styles', admin_url( 'admin-ajax.php' ) ),
        )
    );
}

add_action( 'wp_ajax_my_editor_styles', 'my_editor_styles_callback' );
add_action( 'wp_ajax_nopriv_my_editor_styles', 'my_editor_styles_callback' );

function my_editor_styles_callback() {

    // @todo sanitize
    $color_1 = get_theme_mod( 'color_1', 'cc4a00' );

    echo "a { color: #{$color_1}; }";

    die();
}

I'm hoping it's okay to post another answer here like this. I didn't see a way to post this in direct reply to my accepted solution. I'm still learning how to use WPSE.

4
  • Maybe you should add add_action( 'wp_ajax_nopriv_my_editor_styles', 'my_editor_styles_callback' ); if editor is used on the frontend (for non logged-in users). Commented Oct 31, 2013 at 21:06
  • Yes, answer with significant changes/tweaks is perfectly fine. :) FYI if it was over some issue to be fixed in original answer then submitting edit to it would be way to go.
    – Rarst
    Commented Oct 31, 2013 at 21:11
  • @OriginalEXE - Yep. I'll update it. Commented Oct 31, 2013 at 21:23
  • Just a tip for anyone else who finds this later, I was unable to get this to work properly, as it's presented. I finally figured out that I had to include header information in the callback, so it would be seen as css. header("Content-type: text/css; charset: UTF-8"); Not sure if this is something different in my .htaccess or what.
    – SkyShab
    Commented Nov 18, 2014 at 15:29
4

I am probably late to this party but after using the above solution, I soon realized that page load speed of the editor had severely been crippled! Taking a keen look at the code, I realized that the code keeps executing long after tinyMCE.activeEditor has been initialized. The code uses The setInterval() method which evaluates an expression at specified intervals, I believe that was because you couldn't determine at what point during code execution "activeEditor" will be available. This is what brought the page speed down to its knees.

A much better solution I am using to build a plugin is this

   /**
     * Extend TinyMCE config with a setup function.
     * See http://www.tinymce.com/wiki.php/API3:event.tinymce.Editor.onInit

     */
    function custom_tinymce_css($init) {

      $css = get_option('some_css'); 

     ?>

        <script type="text/javascript">            

            function addTempCSS( ed ) {
                ed.onInit.add( function() {
                    tinyMCE.activeEditor.dom.addStyle(<?php echo json_encode($css) ?>);
                } );
            };
        </script>

        <?php
        if (wp_default_editor() == 'tinymce')
            $init['setup'] = 'addTempCSS';

        return $init;
    }
    add_filter('tiny_mce_before_init', 'custom_tinymce_css');

Here a native TinyMCE listener is used to execute the code after the active editor is initialized. I hope this will help someone out there. Kind regards.

1
  • 1
    This working for me it's just has a deprecated API call in tinymce 4.0 you need to change ed.onInit.add( function() { ... to ed.on('init', function() { ...
    – Mohammed
    Commented Feb 26, 2016 at 7:41
1

This is a modified solution posted on the WordPress.org forums for this question: http://wordpress.org/support/topic/customdynamic-css-in-tinymce?replies=14#post-4827573

This definitely works. I'm no JS guru though, so I'm not entirely sure if this is the best solution.

add_action( 'before_wp_tiny_mce', 'my_tinymce_callback' );

function my_tinymce_callback() {

    $color_1 = get_theme_mod( 'color_1', 'cc4a00' ); ?>

    <script type="text/javascript">
    jQuery( document ).ready(

        function() {
            var my_style = 'a { color: #<?php echo $color_1; ?>; }';

            var checkInterval = setInterval(
                function() {

                    if ( 'undefined' !== typeof( tinyMCE ) ) {
                        if ( tinyMCE.activeEditor && ! tinyMCE.activeEditor.isHidden() ) {

                            jQuery( '#content_ifr' ).contents().find( 'head' ).append( '<style type="text/css">' + my_style + '</style>' );

                            clearInterval( checkInterval );
                        }
                    }
                }, 
                500 
            );
        }
    );
    </script>
<?php }

This could also be added to a JS file. You could easily pass variables via wp_localize_script() with that.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.