Display Posts Shortcode – Full Content

The Display Posts Shortcode plugin lets you display a list of posts based on any criteria. By default it displays titles only. The example below makes it display the post’s title and full content.

  • Using Yoast SEO Structured Data with Genesis

    The most recent update to Yoast SEO includes an incredibly powerful implementation of structured data, which is a tool search engines use to better understand your website. They also have extensive documentation for extending it.

    But Genesis already includes structured data, also known as schema markup. If you’re using Genesis and Yoast SEO, it is best to disable Genesis schema to prevent search engine confusion and conflicting data.

    If you don’t disable either Genesis or Yoast SEO schema, you’ll have duplicate schema on your page (screenshot).

    Install the Plugin

    If you don’t want to edit code in your Genesis theme, you can install my
    Disable Genesis Schema plugin. Simply upload and activate!

    View Plugin

    Before we get to the removal code, I’ll describe how Genesis and Yoast’s structured data are different.

    How Genesis Schema works

    Genesis uses an approach called microdata, which are data attributes attached to elements already on the page to describe those elements to search engines. For instance, your post title has itemprop="headline" to tell search engines that this is the headline of the current article.

    You can run into issues if you remove elements from the page because you’re also removing the schema data that’s attached to those elements. For instance, if you remove the author name from the top of the post, you also lose the structured data saying you’re the author of this post.

    You also end up with many unconnected blocks of schema. Here’s the results of the Structured Data Testing Tool on an article on my site before the code change:

    How Yoast SEO Schema works

    Yoast SEO uses JSON-LD which is a newer and better approach to structured data (more information).

    JSON is a lightweight method to structure data to share with search engines and others. Rather than scattering your Schema data throughout the page and making the search engine assemble it (e.g. microdata), you prepare a small block of code that tells search engines exactly what they need to know in one place.

    The -LD part stands for Linked Data, which means you’re able to link all of the individual schema components together. In the screenshot above you can see everything is a separate block with microdata, but with JSON-LD it would follow a hierarchical structure. The Article is part of a WebPage which is part of a WebSite.

    Here’s my structured data after disabling Genesis schema and using the latest version of Yoast SEO:

    Disable Genesis Schema

    Install the Plugin

    If you don’t want to edit code in your Genesis theme, you can install my
    Disable Genesis Schema plugin. Simply upload and activate!

    View Plugin

    The code below will remove the schema markup from all the elements on the page.

    I created a disable-genesis-schema.php file in my child theme and included it in my functions.php file (see here). There’s also a plugin version you can use instead of adding this code to your theme.

    <?php
    /**
    * Disable Genesis Schema
    *
    * @package EAGenesisChild
    * @author Bill Erickson
    * @since 1.0.0
    * @license GPL-2.0+
    */
    add_action( 'init', 'be_disable_genesis_schema' );
    /**
    * Disable Genesis Schema
    * @author Bill Erickson
    * @see https://www.billerickson.net/yoast-schema-with-genesis/
    */
    function be_disable_genesis_schema() {
    $elements = array(
    'head',
    'body',
    'site-header',
    'site-title',
    'site-description',
    'breadcrumb',
    'breadcrumb-link-wrap',
    'breadcrumb-link-wrap-meta',
    'breadcrumb-link',
    'breadcrumb-link-text-wrap',
    'search-form',
    'search-form-meta',
    'search-form-input',
    'nav-primary',
    'nav-secondary',
    'nav-header',
    'nav-link-wrap',
    'nav-link',
    'entry',
    'entry-image',
    'entry-image-widget',
    'entry-image-grid-loop',
    'entry-author',
    'entry-author-link',
    'entry-author-name',
    'entry-time',
    'entry-modified-time',
    'entry-title',
    'entry-content',
    'comment',
    'comment-author',
    'comment-author-link',
    'comment-time',
    'comment-time-link',
    'comment-content',
    'author-box',
    'sidebar-primary',
    'sidebar-secondary',
    'site-footer',
    );
    foreach( $elements as $element ) {
    add_filter( 'genesis_attr_' . $element, 'be_remove_schema_attributes', 20 );
    }
    }
    /**
    * Remove schema attributes
    *
    */
    function be_remove_schema_attributes( $attr ) {
    unset( $attr['itemprop'], $attr['itemtype'], $attr['itemscope'] );
    return $attr;
    }

    Disable Yoast SEO Schema

    If you would prefer keeping Genesis’ schema data, you can disable Yoast SEO’s schema data using the following code in your theme’s functions.php file or the Code Snippets plugin:

    add_filter( 'wpseo_json_ld_output', '__return_false' );

    This is a good option if you’ve already made extensive customizations to the Genesis schema data on your site and aren’t ready to refactor that code to support Yoast’s structured data.

    But if you’re like 99% of Genesis sites that haven’t modified the default schema, you’ll be better served switching over to Yoast SEO for schema data. I expect at some point Genesis will disable its own schema output if Yoast SEO is active, in the same way it removes its “Genesis SEO Settings” metabox if an SEO plugin is active. For now, you’ll have to manually disable it using the code provided above.

    With the popularity of Yoast SEO, I expect more plugins to include support for schema through Yoast SEO. A recipe plugin could extend the article’s schema to include the recipe, rather than inserting their disconnected recipe schema directly into the page content.

    I’m excited to see how this can be extended! If you have made any extensions to the schema (documentation here), please share it in the comments below.

  • CSS Grid – Center Last Item

    CSS Grid is wonderful for building multi-column layouts, but if you have one less item in your grid than you expected, it will look off balance.

    To center the last one, you can double the number of columns to 4 and set each item to span 2. If the last item is odd, start on the second column so it spans columns 2 and 3.

    .grid {
    	display: grid;
    	grid-template-columns: repeat( 4, 1fr );
    }
    
    .item {
    	grid-column: span 2;
    }
    
    .item:nth-last-child(1):nth-child(odd) {
    	grid-column: 2 / span 2;
    }

    With a bit of math and SCSS, this can be generalized to work with any number of columns.

    However, I only recommend using this for two or three columns because it will only center if the last row is short by one.

    .grid-on-left {
    	@include grid-center-last( 2 );
    }
    
    .grid-on-right {
    	@include grid-center-last( 3 );
    }
    
    @mixin grid-center-last( $columns ) {
    	$total_columns: $columns * 2;
    	$span: $total_columns / $columns;
    	$offset: $columns - 1;
    	$nth-child: if( $columns % 2 == 0, 'odd', 'even' );
    
    	display: grid;
    	grid-template-columns: repeat( $total_columns, 1fr );
    
    	& > * {
    		grid-column: span $span;
    
    		&:nth-last-child(#{$offset}):nth-child(#{$nth-child}) {
    			grid-column: $span / span $span;
    		}
    	}
    }
  • Advanced Search using SearchWP and FacetWP

    I recently collaborated with Emergent Order to design and build a new website for Mountain States Legal Foundation, a non-profit providing legal services.

    One of the key goals for the redesign was improved site search. Users need to easily find information about the cases they care about.

    We leveraged SearchWP for relevant search results, indexing of documents, and generating related content listings throughout the site. We used FacetWP for advanced filtering on the search results page.

    Here are some tips and code snippets I used to implement the advanced site search.

    SearchWP

    We use SearchWP on every site because it provides two valuable features:

    1. It expands the data used for indexing.
      While the standard WordPress search only looks at the title and content fields, SearchWP lets you index metadata, taxonomies, and more. You can also control which content types can be included in the index and the weighting of different factors when determining relevance.
    2. It delivers relevant search results.
      The standard WordPress search finds any post with a keyword match in the title or content and returns those results with the most recent first. The most recent article often isn’t the most relevant.

    Include documents in search

    When setting up SearchWP, we added “Media” as one of the post types to index, and limited the file type to “All Documents”.

    If you check “Transfer weight to parent”, the post or page containing the document will be displayed instead of the document itself, which is a really nice feature.

    In this particular case we wanted to expose the documents themselves. You’ll need to ensure your theme displays ‘attachment’ posts correctly in the loop.

    Here’s the archive partial we used for documents in search results. We’re also including the source page as a clickable subtitle in the results.

    <?php
    /**
    * Attachment archive partial
    *
    * @package MountainStatesLegal2019
    * @author Bill Erickson
    * @since 1.0.0
    * @license GPL-2.0+
    **/
    $subtitle = $subtitle_url = false;
    $parent = wp_get_post_parent_id( get_the_ID() );
    if( !empty( $parent ) && 'case' == get_post_type( $parent ) ) {
    $subtitle = get_the_title( $parent );
    $subtitle_url = get_permalink( $parent );
    }
    echo '<article class="post-summary type-document">';
    if( !empty( $subtitle ) )
    echo '<h5 class="small"><a href="' . esc_url_raw( $subtitle_url ) . '">' . $subtitle . '</a></h5>';
    echo '<h2 class="entry-title"><a href="' . get_permalink() . '">' . get_the_title() . '</a></h2>';
    echo '<div class="entry-meta"><span class="entry-type">Document</span></div>';
    echo '</article>';

    Search Metrics

    We wanted to provide editors with insight into how users are interacting with the site search. SearchWP Metrics is perfect for this. It lets you monitor the amount of site searches, popular search terms, average ranking of clicked result, and more.

    The “No Result Searches” can be a tool to determine new content that needs to be written, updates to existing content so it better matches common search terms, or search synonyms you can add to direct those searches to ones with results.

    If you enable Live Search results as the user types, use this code to track metrics on those as well.

    Customize Metabox

    When you have the SearchWP Related addon installed, it adds a “SearchWP Related Content” metabox to all post types.

    The code below makes the following changes:

    • Limits it to just the post, case, and press_release post type
    • Sets the priority to ‘low’ so it doesn’t appear above important metaboxes
    • Changes the title to “Related News”
    <?php
    /**
    * SearchWP Related on specific post types
    * @see https://www.billerickson.net/code/searchwp-related-on-specific-post-types/
    */
    function ea_swp_related_excluded_post_types( $exclude = array() ) {
    $allowed = array( 'post', 'case', 'press_release' );
    $all = array_keys( get_post_types() );
    return array_diff( $all, $allowed );
    }
    add_filter( 'searchwp_related_excluded_post_types', 'ea_swp_related_excluded_post_types' );
    /**
    * SearchWP Related, metabox priority
    *
    */
    function ea_swp_metabox_priority( $priority ) {
    return 'low';
    }
    add_filter( 'searchwp_related_meta_box_priority', 'ea_swp_metabox_priority' );
    /**
    * SearchWP Related, metabox title
    *
    */
    function ea_swp_metabox_title( $title ) {
    return 'Related News';
    }
    add_filter( 'searchwp_related_meta_box_title', 'ea_swp_metabox_title' );
    view raw searchwp.php hosted with ❤ by GitHub

    FacetWP

    FacetWP is the simplest tool for filtering and sorting content in WordPress. We use it often for filtering results on archive pages (example), but it can also be used for search results.

    Add labels to FacetWP filters

    When you setup your FacetWP filters (known as “facets”), you specify a label for each. FacetWP doesn’t display these labels on the frontend, but the JavaScript code below adds the label above the facet.

    jQuery(function($){
    // FacetWP Labels
    // @see https://www.billerickson.net/advanced-search-with-searchwp-and-facetwp/#facetwp-labels
    $(document).on('facetwp-loaded', function() {
    $('.facetwp-facet').each(function() {
    var $facet = $(this);
    var facet_name = $facet.attr('data-name');
    var facet_label = FWP.settings.labels[facet_name];
    if ($facet.closest('.facet-wrap').length < 1) {
    $facet.wrap('<div class="facet-wrap"></div>');
    $facet.before('<h5 class="facet-label">' + facet_label + '</h5>');
    }
    });
    });
    });
    view raw global.js hosted with ❤ by GitHub

    Filter by year

    When you create a facet, you can select from many different types and data sources. I used the “Slider” type and “Post Date” as the data source.

    The Post Date source uses a UNIX timestamp, so I had to customize the indexer to use the year for this facet.

    My facet name was filter_by_year, so change that in the code below to match your facet name. Once you’ve added this code, click “Re-index” to update the data.

    <?php
    /**
    * FacetWP, Filter by Year
    */
    add_filter( 'facetwp_index_row', function( $params, $class ) {
    if ( 'filter_by_year' == $params['facet_name'] ) {
    $raw_value = $params['facet_value'];
    $params['facet_value'] = date( 'Y', strtotime( $raw_value ) );
    $params['facet_display_value'] = $params['facet_value'];
    }
    return $params;
    }, 10, 2 );
    view raw facetwp.php hosted with ❤ by GitHub

    Hide empty facets

    Some of the filters we’ve added only apply to certain content types. For instance, the “Open” and “Closed” status at the bottom is a taxonomy on the ‘case’ content type.

    FacetWP displays the filter regardless of whether there’s anything to filter. The code below hides the facet if it’s empty. Note: this assumes each facet is inside of its own .widget container.

    jQuery(function($){
    // Hide empty facets
    // @see https://www.billerickson.net/advanced-search-with-searchwp-and-facetwp/#facetwp-hide-empty
    $(document).on('facetwp-loaded', function() {
    $.each(FWP.settings.num_choices, function(key, val) {
    var $parent = $('.facetwp-facet-' + key).closest('.widget');
    (0 === val) ? $parent.hide() : $parent.show();
    });
    });
    });
    view raw global.js hosted with ❤ by GitHub

    If a user submitted an empty search form, WordPress listed all the site’s content and the filters appeared in the sidebar. Selecting any filter caused the 404 page to load as a listing in the content area.

    I added the following to functions.php to redirect empty search queries to a Search page that contained a search form and no FacetWP filters.

    <?php
    /**
    * Empty search redirect
    * https://www.billerickson.net/advanced-search-with-searchwp-and-facetwp/#empty-search
    */
    function ea_empty_search_redirect( $query ) {
    if( $query->is_main_query() && ! is_admin() && $query->is_search() && empty( $query->query['s'] ) ) {
    wp_redirect( home_url( 'search' ) );
    exit;
    }
    }
    add_action( 'pre_get_posts', 'ea_empty_search_redirect' );
    view raw functions.php hosted with ❤ by GitHub

    I’m using pre_get_posts instead of template_redirect so I can target only the main query. When I used template_redirect I ran into an issue with FacetWP filters not working.

    Customize FacetWP Pagination

    FacetWP uses JavaScript to refresh results so you can’t use the standard WordPress pagination. You can either use the shortcode [facetwp pager="true"] or the function facetwp_display( 'pager' );.

    It’s a good idea to add pagination scrolling so users are taken to the top of the results after navigating to a new page.

    You can customize the markup of the pagination function using the facetwp_pager_html filter. Here’s what I used on the site referenced above to match our standard pagination used elsewhere:

    <?php
    /**
    * FacetWP Pagination, all links, prev/next arrows
    *
    */
    add_filter( 'facetwp_pager_html', function( $output, $params ) {
    // All Links
    $output = '';
    if ( 1 < $params['total_pages'] ) {
    for ( $i = 1; $i <= $params['total_pages']; $i++ ) {
    $is_curr = ( $i === $params['page'] ) ? ' active' : '';
    $output .= '<a class="facetwp-page' . $is_curr . '" data-page="' . $i . '">' . $i . '</a>';
    }
    }
    // Prev / Next
    if( $params['page'] > 1 )
    $output = '<a class="facetwp-page" data-page="' . intval( $params['page'] - 1 ) . '">' . ea_icon( 'arrow-left' ) . '</a>' . $output;
    if( $params['page'] < $params['total_pages'] )
    $output .= '<a class="facetwp-page" data-page="' . intval( $params['page'] + 1 ) . '">' . ea_icon( 'arrow-right' ) . '</a>';
    $output = '<nav class="posts-navigation">' . $output . '</output>';
    return $output;
    }, 10, 2 );
    view raw facetwp.php hosted with ❤ by GitHub

    Include attachments in search results

    Even though I had set SearchWP to index attachments, FacetWP wasn’t returning them in the search results. The code below tells FacetWP to include attachments in its query:

    <?php
    /**
    * FacetWP, Index attachments
    *
    */
    add_filter( 'facetwp_indexer_query_args', function( $args ) {
    $args['post_status'] = array( 'publish', 'inherit' );
    return $args;
    });
    /**
    * FacetWP Query Args
    *
    */
    add_filter( 'facetwp_query_args', function( $args ) {
    $args['post_status'] = 'any';
    return $args;
    }, 10 );
    view raw facetwp.php hosted with ❤ by GitHub

    List total number of found results

    You can use facetwp_display( 'counts' ); or [facetwp counts="true"] to display the number of posts matching the current faceted search.

    You can customize the output using the facetwp_result_count filter.

    <?php
    /**
    * FacetWP, result count
    *
    */
    add_filter( 'facetwp_result_count', function( $output, $params ) {
    $output = 'Showing ' . $params['lower'] . '-' . $params['upper'] . ' of ' . $params['total'] . ' results';
    return $output;
    }, 10, 2 );
    view raw facetwp.php hosted with ❤ by GitHub

    Sort results

    You can display a dropdown to reorder the results using facetwp_display( 'sort' ); or [facetwp sort="true"].

    The placeholder when nothing has been selected is “Sort by”, which I changed to “Relevance” since SearchWP is delivering relevancy-based results.

    <?php
    /**
    * FacetWP, change sort label
    *
    */
    add_filter( 'facetwp_sort_options', function( $options, $params ) {
    $options['default']['label'] = 'Relevance';
    return $options;
    }, 10, 2 );
    view raw facetwp.php hosted with ❤ by GitHub

    Use singular name in post type filter

    We have a “Filter by Type” facet for filtering by post type. This uses the post type name which is typically plural, but the client wanted the singular form used in the filter. Also, we wanted to change “Media” to “Document” since that’s the only type of media we’re indexing.

    You can use the facetwp_index_row filter, and then check the facet name before making changes. In my case, our facet name is filter_by_type.

    <?php
    /**
    * FacetWP, singular name for post types
    *
    */
    add_filter( 'facetwp_index_row', function( $params, $class ) {
    if ( 'filter_by_type' == $params['facet_name'] ) {
    $labels = array(
    'Pages' => 'Page',
    'Cases' => 'Case',
    'News' => 'News Post',
    'Press Releases' => 'Press Release',
    'Media' => 'Document',
    );
    if( array_key_exists( $params['facet_display_value'], $labels ) )
    $params['facet_display_value'] = $labels[ $params['facet_display_value'] ];
    }
    return $params;
    }, 10, 2 );
    view raw facetwp.php hosted with ❤ by GitHub
  • Remove Genesis Layout Options

    Genesis provides six different layout options with one, two, or no sidebars.

    Most of the sites we build either have no sidebar or a single sidebar layout. We remove the unused layouts to keep the backend interface as simple as possible.

    Here are two different ways to remove unused layouts.

    Remove specific layouts

    You can use the genesis_unregister_layout() function to remove a single layout. If the only layouts you want to keep are Full Width Content and Content Sidebar, you can add this to your theme:

    // Remove unused sidebar
    unregister_sidebar( 'sidebar-alt' );
    
    // Remove unused layouts
    genesis_unregister_layout( 'content-sidebar-sidebar' );
    genesis_unregister_layout( 'sidebar-content-sidebar' );
    genesis_unregister_layout( 'sidebar-sidebar-content' );
    genesis_unregister_layout( 'sidebar-content' );
    

    Remove all layout options

    There’s no reason to display the Layout Settings when there aren’t multiple layouts to choose from. The following code:

    • Removes both sidebars
    • Removes all layouts except ‘full-width-content’
    • Removes the Layout Options metabox from the Edit Post / Page screen
    • Removes the Layout Options metabox from Genesis Archive Settings
    // Remove unused sidebars
    unregister_sidebar( 'sidebar' );
    unregister_sidebar( 'sidebar-alt' );
    
    // Remove layouts
    genesis_unregister_layout( 'content-sidebar-sidebar' );
    genesis_unregister_layout( 'sidebar-content-sidebar' );
    genesis_unregister_layout( 'sidebar-sidebar-content' );
    genesis_unregister_layout( 'sidebar-content' );
    genesis_unregister_layout( 'content-sidebar' );
    
    // Remove layout metaboxes
    remove_theme_support( 'genesis-inpost-layouts' );
    remove_theme_support( 'genesis-archive-layouts' );

  • Setting up a WordPress website with Genesis

    Genesis makes it easy for anyone to setup a WordPress website – no developer required!

    I specialize in crafting custom WordPress themes for businesses, but not everyone has the budget or time available for our full design process.

    This article will walk you through setting up a WordPress website yourself. My recommendations below will help you create a fast, elegant, and SEO friendly website.

    Quick Links

    1. Domain and Hosting
    2. Pick a theme
    3. Install the theme
    4. One-click demo install
    5. WordPress training
    6. Recommended plugins

    Need help?

    I can introduce you to WordPress developers who can help setup your website.

    Contact Me

    Why Genesis?

    There are plenty of WordPress themes on the market, but most are bloated with features you don’t need that slow down your site and make it difficult to edit. Once you purchase the theme, it’s often challenging to set it up to match the demo.

    Genesis is different. It’s built for speed and SEO. And now with its one-click demo install, it’s simple to setup.

    Genesis includes many design and layout options packaged as child themes. There are dozens of Genesis child themes available, so pick the one that best fits your needs.

    The core Genesis theme framework holds all the essential features and tools used on every website and is packaged as the parent theme.

    Where to find Genesis themes?

    You can purchase Genesis themes and the core framework from StudioPress. If you use WPEngine as your web host, you have access to all the themes for free.

    Here’s how you setup a WordPress website using a Genesis theme.

    Step 1: Domain and Hosting

    If you already have a website up and running, you can move on to the next step.

    A domain is the URL people type in their browser to find you. It’s your address on the web. You can purchase them for about $10/year at Namecheap or Godaddy.

    A web host is the server that holds your website. Since you plan to have a WordPress website, I highly recommend getting a WordPress managed host, which is a host that specializes in WordPress websites. Here are my recommended hosts.

    Once you have purchased a domain, signed up for hosting, and pointed your domain at your new host (your host will provide instructions on how to do this), you should have a brand new WordPress website that looks something like this:

    Step 2: Select a theme

    Browse the dozens of Genesis themes to find one that will work well for your specific needs. I recommend picking one of the newer themes because they take advantage of all the new features in WordPress, and are more likely to have the one-click demo install available.

    For this tutorial I’m going to use the Revolution Pro theme, the most recently released Genesis theme when this article was published.

    Step 3: Install your theme

    After selecting a theme and either purchasing it from StudioPress or downloading it from your WPEngine account, you should have two zip files: one for the Genesis theme framework, and one for your child theme.

    In your WordPress website, go to Appearance > Themes and click the “Add New” button at the top:

    Then click the “Upload” button and upload the Genesis zip file. Do the same with your child theme zip file. After uploading your child theme, click “Activate”.

    Step 4: One-click demo install

    If you selected one of the newer themes, you should now see the screen below. This is the one-click demo install. It will automatically install any plugins used on the demo site, and update the content to match the demo.

    That’s it! You should now have a website that matches the demo site. Visit your homepage, click “Edit Page” in the toolbar at the top, and start customizing your site’s content.

    WordPress Training

    If you need help learning the WordPress content editor or backend, I recommend you sign up for WP101. They provide incredible video tutorials for WordPress and popular plugins.

    Here’s one of their videos describing the Gutenberg content editor.

    These are plugins I use on almost every website.

  • Dynamic Dropdown Fields in ACF

    Advanced Custom Fields is a powerful tool for building metaboxes, theme settings, custom blocks, and more.

    The acf/load_field filter lets you customize a field before it loads. I often use it for dynamically populating dropdown lists.

    These code snippets can go in your theme’s functions.php file or a Core Functionality plugin.

    Selecting a form

    When a site contains multiple forms that might need to change over time, I like to create a site options page for managing which forms appear where.

    The acf/load_field filter runs before each field loads. I’ll then check to see if the field name matches the ones I have for the Subscribe and User Registration forms, and if so I’ll populate $field['choices'] with all the forms we have on the site.

    I’m using WPForms so will use wpforms()->forms->get() to list all forms, but you could do the same thing with Gravity Forms (example) or any other form plugin.

    /**
     * Dynamically select form in ACF
     * @see https://www.billerickson.net/dynamic-dropdown-fields-in-acf/
     * @author Bill Erickson
     *
     * @param array $field, the field settings array 
     * @return array $field
     */
    function be_acf_select_form( $field ) {
    
    	// These are the field names we're updating
    	$form_fields = array( 
    		'ea_subscribe_form', 
    		'ea_registration_form,' 
    	);
    	
    	if( ! in_array( $field['name'], $form_fields ) )
    		return $field;
    
    	// Get form list from WPForms
    	if( ! function_exists( 'wpforms' ) )
    		return $field;
    	$forms = wpforms()->form->get();
    
    	// Build list of choices
    	$field['choices'] = array( 0 => '(None)' );
    	foreach( $forms as $form ) {
    		$field['choices'][ $form->ID ] = $form->post_title;
    	}
    	return $field;
    }
    add_filter( 'acf/load_field', 'be_acf_select_form' );

    When you need to display the form, you pull the form ID from the Site Options page.

    Using ACF function: get_field( 'ea_subscribe_form', 'options' );
    Using WP function: get_option( 'options_ea_subscribe_form' );

    Display WPForm: wpforms_display( $form_id )
    Display Gravity Form: gravity_form( $form_id )

    I use this approach to define theme locations for forms, which adds a class of .location-subscribe to the subscribe form. I can then style the form using this class rather than the form ID, which ensures the styling continues to work if the form assigned to the “Subscribe” location changes.

    Selecting an icon

    On my homepage I have an Industries block with an icon for each industry.

    My clients are…

    Law Firms

    Your website needs to reflect the quality and professionalism of your practice.

    Publishers

    Your website needs a captivating design that keeps readers engaged and load fast at scale.

    Non-Profits

    You need a website that 
