Register CPTs using Dashicons for admin menu icon in WP 3.8

WordPress 3.8 introduced in core the plugin MP6 that among other things use an iconic font called Dashicons to display fonts in the dashboard.

Now, is well know that register_post_type has an argument 'menu_icon' that allow to specify a custom icon for the CPT admin menu entry.

Read More

In my plugins/themes I often use that argument with my custom icon images that normally are dark because before 3.8 admin menu had a light background. With the default dark menu background in WP 3.8 my icons become almost invisible.

That aside, I think that use the new dashicons for my CPT will be cool.

After some research, I know I can just use the CSS from dashicons, something like

#menu-posts-mycpt div.wp-menu-image:before { content: "f226"; }

However using both 'menu_icon' argument of register_post_type and previous css will print both icons in WP 3.8 and one icon + a strange char in WP 3.8-, and without use 'menu_icon' argument, on older versions the default icon is used.

I know I can conditionally add 'menu_icon' in register_post_type for WP 3.8- versions and conditionally add previous css for WP 3.8+, but:

  • that involves to add some code (2 conditional statements) for every CPT registered, so updating plugins/themes is quite an hard work
  • it seems to me more a workaround than an elegant solution

So, questions are:

Is possible to use dashicons css for WP 3.8+ and use custom image set up via 'menu_icon' param for previous versions in a “simpler” way that doesn’t involve add 2 conditional for every CPT registered?

And, if so, is possible do that in some automagic way straight from register_post_type without any additional code?

Related posts

4 comments

  1. After going through the rabbit hole and back, the answer is – yes, core does allow to easily use dashicons when registering post types and adding menu pages.

    To use dashicon you need to pass its CSS class dashicons-[name] to menu_icon or icon_url in relevant places.

    Classes available can be looked up in dashicons.css in source or Dashicons site (click icon and look at the name for it on top).

    Alert! 3.8 seems to have released with dashicons-piechart as example of the class in inline docs, which is wrong and won’t work. Actual class for that icon in release is dashicons-chart-pie.

  2. Easy: Just read the the relevant part of the register_post_type() phpDocBlock and then use the right argument for menu_icon 😀

    • Use the dashicons class. For e.g. dashicon-groups
    • Pass a base64-encoded SVG using a data URI, which will be colored to match the color scheme. This should begin with data:image/svg+xml;base64,.
    • Pass 'none' to leave div.wp-menu-image empty, so an icon can be added via CSS.
  3. I’m answering myself because today I asked myself the 2 questions I posted, and spent some time to find an answer. Once I found a solution, I want to share it, but any other solution is higly apprecciated and I’m ready to accept any solution that I found better than mine.
    Edits and improvements to my solution are appreciated as well nay encouraged.


    Edit

    After Rarst answer I’ve edited the code. Now function use standard dashicons classes, but also allow to specify an old style image url in menu_icon argument and a brand new dashicons class in the menu_dashicon argument.


    Workflow

    First think I thought was that register_post_type, fires an action, registered_post_type, that pass to hooking functions the arguments passed to register_post_type, without filtering them, so is possible to creat custom arguments to that functions.

    So I decided to pass the argument 'menu_dashicon' to pass a custom dashicon.

    After that I thought to create a class that listen to that argument, saving icon in a class variable. The same class can be responsible to

    1. check the currnt version of WP, and if it is less then 3.8 do nothing
    2. if version is 3.8+ loop the $menu array on proper hook and:
    3. remove, if present, any custom images added via 'menu_icon' and
    4. add the inline style according the what is added via the 'menu_dashicon' param

    I create the code in a single file, in this way it can be easily included in any theme/plugin or even used as MU plugin and after that one can just use the brand new 'menu_dashicon' argument in every theme and/or plugin installed.

    I’ve also added a minimal plugin header that allow to use it as a standalone plugin, but probably that’s the less usefull way to use.

    How to use

    Inside register_post_type just pass the 'menu_dashicon' argument with the value of the dashicon class (without prefix ‘dashicons-‘):

    $args = array(
      ...
      'menu_dashicon' => 'chart-pie', // dashicons will be used in WP 3.8+
      'menu_icon' => $url_of_the_icon // icon images will be used in WP 3.7.1 & previous
    );
    
    register_post_type('my_cpt', $args);
    

    That’s all. Get Dashicons icon class name from its site.

    So here the code:

    <?php
    /**
    * Plugin Name: GM CPT Icon
    */
    namespace GM;
    
    class CptIcon {
    
      public static $cpt;
      
      public $css;
      
      static function registerIcon( $cpt, $icon ) {
        self::$cpt[$cpt] = $icon;
      }
      
      function init() {
        if ( $this->mp6() ) {
          add_action('admin_menu', array($this, 'parseMenu') );
        }
      }
      
      function mp6() {
        return version_compare( $GLOBALS['wp_version'],  '3.8', '>=' );
      }      
      
      function parseMenu() {
        if ( $this->mp6() && ! empty( self::$cpt ) )  {
          foreach ( $GLOBALS['menu'] as $i => $item ) {
            if  $item[1] === 'edit_posts' && (strpos($item[2], 'edit.php?post_type=') === 0)) {
              $this->menuItemClass($i, str_replace('edit.php?post_type=', '', $item[2]));
            }
          }
        }
      }
      
      function menuItemClass( $i, $type ) {
        if ( in_array($type, array_keys(self::$cpt), TRUE ) ) {
          $GLOBALS['menu'][$i][4] = str_replace('menu-icon-post', '', $GLOBALS['menu'][$i][4]);
          $GLOBALS['menu'][$i][6] = 'dashicons-' . self::$cpt[$type];
        }
      }
    
    }
    
    add_action('plugins_loaded', function() {
      if ( is_admin() && !( defined('DOING_AJAX') && DOING_AJAX  ) ) {
        $cpticon = new CptIcon;
        $cpticon->init();
      }
    });
    
    add_action('registered_post_type', function( $post_type, $args ) {
      if ( isset($args->menu_dashicon) && ! empty($args->menu_dashicon) ) {
        CptIcon::registerIcon($post_type, $args->menu_dashicon);
      }
    }, 10, 2);
    

    It is also available as Gist


    CPTs using dashicons

    Two CPTs: “Ideas” and “Gallery” using Dashicons. Note auto color change with different admin color schemes.


  4. I simply added this line to the code which registers the custom post type:

    'menu_icon'    => 'dashicons-admin-users',
    

    Here’s the full code

    enter image description here

    No need to add any CSS.

Comments are closed.