a Custom Archive With a Neat Fade Effect.

a Custom Archive With a Neat Fade Effect.

Custom Post Type Archive with a jQuery Fade Effect
click to enlarge
A few years ago I had a project for a company with about twenty staff, all of whom needed to be on the site’s staff page, each with a photo and bio. It was going to be a long page no matter what I did, but I did some looking around and discovered a neat trick I could use to shorten it up quite a lot.

Warning: that doesn’t mean this is a short post. It’s actually rather long. With lots of code. But if you like code and want to make this happen, it’s a good read.

The thumbnails run down the left side of the page and only one bio shows at a time. When you click on a new thumbnail, a little animation magic happens to fade out one bio and bring in another. It’s quite pretty and surprisingly easy to do.

At that time, the only way I knew how to do it was by hand. It worked, and it looked great, but involved so. much. work; was a nightmare to update; and involved custom styling for each and every background image, which had to be created by hand. It was a project that I often thought of and wished I could revisit so I could figure out an easier way to set it up.

Over the past year, I’ve worked a lot more with custom post types and custom loops, and the wheels began turning. Finally, I knew how to tackle this. It still takes a bit of setup, but it’s brilliantly simple to maintain once you hand it over to the person who will be handling the content itself. I just put it into action on the Signal Mountain Student Ministry website.

Today, I’m going to walk you through it! (You can also skip straight to the gist!)

This tutorial assumes you’re setting this up with a Genesis child theme using HTML5. It should be totally possible to use this with any theme, HTML5 or not, but would obviously require some revision.

Set up your Custom Post Type

The first step is to get your custom post type set up. I created one for Staff and tucked it in the mu-plugins folder for two reasons: 1) I don’t want it to be tied to the theme, and 2) I don’t want someone who isn’t paying attention to accidentally turn it off.