keeps stakeholders 
engaged, communicates 
your story, and supports 
your fundraising efforts.

    Corporations

    Your website needs flexible landing pages that are easy to build, on-brand, and can track success.

    The icons are stored as SVGs in my theme’s /assets/icons/category directory. In the ACF block metabox, the select dropdown dynamically lists all icons in that directory.

    Since I want this to only apply to a single field (name=”icon”), we can use a more specific filter: acf/load_field/name=icon.

    /**
     * Dynamically select icon in ACF
     * @see https://www.billerickson.net/dynamic-dropdown-fields-in-acf/
     * @author Bill Erickson
     *
     * @param array $field, the field settings array 
     * @return array $field
     */
    function be_acf_select_icon( $field ) {
    
    	$field['choices'] = array( 0 => '(None)' );
    	$icons = ea_get_theme_icons( 'category' );
    	foreach( $icons as $icon ) {
    		$field['choices'][ $icon ] = $icon;
    	}
    
    	return $field;
    }
    add_filter( 'acf/load_field/name=icon', 'be_acf_select_icon' );
    
    /**
     * Get Theme Icons
     * Refresh cache by bumping CHILD_THEME_VERSION
     */
    function ea_get_theme_icons( $directory = 'utility' ) {
    	$icons = get_option( 'ea_theme_icons_' . $directory );
    	$version = get_option( 'ea_theme_icons_' . $directory . '_version' );
    	if( empty( $icons ) || ( defined( 'CHILD_THEME_VERSION' ) && version_compare( CHILD_THEME_VERSION, $version ) ) ) {
    		$icons = scandir( get_stylesheet_directory() . '/assets/icons/' . $directory );
    		$icons = array_diff( $icons, array( '..', '.' ) );
    		$icons = array_values( $icons );
    		if( empty( $icons ) )
    			return $icons;
    		// remove the .svg
    		foreach( $icons as $i => $icon ) {
    			$icons[ $i ] = substr( $icon, 0, -4 );
    		}
    		update_option( 'ea_theme_icons_' . $directory, $icons );
    		if( defined( 'CHILD_THEME_VERSION' ) )
    			update_option( 'ea_theme_icons_' . $directory . '_version', CHILD_THEME_VERSION );
    	}
    	return $icons;
    }

    To display the icon, we use the ea_icon() function in our theme (see here).

    $icon = get_field( 'icon' );
    if( !empty( $icon ) )
    	echo ea_icon( array( 'icon' => $icon, 'group' => 'category' ) );

  • Gutenberg Starter Themes

    WordPress starter themes provide a solid foundation for building your own custom WordPress themes. In this article, I share my two Gutenberg-optimized starter themes.

    What is a starter theme

    A starter theme is a lean WordPress theme with minimal styles and functionality. It often contains simple functionality you use across most projects. It gives you a common starting point for your theme development.

    Gutenberg introduces many new features and styling requirements, which I’ve documented in my Developer’s Guide to Gutenberg. These starter themes incorporate all the recommendations listed in those articles.

    Both of my starter themes use hooks and filters for customization, on which I’m writing a tutorial right now. Be sure to subscribe to email updates so you’re notified when that article is published.

    Genesis Starter Theme

    Genesis is a theme framework, a base upon which you build your own custom WordPress theme. It helps you build higher quality themes faster, and is built with a focus on SEO and performance. Here’s more information on Genesis.

    EA Genesis Child is my starter theme for building custom Genesis child themes.

    Non-Genesis Starter Theme

    EA Starter is my other starter theme, which I use when Genesis isn’t a good fit – usually when I’m building a network theme that needs to support its own child themes.

    It’s a fork of underscores that I updated to include hooks from Theme Hooks Alliance. It’s structured exactly like my Genesis child theme so I can easily move between the two. The only differences are the hook names (ex: tha_content_top instead of genesis_before_loop ).

    More Starter Themes

    Here’s a list of other Gutenberg-friendly starter themes.

    Do you know of any other good starter themes that are Gutenberg optimized? Please share them in the comments below.

  • Shortcode Finder

    I’m cleaning up a website with a lot of old shortcodes. Before removing a shortcode, we wanted to be sure all pages that used it were updated.

    So I wrote a shortcode to help 🙂

    [find_shortcode shortcode="listing"] will display a list of all posts/pages using the [listing] shortcode.

    <?php
    /**
    * Find Shortcode
    *
    */
    function be_find_shortcode( $atts = array() ) {
    $atts = shortcode_atts( array(
    'shortcode' => '',
    ), $atts, 'find_shortcode' );
    $loop = new WP_Query( array(
    'posts_per_page' => -1,
    'post_type' => array( 'post', 'page' ),
    'orderby' => 'title',
    'order' => 'ASC'
    ));
    $found = array();
    if( $loop->have_posts() ): while( $loop->have_posts() ): $loop->the_post();
    global $post;
    if( has_shortcode( $post->post_content, $atts['shortcode'] ) )
    $found[] = '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>';
    endwhile; endif; wp_reset_postdata();
    if( !empty( $found ) ) {
    $output = '<h3>Pages with the [' . $atts['shortcode'] . '] shortcode</h3><ul>' . join( PHP_EOL, $found ) . '</ul>';
    } else {
    $output = '<h3>No pages found with [' . $atts['shortcode'] . '] shortcode</h3>';
    }
    return $output;
    }
    add_shortcode( 'find_shortcode', 'be_find_shortcode' );
    view raw functions.php hosted with ❤ by GitHub
  • Block Styles in Gutenberg

    Every block type in the WordPress editor can have multiple style options. Some block types already have style options like buttons and quotes.

    I’ll show you how to add and remove style options from blocks.


    Block styles let writers easily change the look of a block without any code. When editing a block with available styles, click the “Transform” icon in the top left to change styles.

    Here are the style options available in my theme for the quote block:

    This is a standard blockquote.

    Citation

    This is a large quote. It has a CSS class of “is-style-large”

    Citation

    They are technically just CSS classes added to the block. By using the Block Styles UI for previewing and selecting different styles, your clients won’t need to remember specific class names.

    You can add block styles to any block type in Gutenberg: core blocks and custom blocks from plugins. See my note at the bottom on finding the block name so you can attach your custom styles to it.

    Adding a script to the editor

    We’ll need to create a JavaScript file in your theme or plugin and load it in the block editor. You can use the enqueue_block_editor_assets hook to load assets into the block editor. It works the same as using wp_enqueue_scripts to load assets into the frontend of the site.

    Create an empty editor.js file in the directory /your-theme/assets/js/. Then add the following to your theme’s functions.php file:

    /**
     * Gutenberg scripts and styles
     * @see https://www.billerickson.net/block-styles-in-gutenberg/
     */
    function be_gutenberg_scripts() {
    
    	wp_enqueue_script(
    		'be-editor', 
    		get_stylesheet_directory_uri() . '/assets/js/editor.js', 
    		array( 'wp-blocks', 'wp-dom' ), 
    		filemtime( get_stylesheet_directory() . '/assets/js/editor.js' ),
    		true
    	);
    }
    add_action( 'enqueue_block_editor_assets', 'be_gutenberg_scripts' );

    Adding Block Styles

    I’m working on a website with two styles of headings.

    In my editor.js file I’m adding “Default” and “Alternate” style options:

    wp.domReady( () => {
    
    	wp.blocks.registerBlockStyle( 'core/heading', {
    		name: 'default',
    		label: 'Default',
    		isDefault: true,
    	} );
    
    	wp.blocks.registerBlockStyle( 'core/heading', {
    		name: 'alt',
    		label: 'Alternate',
    	} );
    
    } );
    

    When a style is selected, a class of .is-style-{name} is applied to the block. So in my stylesheet, the h2 { } has my default styling and h2.is-style-alt { } has my alternate style.

    Why register a default style?

    The block style selector lets you select which style you want applied, but does not let you unselect one after you’ve picked a style. If you register just one style and then select it for a block, there’s no way to unselect that block style option and return back to the default styling.

    I register an extra style option called “Default” or “Regular”, but don’t add any specific styles for that style option. When a user selects the “Default” style for an h2, it uses the h2 {} styling rather than a more specific h2.is-style-default{ }.

    If you add isDefault: true then this style will be marked active on blocks that don’t already have a style specified.

    Removing block styles

    When styling buttons in the Gutenberg block editor, I usually don’t want the style options that come standard with WordPress: rounded, square, and outline.

    You can use unregisterBlockStyle to remove existing block styles from a block. Add the following to the editor.js file described above:

    wp.domReady( () => {
    	wp.blocks.unregisterBlockStyle( 'core/button', 'default' );
    	wp.blocks.unregisterBlockStyle( 'core/button', 'outline' );
    	wp.blocks.unregisterBlockStyle( 'core/button', 'squared' );
    } );

    I’ll often remove the defaults and then load my own button style options:

    wp.domReady( () => {
    	wp.blocks.unregisterBlockStyle( 'core/button', 'default' );
    	wp.blocks.unregisterBlockStyle( 'core/button', 'outline' );
    	wp.blocks.unregisterBlockStyle( 'core/button', 'squared' );
    
    	wp.blocks.registerBlockStyle( 'core/button', {
    		name: 'default',
    		label: 'Default',
    		isDefault: true,
    	});
    
    	wp.blocks.registerBlockStyle( 'core/button', {
    		name: 'full-width',
    		label: 'Full Width',
    	} );
    
    } );
    

    List of block names

    You need to know the full block name in order to attach or remove styles from it. Here’s a list of all the core blocks (excluding all the embed ones):

    • core/paragraph
    • core/image
    • core/heading
    • core/gallery
    • core/list
    • core/quote
    • core/audio
    • core/cover
    • core/file
    • core/video
    • core/preformatted
    • core/code
    • core/freeform
    • core/html
    • core/pullquote
    • core/table
    • core/verse
    • core/button
    • core/columns
    • core/media-text
    • core/more
    • core/nextpage
    • core/separator
    • core/spacer
    • core/shortcode
    • core/archives
    • core/categories
    • core/latest-comments
    • core/latest-posts

    Finding a block’s name

    A simple way to find the block name is to insert the block in a page and then print out an escaped version of post_content to see what’s stored.

    I’m using ea_pp() below (code here) but you could also use print_r().

    /**
     * Display Post Blocks 
     *
     */
    function ea_display_post_blocks() {
    	global $post;
    	ea_pp( esc_html( $post->post_content ) );
    }
    add_action( 'wp_footer', 'ea_display_post_blocks' );
  • Gutenberg Color Palette and Button Styling

    The Editor Color Palette allows content editors to style their blocks using the theme’s color scheme.

    When a custom color palette is registered in your theme, WordPress will use CSS classes for styling elements rather than hardcode the color in the content.

    There are two aspects to setting up your custom color palette:

    1. Register the color palette options in functions.php
    2. Add css for each color option in your stylesheet

    I’ll also show you how I style the button block to use the color palette.

    Registering your colors

    In your theme’s functions.php, add support for the custom color palette by specifying the following for each color option:

    • Name: visible to user
    • Slug: used in CSS class
    • Color: used for rendering elements in the Gutenberg block editor

    Here’s an example from my Genesis starter theme:

    add_theme_support( 'editor-color-palette', array(
    	array(
    		'name'  => __( 'Blue', 'ea_genesis_child' ),
    		'slug'  => 'blue',
    		'color'	=> '#59BACC',
    	),
    	array(
    		'name'  => __( 'Green', 'ea_genesis_child' ),
    		'slug'  => 'green',
    		'color' => '#58AD69',
    	),
    	array(
    		'name'  => __( 'Orange', 'ea_genesis_child' ),
    		'slug'  => 'orange',
    		'color' => '#FFBC49',
    	),
    	array(
    		'name'	=> __( 'Red', 'ea_genesis_child' ),
    		'slug'	=> 'red',
    		'color'	=> '#E2574C',
    	),
    ) );

    After adding that to your theme, you should see the following in the Color Settings panel:

    Naming your color options

    I’m using the actual color as the slug, which I think is the best approach for individual client sites. A few years from now we may build a new theme that looks totally different, but their color scheme for brand colors will remain unchanged.

    An alternative approach is to use generic terms for the colors like primary, secondary, and tertiary. I think this is the best approach for widely distributed themes, like premium themes by StudioPress. When a user changes themes, the actual colors will change to match that theme’s style guide, allowing a seamless transition between themes.

    Remove custom color picker

    WordPress includes a custom color picker after our theme colors. We can disable color picker and limit them to only our color options with this:

    // -- Disable Custom Colors
    add_theme_support( 'disable-custom-colors' );
    

    Your Color Settings panel should now match the screenshot at the top of this article.

    CSS for your color palette

    If you select the blue color as the Background Color for a block, WordPress adds .has-background and .has-blue-background-color to the block.

    If you select blue as the Text Color, WordPress sets the classes to .has-text-color and .has-blue-color.

    Your theme needs to provide styling for these color classes. Example:

    .has-blue-color {
    	color: #59BACC;
    }
    
    .has-blue-background-color {
    	background-color: #59BACC;
    }
    
    .has-green-color {
    	color: #58AD69;
    }
    
    ...

    Using SASS

    You can automate this with SASS by defining an array of colors and looping over it.

    // Gutenberg color options
    // -- see editor-color-palette in functions.php
    $colors: (
    	'blue' 		: #59BACC,
    	'green' 	: #58AD69,
    	'orange' 	: #FFBC49,
    	'red' 		: #E2574C,
    );
    
    @each $name, $color in $colors {
    
    	.has-#{$name}-color {
    		color: $color;
    	}
    
    	.has-#{$name}-background-color {
    		background-color: $color;
    	}
    }

    Button Styling

    Styling the button block can be difficult given all the styling options available to editors. Hopefully soon we’ll be able to reduce the color options for the button block.

    My goals with styling buttons are:

    1. Make the default button block match all other buttons throughout the site
    2. Provide appropriate hover styling based on button background color.
    3. Provide appropriate text color styling if editor hasn’t selected one, based on background color.
    4. Remove button styles that aren’t applicable.

    Here’s the default button styling in WordPress:

    I want all buttons to be square, and the default background to be blue (the first color in my $colors array). On hover, the background should get darker and no underline should appear on the text.

    /* Button block
    --------------------------------------------- */
    
    .wp-block-button__link {
    	border-radius: 0;
    
    	&:hover {
    		text-decoration: none;
    	}
    
    	&:not(.has-text-color) {
    		color: white;
    
    		&:hover {
    			color: white;
    		}
    	}
    
    	&:not(.has-background) {
    		$default: nth( nth( $colors, 1 ), 2 ); // first color in $colors array
    		background-color: $default;
    
    		&:hover,
    		&:focus {
    			background-color: darken( $default, 20% );
    		}
    	}
    
    	@each $name, $color in $colors {
    
    		&.has-#{$name}-color:hover,
    		&.has-#{$name}-color:focus {
    			color: $color;
    		}
    
    		&.has-#{$name}-background-color:hover,
    		&.has-#{$name}-background-color:focus {
    			background-color: darken( $color, 20% );
    		}
    	}
    
    }

    Here’s a summary of what’s happening since it can be a bit hard to read the SASS:

    • Button should be square, not rounded
    • On hover, don’t show an underline
    • If the button doesn’t have a text color selected, set the default and hover text color to white.
    • If the button doesn’t have a background color selected, use the first color listed in the $colors array shown above, and set the hover background to be 20% darker.
    • For each color in our color palette:
      • If the button has this text color, ensure the hover state also has this text color
      • If the button has this background color, set the hover state to 20% darker.

    This is just a starting point. You can tweak it as needed to match your style guide. For instance, if the blue button should have a green background color on hover, you could add:

    .wp-block-button__link:not(.has-background):hover,
    .wp-block-button__link.has-blue-background-color:hover {
    	background-color: $green;
    }

    Define alternate hover color

    In the SASS snippet above, we’re setting the button background color to 20% darker on hover: background-color: darken( $color, 20% ); . But often our designs call for a specific color to be used on hover.

    I create two SASS arrays:

    1. The $colors one shown above, that maps the color name to its hex value.
    2. A $color_hover array, which maps the color name to the hover color hex value.
    // Gutenberg color options
    // -- see editor-color-palette in functions.php
    $colors: (
    	'blue' 		: #59BACC,
    	'green' 	: #58AD69,
    	'orange' 	: #FFBC49,
    	'red' 		: #E2574C,
    );
    
    $color_hover: (
    	'blue' 		: #226370,
    	'green' 	: #2a5733,
    	'orange' 	: #a36700,
    	'red' 		: #811d15,
    );
    
    @each $name, $color in $colors {
    
    	.has-#{$name}-background-color:hover {
    		background-color: map_get( $color_hover, $name );
    	}
    }

    Remove Button Block Styles

    The button block includes three “Block Styles” – Rounded, Outline, and Squared. You can switch between by clicking the “Change Block Type” button in the top left corner.

    On most of the sites we build, we don’t want different button styles like this. The style guide calls for buttons to be square or rounded – not a mix of both.

    You can unregister these block styles using JavaScript:

    wp.domReady( () => {
    wp.blocks.unregisterBlockStyle( 'core/button', 'default' );
    wp.blocks.unregisterBlockStyle( 'core/button', 'outline' );
    wp.blocks.unregisterBlockStyle( 'core/button', 'squared' );
    } );
    view raw editor.js hosted with ❤ by GitHub
    <?php
    /**
    * Gutenberg scripts and styles
    * @see https://www.billerickson.net/wordpress-color-palette-button-styling-gutenberg
    */
    function be_gutenberg_scripts() {
    wp_enqueue_script( 'be-editor', get_stylesheet_directory_uri() . '/assets/js/editor.js', array( 'wp-blocks', 'wp-dom' ), filemtime( get_stylesheet_directory() . '/assets/js/editor.js' ), true );
    }
    add_action( 'enqueue_block_editor_assets', 'be_gutenberg_scripts' );
    view raw functions.php hosted with ❤ by GitHub

    For more information see Block Styles in Gutenberg.

    Share your tips

    How have you been using the color palette in your theme? Any tips or tricks on styling different blocks? Please share them in the comments below.

Bill Erickson

Bill Erickson is a freelance WordPress developer and a contributing developer to the Genesis framework. For the past 14 years he has worked with attorneys, publishers, corporations, and non-profits, building custom websites tailored to their needs and goals.

Ready to upgrade your website?

I build custom WordPress websites that look great and are easy to manage.

Let's Talk