Twenty Seventeen

Here’s a look at how to make a theme AMP-compatible, using Twenty Seventeen as an example.

As usual, reimplementing JavaScript is the most common challenge. AMP doesn’t allow custom JavaScript.

‘Sticky’ Navigation

In Twenty Seventeen, there’s a ‘sticky’ nav menu implemented in JavaScript:

<?php if ( twentyseventeen_is_amp() ) : ?>
	<amp-position-observer
		layout="nodisplay"
		intersection-ratios="1"
		on="exit:navigationTopShow.start;enter:navigationTopHide.start"
		<?php if ( is_admin_bar_showing() ) : ?>
			viewport-margins="32px 0"
		<?php endif; ?>
	></amp-position-observer>
<?php endif; ?>

Notice the on attribute above:

on="exit:navigationTopShow.start;enter:navigationTopHide.start"

The navigationTopShow.start event then triggers an animation that’s set in an amp-animation element. This then displays a new menu, giving the effect of a menu ‘sticking’ to the top.

Sub-menu Buttons

Revealing sub-menu items is another common task in making themes AMP-compatible.

For example, Twenty Seventeen’s functions.js file causes the submenu to expand on clicking a menu item:

The AMP implementation can use amp-state.

It creates an amp-state to store the expanded state:

$item_output .= sprintf(
	'<amp-state id="%s"><script type="application/json">%s</script></amp-state>',
	esc_attr( $expanded_state_id ),
	wp_json_encode( $expanded )
);

This state is like a global JavaScript variable that stores whether that sub-menu is expanded.

On clicking a button to expand a sub-menu section, it toggles that amp-state. For example, if it was original false (unexpanded), it toggles to true.

This then toggles the class  to toggled-on, which determines whether the sub-menu items display. Notice how this class displays and is removed on clicking:

AMP-Specific Styling

The plugin generally handles styling well. AMP URLs will usually look very similar to non-AMP URLs.

But sometimes it’s necessary to add styling for AMP markup.

For example, twentyseventeen/style.css uses the class js, but that class won’t be present in AMP because it doesn’t allow custom JavaScript.

So it can help to have AMP-equivalents for those style rules.
For example, here are the non-AMP selectors:

.js .main-navigation ul,
.js .main-navigation ul ul,
.js .main-navigation > div > ul {
	display: block:
}

And you can apply equivalent AMP selectors so the rule will also apply to AMP URLs:

.js .main-navigation ul,
.js .main-navigation ul ul,
.js .main-navigation > div > ul,
html[amp] .main-navigation ul,
html[amp] .main-navigation ul ul,
html[amp] .main-navigation > div > ul {
	display: block;
}

Body Classes

Themes sometimes add body classes with JavaScript, based on the state of the document.

For example, twentyseventeen/assets/js/global.js adds a has-header-video class when the header video is loaded.

For AMP endpoints, you can add this class with PHP:

/**
 * On AMP endpoints with a header video, add a class to the body classes.
 * 
 * @param array $classes The body classes.
 * @return array $classes The body classes.
 */
add_filter( 'body_class', function( $classes ) {
	if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() && has_header_video() ) {
		$classes[] = 'has-header-video';
	}
	return $classes;
} );