Building my Portfolio


I received the following email from a developer yesterday.

As a newcomer to the web world and genesis included, I’m attempting to learn from others and looking at great designs that inspire me. This brings me to my question. Would you be willing to share your experience or even go as far as sharing your code for your custom designed portfolio page here?

After writing out my response, I figured others might be interested as well. Here’s the code, followed by a description of what each function does below.

First, the functions.php file:

  • be_portfolio_query() customizes the query on the portfolio pages. This must go in functions.php since it needs to run before main query, which happens before the template is determined. More information on customizing the query.

I specify the post type so the projects show up on the taxonomy archive pages AND so that the archive-projects.php file is used for the taxonomy archive pages. I could’ve also done this second part using the template_include filter.

  • be_portfolio_body_class() adds a consistent body class to all the pages using this template, for styling (if I didn’t use this, I’d have to write styles with and )
  • be_collect_testimonials(), I wanted to display a testimonial between certain projects, and wanted that testimonial to be from one of the projects featured on this page. So before the projects are listed, I loop through them all and pull out all the testimonials. I can then use this in a function lower down in the page
  • be_sort_projects() adds the links before the project listing that lets you go from the main portfolio (all projects) to one of the taxonomy archives
  • be_project_post_classes() – This looks at the loop counter and adds column classes to the posts, breaking it into multiple columns. See this tutorial for more information.
  • be_outer_wrap() and be_outer_wrap_close() add a wrapping div around all the posts and testimonials so they can be cleared with CSS
  • be_project_archive_content() is the content of each project. It has the image and then displays the taxonomy terms (Project Type) below it
  • be_project_divider() adds a dividing line after every 3 posts, except after the 6th one (where a testimonial is the divider)
  • be_project_quote() adds a testimonial after the 6th project (it’s #5 since it starts counting at 0) OR after the last project if there are less than 6. I also have it choosing different testimonials for different taxonomies so the same testimonial isn’t used on every page


  1. says

    So there I am, just redone my portfolio, all snug and happy and then Bill comes with one of his great posts again :-) should I restart?

    Thanks for taking the time to share, your are my top Genesis guru ! although when I look at your beard on the twitter avatar, you seem to work at looking like the first WordPress prophet…

    Just noticed before posting this.. is the “Notify me of new posts by email” custom code as well ?

  2. says

    Thanks so much Bill – I honestly didn’t expect this detailed of a response when I sent you that email. So generous of you :)

    Now it’s time for me to work my way through the code and see what I come up with. Let the fun begin!

    Thanks again Bill.

  3. says


    I cannot style the “active” taxonomy with CSS on my portfolio page. I have structured my CSS in the same way with the .sort-works li.(tax-slug).active a, but I cannot get the menu item to style differently when the current page is active.

    Here are my functions:

    css snippet:
    .sort-works a,
    .sort-works li.available-works a:hover {
    background: #000000;

  4. Richard Buff says

    I’m working on a similar portfolio style setup as this and I’ve looked over this code line by line and I love it. Very practical and effective.

    One question. Why is it necessary to have the wpautop in “echo wpautop( $terms_output );”

    I understand why you use wpautop on the testimonials, which would have line breaks in their content, but why does the terms output need it?

    • Bill Erickson says

      Because I wanted the links (inline elements) wrapped in a paragraph (block level element)

      • Richard Buff says

        So is that an undocumented feature of wpautop? Adding a wrapping paragraph around whatever you feed it? To me at least ,the Codex seems to imply that it only replaces double line breaks with a p tag, like it would in your testimonial content. I don’t see anything to suggest that I can feed it my own HTML (which has no double line breaks in it) and it will output my HTML wrapped in a paragraph tag.

        Is there a reason, other than perhaps the interest of cleaner code or simplicity, that you chose to use wpautop and not to simply echo an opening p tag, echo the $terms_output, and then echo a closing p tag?

        • Bill Erickson says

          I guess it’s an undocumented feature. If you pass it content, it will wrap that content in paragraph tags unless it matches certain elements that shouldn’t be wrapped (table, ol, ul, li…). If two line breaks are found, it ends the first paragraph and starts a new one there.

          I just think wpautop looks cleaner than opening and closing a paragraph tag.

          • Richard Buff says

            After thinking my last comment over and reading your response, it makes a lot more sense now. Conceptually I think I was approaching it wrong. Thinking of it as wrapping whatever content you give it in a paragraph tag (with exceptions) and then replacing any double line break with a close and open paragraph tag makes it “click” better in my head. Thanks Bill.

  5. Richard Buff says

    Hi Bill,

    Since here you’re storing testimonials in a meta box for each project post type, I was wondering if:
    1) Your actual testimonials page,, is built dynamically or if you just edit it directly in the page editor.
    2) For the testimonials that appear on your service pages, if you setup metaboxes for them or just manually inserted them.
    3) And one final unrelated question: Is your “Scheduling projects for” date value manually edited or do you store things like that in the options table, since it looks like it appears in two different variations?

    • Bill Erickson says

      1. Testimonials page is built dynamically by querying my projects and pulling out the testimonials.

      2. Testimonials on service pages are dynamic as well. I query for a project in that service (there’s a Service taxonomy for projects).

      3. Yes, my availability date is stored in the options table as a UNIX timestamp, so I can then display it however I like.

  6. says

    Awesome, I felt like if i had written an email to you than that would look same as that you pointed at beginning.
    I was struggling to create my portfolio and cames across to this post. Thanks Google and thanks Bill.

  7. says


    This is great stuff, thanks for posting it!

    It looks like this code works for inserting one testimonial after the 6th project, but not any further than that. Do you have an updated version that you are using on your portfolio that displays more than one testimonial? Just curious, since it looks like your portfolio page is a single page instead of multiple with 18 projects per page as outlined above. I imagine you would use the $count and pull the testimonial based on the $count divided by 6?

    I had to add similar !empty checks for each if( is_tax() ) statement in the portfolio_quote function, similar to the parent if statement, to avoid errors on the taxonomy pages: This is probably just because I was playing around with it an didn’t have enough projects in the portfolio.

    Thanks again!

Leave A Reply