/** Custom Post Types: place this file in mu-plugins to carry through from one theme to the next, or add this code directly to your functions.php */
add_action( 'init', 'rgc_staff_post_type' );
function rgc_staff_post_type() {
register_post_type( 'staff',
'labels' => array(
'name' => __( 'Staff', 'rgc' ),
'singular_name' => __( 'Staff', 'rgc' ),
'exclude_from_search' => false,
'has_archive' => true,
'hierarchical' => true,
'public' => true,
'rewrite' => array( 'slug' => 'staff' ),
'supports' => array( 'title', 'editor', 'thumbnail', 'genesis-cpt-archives-settings', 'page-attributes', 'genesis-seo' ),
view raw rgc_cpt.php hosted with ❤ by GitHub

It’s pretty straightforward. Notice that it supports the featured image as well as the 'genesis-cpt-archives-settings', both of which are important. You could get by without the custom archive settings but if you want to have an intro or custom SEO settings on the staff page, you’ll need it. And I love having them. Supporting the page attributes is also key for this situation, where you want to have total control over the order of your thumbnails.


My super awesome brother helped me with this script when I needed it originally, and it needed only a minor adjustment for this go ronud. Save it in your child theme’s /lib/js/ folder, or someplace similar (I’d create a js folder if you don’t have one).

jQuery(document).ready( function() {
jQuery('.bio_thumbs').mouseover(function () {
}).mouseout(function () {
}).click(function() {
var bio = jQuery(this).attr('id');
if (jQuery('#' + bio + '_desc').hasClass('active_desc')) {
// Do Nothing
else {
jQuery('.active_desc').fadeOut(400, function() {
jQuery('#' + bio + '_desc').fadeIn(400);
jQuery('#' + bio + '_desc').addClass('active_desc');
view raw avenger.js hosted with ❤ by GitHub

Call It In Your Functions

You’ve got two of your big keys in place, so now you need to call them into action through your theme’s functions file. We’re enqueueing the script and (optionally) adding a widget area which will display after the content on the staff archive page. You could use it for a contact form, for example.

// Add this to your theme's functions file to call the javascript and set up the widget area.
//* Required Scripts
add_action ( 'wp_enqueue_scripts', 'rgc_scripts' );
function rgc_scripts() {
if ( is_post_type_archive( 'staff' ) ) {
wp_enqueue_script( 'rgc-staff', get_stylesheet_directory_uri() . '/lib/js/avenger.js', array( 'jquery' ) );
// Register Widget Areas
genesis_register_sidebar( array(
'id' => 'after-staff',
'name' => __( 'After Staff', 'rgc' ),
'description' => __( 'This is the after staff section.', 'rgc' ),
) );
// Hooks after-staff widget area to Staff Page
add_action( 'genesis_after_loop', 'rgc_after_staff' );
function rgc_after_staff() {
if ( is_post_type_archive( 'staff' ) && is_active_sidebar( 'after-staff' ) ) {
echo '<div class="after-staff"><div class="wrap">';
dynamic_sidebar( 'after-staff' );
echo '</div></div>';
view raw functions.php hosted with ❤ by GitHub

The Staff Archive Page Itself

Obviously, we also need to set up a custom archive page (called archive-staff.php … if your custom post type is called something else, you’ll need to change this). This involves removing the standard loop and setting up a custom one to run not once, but twice.

The first part is setting up the arguments. All staff must show, even if there are more than allowed by your site settings. Additionally, we want to sort them by menu_order, which we set up in the Page Attributes (0 is first, etc. I usually go by fives so it’s super easy to add someone new if they are coming in above others).

The first loop is inside the thumbs div (which isn’t styled yet). It displays only the featured image from each post with the title. There is an if/else statement here because the first staff member is “active”, while the others are not–only the one that is clicked is “active”. Notice the different class on the if, as opposed to the else. Also, notice the id="' . get_the_ID() . '" in each part, which gives us something tangible to tie the thumbnails to their respective bios.

It’s really important to include the rewind_posts(); line so that the second loop starts at the beginning as well.

The second loop contains the title and the actual bio (or content). The setup is very similar to the thumbs, including that id–since both are using the actual post ID, it guarantees that each div will have a unique id which matches its respective thumb/bio.

//* Remove the entry meta in the entry header
remove_action( 'genesis_entry_header', 'genesis_post_info', 12 );
remove_action( 'genesis_entry_footer', 'genesis_post_meta' );
remove_action ('genesis_loop', 'genesis_do_loop'); // Remove the standard loop
add_action( 'genesis_loop', 'custom_do_loop' ); // Add custom loop
function custom_do_loop() {
// Intro Text (from page content)
printf( '<article %s>' , genesis_attr( 'entry') );
global $post;
// arguments, adjust as needed
$args = wp_parse_args(
genesis_get_custom_field( 'query_args' ),
'post_type' => 'staff',
'posts_per_page' => '-1',
'post_status' => 'publish',
'cat' => $include,
'orderby' => 'menu_order',
'order' => 'ASC',
'paged' => get_query_var( 'paged' ) )
global $wp_query;
$wp_query = new WP_Query( $args );
echo '<div class="thumbs">';
if ( have_posts() ) :
while ( have_posts() ) : the_post();
if( 0 == $wp_query->current_post ) {
echo '<div class="bio_thumbs active_bio_thumb" id="' . get_the_ID() . '">';
else {
echo '<div class="bio_thumbs" id="' . get_the_ID() . '">';
echo get_the_post_thumbnail( $post->ID, 'thumbnail' );
echo get_the_title(); // show the title
echo '</div>';
do_action( 'genesis_after_endwhile' );
echo '</div>'; //* end .thumbs
if ( have_posts() ) :
while ( have_posts() ) : the_post();
if( 0 == $wp_query->current_post ) {
echo '<div class="bio_desc active_desc" id="' . get_the_ID() . '_desc">';
else {
echo '<div class="bio_desc" id="' . get_the_ID() . '_desc" style="display:none;">';
echo '<h2>' . get_the_title() . '</h2>'; // show the title
echo '</div>';
do_action( 'genesis_after_endwhile' );
view raw archive-staff.php hosted with ❤ by GitHub

Give It Some Style

The final step is the easiest, right? We want to make sure that our new divs all behave as intended, and that our thumbs have some nifty opacity, etc. Additionally, we’re going to add some rules to our media queries to make sure that everything resizes as intended on smaller browser windows and that the thumbs rearrange altogether on mobile devices.

Staff Archive Page
---------------------------------------------------------------------------------- */
.thumbs {
float: left;
width: 100px;
.bio_thumbs {
color: #000;
cursor: pointer;
margin-bottom: 15px;
opacity: .75;
text-align: center;
.hover_bio_thumb {
opacity: .9;
.active_bio_thumb {
opacity: 1;
.bio_desc {
float: right;
text-align: justify;
width: 540px;
.bio_desc.active_desc {
display: block;
.after-staff {
clear: both;
padding-top: 20px;
@media only screen and (max-width: 1139px) {
.bio_desc {
width: 435px;
@media only screen and (max-width: 1023px) {
.bio_desc {
width: 100%;
.bio_thumbs {
float: left;
width: 100px;
view raw style.css hosted with ❤ by GitHub

If you’re still with me, congratulations! Now go out and do something with this. I’d love to see your version!

Reader Interactions


  1. Avinash D'Souza says

    Ok, I’m just gonna say that this post is INSANE!!! And by that, I mean, it’s incredibly detailed and helpful…

    Thanks for putting this out there Robin…as a relatively new user of Genesis(old hand of Thesis), the power of the framework still boggles me.

    If you don’t mind, how would I do this if I didn’t want to do a new CPT? Essentially, I wanna do this for my Authors Archive…ergo, I don’t need a new CPT because the Authors taxonomy already exists.

    Again, thanks for the tut! 🙂

    • Robin says

      Hmmm, that’s a great question–and a great use of this kind of page! I am not sure, but I think I’d start by trying to swap out the wp_parse_args with wp_list_authors, maybe, as they seem to accept the same kind of parameters?

      I will guess that you’ll have to explore the related references at the bottom–think get_the_title, etc. may not work. Probably things like get_the_author_link, etc. instead.

      If this approach works, please let me know! Would love to see it in action. Good luck!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.