As of WordPress 3.4, the way custom headers and backgrounds are handled by WordPress is changing. This won’t make any real difference to end-users, but those of us who make themes should being transitioning to the new system immediately. On the current trunk version of WordPress, even the Twenty Eleven theme is serving deprecated errors due to it’s use of the older system.

To make it a little easier to transition to the new systems I’ve put together a short class to help you figure out how it all works. The primary difference is that add_custom_background() and add_custom_header() have been replaced with add_theme_support( ‘custom-header’ ) and add_theme_support( ‘custom-background’ ), plus the old constants we used to define can now be handled by a nice big array sent as an argument to the add_theme_support() calls. This makes for cleaner code and hence is better for all of us :)

I’m not sure who was responsible for making these changes to the core of WordPress, but thank you to whoever you are! I strongly dislike these features being present in core at all, but at least if they are in there, it is nice for them to handled in a more elegant fashion as the old route was a little messy IMHO.

So without further ado, here is a largely untested chunk of code I created this evening for a new theme I’m working on.

<?php
/**
 * WordPress 3.4 Custom Header and Background demo
 *
 */

/**
 * Instantiate the class
 */
new HeaderBackgroundDemo;

/**
 * Demo class for adding custom headers and backgrounds to WordPress
 * 
 * @since 1.0
 * @author Ryan Hellyer <ryan@pixopoint.com>
 */
class HeaderBackgroundDemo {

	/**
	 * Class constructor
	 * Adds all the methods to appropriate hooks
	 * 
	 * @since 1.0
	 * @author Ryan Hellyer <ryan@pixopoint.com>
	 */
	public function __construct() {
		add_action( 'after_setup_theme',     array( $this, 'theme_setup' ) );
	}

	/**
	 * Sets up the custom header and background
	 * 
	 * @since 1.0
	 * @author Ryan Hellyer <ryan@pixopoint.com>
	 */
	public function theme_setup() {

		// Add custom header support
		add_theme_support(
			'custom-header',
			array(
				'default-image'          => '', // Header image default
				'header-text'            => false, // Header text display default
				'default-text-color'     => '', // Header text color default
				'width'                  => '1100', // Header image width (in pixels)
				'flex-width'             => true, // Header flex width changes width into suggested width
				'height'                 => '655', // Header image height (in pixels)
				'flex-height'            => true, // Header flex height changes height into suggested height
				'random-default'         => true, // Header image random rotation default
				'wp-head-callback'       => array( $this, 'header_style' ), // Template header style callback
				'admin-head-callback'    => array( $this, 'admin_header_style' ), // Admin header style callback
				'admin-preview-callback' => array( $this, 'admin_header_image' ) // Admin preview style callback
			)
		);

		// Add support for custom backgrounds
		add_theme_support(
			'custom-background',
			array(
				'default-image'          => '',
				'default-color'          => 'ff0000',
				'wp-head-callback'       => array( &$this, 'custom_background_callback' ),
				'admin-head-callback'    => '',
				'admin-preview-callback' => '',
			)
		);

		// Default custom headers packaged with the theme. %s is a placeholder for the theme template directory URI.
		register_default_headers(
				array(
				'default'           => array(
					'url'           => '%s/images/headers/default.jpg',
					'thumbnail_url' => '%s/images/headers/default-thumbnail.jpg',
					/* translators: header image description */
					'description'   => __( 'Default', 'themedemo' )
				),
				'willow' => array(
					'url'           => '%s/images/headers/willow.jpg',
					'thumbnail_url' => '%s/images/headers/willow-thumbnail.jpg',
					/* translators: header image description */
					'description'   => __( 'Willow', 'themedemo' )
				),
			)
		);

	}

	/**
	 * Styles the header image and text displayed on the blog
	 *
	 * Adapted from Justin Tadlock's tutorial ... http://justintadlock.com/archives/2011/06/08/custom-background-fix-for-theme-developers
	 *
	 * @since 1.0
	 * @author Ryan Hellyer <ryan@metronet.no>
	 */
	public function custom_background_callback() {

		// Get the background image
		$image = get_background_image();

		// If there's an image, just call the normal WordPress callback. We won't do anything here
		if ( !empty( $image ) ) {
			_custom_background_cb();
			return;
		}

		// Get the background color
		$color = get_background_color();

		// If no background color, return
		if ( empty( $color ) )
			return;

		// Use 'background' instead of 'background-color'
		$style = "background: #{$color};";

		echo '<style type="text/css">body {' . trim( $style ) . '}</style>';
	}

	/**
	 * Styles the header image and text displayed on the blog
	 *
	 * @since 1.0
	 * @author Ryan Hellyer <ryan@pixopoint.com>
	 */
	function header_style() {
	
		echo "n<style>";
	
		// Has the text been hidden?
		if ( 'blank' == get_header_textcolor() || '' == get_header_textcolor() ) {
			echo '
	#site-title,
	#site-description {
		display: none;
	}';
		}
	
		// If the user has set a custom color for the text use that
		else {
			echo '
	#site-title a,
	#site-description {
		color: #' . get_header_textcolor() . ' !important;
	}';
		}
	
		echo "n</style>n";
	}
	
	/**
	 * Styles the header image displayed on the Appearance > Header admin panel.
	 *
	 * @since 1.0
	 * @author Ryan Hellyer <ryan@pixopoint.com>
	 */
	function admin_header_style() {
		echo '<style>';
	
		// If the user has set a custom color for the text use that
		if ( get_header_textcolor() != HEADER_TEXTCOLOR ) {
			echo '
		#site-title a,
		#site-description {
			color: #' . get_header_textcolor() . ';
		}';
		}

		// Set image CSS
		echo '
		#headimg img {
			max-width: 1000px;
			height: auto;
			width: 100%;
		}';
	
		echo '</style>';
	}

	/**
	 * Custom header image markup displayed on the Appearance > Header admin panel.
	 *
	 * @since 1.0
	 * @author Ryan Hellyer <ryan@pixopoint.com>
	 */
	function admin_header_image() { ?>
		<div id="headimg">
			<?php
			if ( 'blank' == get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ) || '' == get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ) )
				$style = ' style="display:none;"';
			else
				$style = ' style="color:#' . get_theme_mod( 'header_textcolor', HEADER_TEXTCOLOR ) . ';"';
			?>
			<h1><a id="name"<?php echo $style; ?> onclick="return false;" href="<?php echo esc_url( home_url( '/' ) ); ?>"><?php bloginfo( 'name' ); ?></a></h1>
			<div id="desc"<?php echo $style; ?>><?php bloginfo( 'description' ); ?></div>
			<?php $header_image = get_header_image();
			if ( ! empty( $header_image ) ) : ?>
				<img src="<?php echo esc_url( $header_image ); ?>" alt="" />
			<?php endif; ?>
		</div><?php
	}

}

There are bound to be ways to improve this code, so feel free to offer your opinion in the comments. This is mostly just a brain dump from this evening and I haven’t spent any time optimising it or working out better ways of doing things, so all input is welcome :)