This is the first post in a series of handling post formats for theme developers. Throughout the series, I plan to offer solutions for more standardization between themes for handling post formats. This series will not be a complete guide on using this feature, but I hope it at least gives theme developers some neat ideas on implementing post formats in a portable fashion.
In this first post of a series on handling post formats for theme developers, I’d like to present to you an option for formatting the “chat” post format.
The chat post format
Since all WordPress post formats are a part of a standard, we should look at WordPress.org’s definition for the “chat” post format:
chat – A chat transcript, like so:
John: foo Mary: bar John: foo 2
That’s pretty simple. As theme developers, we should expect the chat text to be entered into the post content editor in that manner.
The forming of a solution
David Chandra started an interesting discussion on the Theme Hybrid support forums that led to an awesome collaboration of code.
Before we really got started on the code, there was one major feature I wanted to see handled before calling it a “solution”: We must allow more than two speakers. Most of the solutions I’d seen for chats only handled two speakers (for example, alternating paragraph colors with CSS). I wanted to be able to have 100s of speakers, each with their own unique classes for styling in a theme. It makes sense, right? Not all chats are between two speakers.
David went to work hashing out a solution. Honestly, he did all the dirty work. I just cleaned it up and put it in a pretty package.
The final output
The following is a screenshot of a demo I put together for this feature with a little styling thrown in. As you can see, it has multiple speakers, each with a custom color to differentiate them.

The code
The following is the final PHP code we came up with for copying to a theme’s functions.php file.
/* Filter the content of chat posts. */
add_filter( 'the_content', 'my_format_chat_content' );
/* Auto-add paragraphs to the chat text. */
add_filter( 'my_post_format_chat_text', 'wpautop' );
/**
* This function filters the post content when viewing a post with the "chat" post format. It formats the
* content with structured HTML markup to make it easy for theme developers to style chat posts. The
* advantage of this solution is that it allows for more than two speakers (like most solutions). You can
* have 100s of speakers in your chat post, each with their own, unique classes for styling.
*
* @author David Chandra
* @link http://www.turtlepod.org
* @author Justin Tadlock
* @link http://justintadlock.com
* @copyright Copyright (c) 2012
* @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* @link http://justintadlock.com/archives/2012/08/21/post-formats-chat
*
* @global array $_post_format_chat_ids An array of IDs for the chat rows based on the author.
* @param string $content The content of the post.
* @return string $chat_output The formatted content of the post.
*/
function my_format_chat_content( $content ) {
global $_post_format_chat_ids;
/* If this is not a 'chat' post, return the content. */
if ( !has_post_format( 'chat' ) )
return $content;
/* Set the global variable of speaker IDs to a new, empty array for this chat. */
$_post_format_chat_ids = array();
/* Allow the separator (separator for speaker/text) to be filtered. */
$separator = apply_filters( 'my_post_format_chat_separator', ':' );
/* Open the chat transcript div and give it a unique ID based on the post ID. */
$chat_output = "\n\t\t\t" . '<div id="chat-transcript-' . esc_attr( get_the_ID() ) . '" class="chat-transcript">';
/* Split the content to get individual chat rows. */
$chat_rows = preg_split( "/(\r?\n)+|(<br\s*\/?>\s*)+/", $content );
/* Loop through each row and format the output. */
foreach ( $chat_rows as $chat_row ) {
/* If a speaker is found, create a new chat row with speaker and text. */
if ( strpos( $chat_row, $separator ) ) {
/* Split the chat row into author/text. */
$chat_row_split = explode( $separator, trim( $chat_row ), 2 );
/* Get the chat author and strip tags. */
$chat_author = strip_tags( trim( $chat_row_split[0] ) );
/* Get the chat text. */
$chat_text = trim( $chat_row_split[1] );
/* Get the chat row ID (based on chat author) to give a specific class to each row for styling. */
$speaker_id = my_format_chat_row_id( $chat_author );
/* Open the chat row. */
$chat_output .= "\n\t\t\t\t" . '<div class="chat-row ' . sanitize_html_class( "chat-speaker-{$speaker_id}" ) . '">';
/* Add the chat row author. */
$chat_output .= "\n\t\t\t\t\t" . '<div class="chat-author ' . sanitize_html_class( strtolower( "chat-author-{$chat_author}" ) ) . ' vcard"><cite class="fn">' . apply_filters( 'my_post_format_chat_author', $chat_author, $speaker_id ) . '</cite>' . $separator . '</div>';
/* Add the chat row text. */
$chat_output .= "\n\t\t\t\t\t" . '<div class="chat-text">' . str_replace( array( "\r", "\n", "\t" ), '', apply_filters( 'my_post_format_chat_text', $chat_text, $chat_author, $speaker_id ) ) . '</div>';
/* Close the chat row. */
$chat_output .= "\n\t\t\t\t" . '</div><!-- .chat-row -->';
}
/**
* If no author is found, assume this is a separate paragraph of text that belongs to the
* previous speaker and label it as such, but let's still create a new row.
*/
else {
/* Make sure we have text. */
if ( !empty( $chat_row ) ) {
/* Open the chat row. */
$chat_output .= "\n\t\t\t\t" . '<div class="chat-row ' . sanitize_html_class( "chat-speaker-{$speaker_id}" ) . '">';
/* Don't add a chat row author. The label for the previous row should suffice. */
/* Add the chat row text. */
$chat_output .= "\n\t\t\t\t\t" . '<div class="chat-text">' . str_replace( array( "\r", "\n", "\t" ), '', apply_filters( 'my_post_format_chat_text', $chat_row, $chat_author, $speaker_id ) ) . '</div>';
/* Close the chat row. */
$chat_output .= "\n\t\t\t</div><!-- .chat-row -->";
}
}
}
/* Close the chat transcript div. */
$chat_output .= "\n\t\t\t</div><!-- .chat-transcript -->\n";
/* Return the chat content and apply filters for developers. */
return apply_filters( 'my_post_format_chat_content', $chat_output );
}
/**
* This function returns an ID based on the provided chat author name. It keeps these IDs in a global
* array and makes sure we have a unique set of IDs. The purpose of this function is to provide an "ID"
* that will be used in an HTML class for individual chat rows so they can be styled. So, speaker "John"
* will always have the same class each time he speaks. And, speaker "Mary" will have a different class
* from "John" but will have the same class each time she speaks.
*
* @author David Chandra
* @link http://www.turtlepod.org
* @author Justin Tadlock
* @link http://justintadlock.com
* @copyright Copyright (c) 2012
* @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* @link http://justintadlock.com/archives/2012/08/21/post-formats-chat
*
* @global array $_post_format_chat_ids An array of IDs for the chat rows based on the author.
* @param string $chat_author Author of the current chat row.
* @return int The ID for the chat row based on the author.
*/
function my_format_chat_row_id( $chat_author ) {
global $_post_format_chat_ids;
/* Let's sanitize the chat author to avoid craziness and differences like "John" and "john". */
$chat_author = strtolower( strip_tags( $chat_author ) );
/* Add the chat author to the array. */
$_post_format_chat_ids[] = $chat_author;
/* Make sure the array only holds unique values. */
$_post_format_chat_ids = array_unique( $_post_format_chat_ids );
/* Return the array key for the chat author and add "1" to avoid an ID of "0". */
return absint( array_search( $chat_author, $_post_format_chat_ids ) ) + 1;
}
Feel free to break this and build from it. If it’s not a complete solution for you, it should at least be a good starting point.
How to add text to chat posts
Following the example from WordPress.org, you’d enter your chat transcript directly in the post content editor like so:
John: foo
Mary: bar This is an example of a WordPress page, you could edit this to put information about yourself or your site so readers know where you are coming from.
John: foo 2 What: is: this:
Smith: Aye, matey!
Joe: Huh?
Mary: bar 2
You can also post it without the spaces between each speaker. I just prefer it the above way for readability.
How chat posts are formatted
Given the above example, you’d get the following output in your HTML source code.
<div id="chat-transcript-562" class="chat-transcript">
<div class="chat-row chat-speaker-1">
<div class="chat-author chat-author-john vcard"><cite class="fn">John</cite>:</div>
<div class="chat-text"><p>foo</p></div>
</div><!-- .chat-row -->
<div class="chat-row chat-speaker-2">
<div class="chat-author chat-author-mary vcard"><cite class="fn">Mary</cite>:</div>
<div class="chat-text"><p>bar This is an example of a WordPress page, you could edit this to put information about yourself or your site so readers know where you are coming from.</p></div>
</div><!-- .chat-row -->
<div class="chat-row chat-speaker-1">
<div class="chat-author chat-author-john vcard"><cite class="fn">John</cite>:</div>
<div class="chat-text"><p>foo 2 What: is: this:</p></div>
</div><!-- .chat-row -->
<div class="chat-row chat-speaker-3">
<div class="chat-author chat-author-smith vcard"><cite class="fn">Smith</cite>:</div>
<div class="chat-text"><p>Aye, matey!</p></div>
</div><!-- .chat-row -->
<div class="chat-row chat-speaker-4">
<div class="chat-author chat-author-joe vcard"><cite class="fn">Joe</cite>:</div>
<div class="chat-text"><p>Huh?</p></div>
</div><!-- .chat-row -->
<div class="chat-row chat-speaker-2">
<div class="chat-author chat-author-mary vcard"><cite class="fn">Mary</cite>:</div>
<div class="chat-text"><p>bar 2</p></div>
</div><!-- .chat-row -->
</div><!-- .chat-transcript -->
Styling chats
As you can see from the previous section, the source code gives you quite a few ways to style things:
/* Wrapper for entire chat transcript. */
.chat-transcript { }
/* Individual section of the chat. */
.chat-row { }
/* Individual speaker in the chat. */
.chat-speaker-xxx { }
/* Chat author. */
.chat-author { }
.chat-author cite { }
/* Chat text. */
.chat-text { }
There are some other useful classes and IDs to use from the code, but these would be the most common for styling.
Actually use chat transcripts
One of the post formats that theme developers tend to shy away from is the chat format. There’ve been no great solutions in the past, so I hope this helps in implementing something that’s useable for you in theme development. Please feel free to tinker with this code, break it, and send me fixes/enhancements you’d like to see thrown in.
Also, be sure to thank David Chandra for getting me started on this. And, check out his theme site, Genbu Theme (he’s redeveloping the theme to use my Hybrid Core framework).
Wow! This is amazing. I did try something similar in the past, but it didn’t work for me for some reason. I like your approach and I think it can work. However, you should consider prefixing actions and filters, for the same reason you prefix functions and classes.
Cheers and keep up the awesome stuff!
I knew I was forgetting something when I was going through and renaming stuff. I’ll be sure to update and prefix those hooks.
I love the idea but I really don’t think this should be in a theme; it should be built as a plugin designed to complement a theme. Imagine what would happen if someone populated their site with tons of “chat” posts and then switched themes: all of their chats would be broken (missing formatting at least).
I actually tend to agree. However, the specification for the handling of post formats, as defined by WordPress, is that it is a theme feature.
This has been one of my issues from the beginning about post formats. Some themes will support a certain feature; others will not. There’s no real portability when themes are handling post formats in different ways or not supporting them at all. Well, the data is portable, but how the data is displayed is a different story.
The reason for this series of tutorials is to start forming a real standard for theme developers beyond a few notes in the Codex. I’ll also probably wrap up the final code into a plugin. I’ve got several more ideas on the way for other formats though, so I’ll wait for feedback on those before finalizing anything into a plugin.
I believe post types were originally built as a “theme” feature too, as were post thumbnails, but obviously they are both heavily used by plugins. I don’t see any reason post formats should be any different, since they are part of the “functionality” of a site.
The series is a fantastic idea and I really love where it has started!
P2 theme breaks the plugin/theme rule divide and it was created by Auttomatic. I think it’s a matter of using your best judgement.
Funny you should mention it, but I’m working on a a plugin like that. https://github.com/bungeshea/post-formats
I never even knew there was a chat function… thanks!
I love the filter approach (and I’ve really never seen a proper implementation of the post format in the wild), but the markup seems like a lost opportunity for my very favorite woefully underused element, the definition list — it’d save you a bunch of divs and classes, not to mention that it’s semantically perfect for pairs of names and values!
According to this article on 24ways you shouldn’t use definition lists for dialogues and explains why.
I was thinking the same thing yesterday when I saw the code.
In the past, I would have agreed. But, someone pointed me to this a few months ago (I think this was a conversation on the theme review mailing list):
This is from the W3C Working Draft for HTML5 (reference).
Aaugh! Curse you, W3C!!
Very interesting … and somewhat timely as I am looking at how to implement an appropriate “Chat” format in one of my current projects.
Very handy. I’ve just been working on somewhat customised post formats for a blog and I never thought, “Oh, chats, they can be between more than two people, no?”
I also marked it up using definition lists, and was really surprised at the Working Draft you posted, as I remember from somewhere suggested use for dl was e.g. marking up a script with dialogue.
(HTML4 and XHTML2 working drafts both say “Another application of dl, for example, is for marking up dialogues, with each dt naming a speaker, and each dd containing his or her words.”)
This would make for a great plugin as well.
After I wrap up this series of tutorials, I’ll probably port most of the code over to a plugin or as a “drop-in” library for themes to use. Probably both.
I definitely know it’ll be added to the Hybrid Core framework.
My handling of chats in my Formattd theme was a little more simplistic, but had a similar idea:
https://github.com/dougalcampbell/Formattd/blob/master/functions.php#L639
It generates markup like
<p class="chat"><span class="person person-bob">Bob:</span> Hi, Alice!</p>I thought I was being pretty clever with my ‘person-NAME’ classes, at the time. But ‘speaker-N’ is much more practical in real use.
I had done something similar with the
person-nameclass like that for a custom project. It worked fine for that project, but I knew it wouldn’t work for a public theme for general styling.This filter approach is much more powerful and you’ve both accounted for the fact that their might be colons within chat content.
I’ve just been working with the styles – never occurred to me to use a filter.
thank you justin.
you are too kind.
my original code is very messy, and you clean it, and optimize it.
it’s readable now
can’t wait for the next series.
Theme Hybrid forum is the best WordPress forum.
awesome.
All code is usually very messy when we first write it. Cleaning it up is just a natural part of the process.
Thanks for letting me share this with the WordPress community.
This is great! One observation, however: you have the separator hard-coded into line 51, although you’ve made provision for filtering it earlier. Shouldn’t line 51 be:
instead of
Or am I missing something?
If this is true, I can’t believe I actually found a bug in the mighty Justin Tadlock’s code!
Hey, I find bugs in my code all the time.
You did find a bug though. I’ll update the post.
Are you planning to post a guideline to implementing the link post format soon? I’d really appreciate it.
I’m confused, is this just comments made to look a little more like a chat room conversation?
Wow! This is awesome! This saved me hours of thinking and getting a headache while doing so
Best regards,
Benni