get( 'Version' )
);
}
endif;
add_action( 'enqueue_block_editor_assets', 'serial_editor_ui_styles' );
if ( ! function_exists( 'serial_styles' ) ) :
/**
* Enqueue styles.
*
* @since serial 1.0
* @return void
*/
function serial_styles() {
// Register theme stylesheet.
wp_register_style(
'serial-style',
get_template_directory_uri() . '/style.css',
array(),
wp_get_theme()->get( 'Version' )
);
// Enqueue theme stylesheet.
wp_enqueue_style( 'serial-style' );
// Re-send Jetpack like widget styles when OS color scheme changes.
wp_add_inline_script(
'jetpack_likes_queuehandler',
"window.matchMedia( '(prefers-color-scheme: dark)' ).addEventListener( 'change', function() {
var masterFrame = window.frames[ 'likes-master' ];
if ( ! masterFrame ) {
return;
}
var sdText = document.querySelector( '.sd-text-color' );
var sdLink = document.querySelector( '.sd-link-color' );
var textStyles = sdText ? getComputedStyle( sdText ) : {};
var linkStyles = sdLink ? getComputedStyle( sdLink ) : {};
// Update the cached styles in likes-master.
masterFrame.postMessage(
JSON.stringify( {
type: 'likesMessage',
data: {
event: 'injectStyles',
textStyles: {
color: textStyles.color,
fontFamily: textStyles['font-family'],
fontSize: textStyles['font-size'],
direction: textStyles.direction,
fontWeight: textStyles['font-weight'],
fontStyle: textStyles['font-style'],
textDecoration: textStyles['text-decoration'],
},
linkStyles: {
color: linkStyles.color,
fontFamily: linkStyles['font-family'],
fontSize: linkStyles['font-size'],
textDecoration: linkStyles['text-decoration'],
fontWeight: linkStyles['font-weight'],
fontStyle: linkStyles['font-style'],
},
},
} ),
'*'
);
// Trigger a re-render of each widget so applyInjectedStyles runs with the new styles.
document.querySelectorAll( 'iframe.jetpack-likes-widget' ).forEach( function( iframe ) {
masterFrame.postMessage(
JSON.stringify( {
type: 'likesMessage',
data: {
event: 'loadLikeWidget',
name: iframe.name,
width: iframe.offsetWidth,
},
} ),
'*'
);
} );
} );",
'after'
);
}
endif;
add_action( 'wp_enqueue_scripts', 'serial_styles' );
if ( ! function_exists( 'serial_block_stylesheets' ) ) :
/**
* Enqueue custom block stylesheets
*
* @since serial 1.0
* @return void
*/
function serial_block_stylesheets() {
$serial_styled_blocks = array(
'core/accordion' => 'accordion',
'core/archives' => 'archives',
'core/audio' => 'audio',
'core/button' => 'button',
'core/calendar' => 'calendar',
'core/categories' => 'categories',
'core/comment-content' => 'comment-content',
'core/comments-pagination' => 'comments-pagination',
'core/file' => 'file',
'core/latest-comments' => 'latest-comments',
'core/latest-posts' => 'latest-posts',
'core/list' => 'list',
'core/loginout' => 'loginout',
'core/navigation' => 'navigation',
'core/page-list' => 'page-list',
'core/post-author' => 'post-author',
'core/post-author-name' => 'post-author-name',
'core/post-comments-form' => 'post-comments-form',
'core/post-comments-link' => 'post-comments-link',
'core/post-date' => 'post-date',
'core/post-excerpt' => 'post-excerpt',
'core/post-featured-image' => 'post-featured-image',
'core/post-navigation-link' => 'post-navigation-link',
'core/post-terms' => 'post-terms',
'core/post-title' => 'post-title',
'core/pullquote' => 'pullquote',
'core/query-pagination-numbers' => 'query-pagination-numbers',
'core/query-pagination' => 'query-pagination',
'core/quote' => 'quote',
'core/read-more' => 'read-more',
'core/rss' => 'rss',
'core/search' => 'search',
'core/separator' => 'separator',
'core/site-title' => 'site-title',
'core/social-links' => 'social-links',
'core/table' => 'table',
'core/tag-cloud' => 'tag-cloud',
'core/table-of-contents' => 'table-of-contents',
'core/tabs' => 'tabs',
'core/term-description' => 'term-description',
'core/terms-query' => 'terms-query',
'jetpack/ai-chat' => 'jetpack-ai-chat',
'jetpack/contact-form' => 'jetpack-contact-form',
'jetpack/podcast-episode' => 'jetpack-podcast-episode',
);
foreach ( $serial_styled_blocks as $block_name_with_namespace => $block_name ) {
$block_style_path = get_template_directory() . '/assets/css/blocks/' . $block_name . '.css';
wp_enqueue_block_style(
$block_name_with_namespace,
array(
'handle' => 'serial-' . $block_name,
'src' => get_template_directory_uri() . '/assets/css/blocks/' . $block_name . '.css',
'path' => $block_style_path,
'ver' => wp_get_theme()->get( 'Version' ),
)
);
}
}
endif;
add_action( 'init', 'serial_block_stylesheets' );
if ( ! function_exists( 'serial_preload_fonts' ) ) :
/**
* Preloads the Inter variable font files to reduce flash of unstyled text.
*
* Only the default body font is preloaded. Typography variation fonts are
* not preloaded since only a subset of sites use each one.
*
* @since serial 1.0
* @return void
*/
function serial_preload_fonts() {
$fonts = array(
'InterVariable.woff2',
'InterVariable-Italic.woff2',
);
foreach ( $fonts as $font ) {
echo '' . "\n";
}
}
endif;
add_action( 'wp_head', 'serial_preload_fonts', 1 );
if ( ! function_exists( 'serial_post_featured_image_placeholder' ) ) :
/**
* Renders a placeholder for the Post Featured Image block when a post has no thumbnail.
*
* Mirrors the editor's placeholder so card-style layouts don't collapse on
* posts without a featured image.
*
* @since serial 1.0
*
* @param string $block_content The rendered block content.
* @param array $parsed_block The parsed block.
* @return string Modified block content.
*/
function serial_post_featured_image_placeholder( $block_content, $parsed_block ) {
if ( 'core/post-featured-image' !== ( $parsed_block['blockName'] ?? '' ) || ! empty( $block_content ) ) {
return $block_content;
}
$attrs = $parsed_block['attrs'] ?? array();
$aspect = $attrs['aspectRatio'] ?? '';
if ( ! $aspect || 'auto' === $aspect ) {
return $block_content;
}
$style = 'aspect-ratio:' . $aspect . ';';
foreach ( array( 'height', 'width' ) as $prop ) {
if ( ! empty( $attrs[ $prop ] ) ) {
$style .= $prop . ':' . $attrs[ $prop ] . ';';
}
}
$radius = $attrs['style']['border']['radius']
?? wp_get_global_styles( array( 'blocks', 'core/post-featured-image', 'border', 'radius' ) );
if ( is_array( $radius ) ) {
foreach ( $radius as $corner => $value ) {
if ( $value ) {
$style .= 'border-' . strtolower( preg_replace( '/[A-Z]/', '-$0', $corner ) ) . '-radius:' . $value . ';';
}
}
} elseif ( $radius ) {
$style .= 'border-radius:' . $radius . ';';
}
$classes = 'wp-block-post-featured-image is-placeholder';
if ( ! empty( $attrs['className'] ) ) {
$classes .= ' ' . $attrs['className'];
}
$transparent = '';
$overlay = '';
$inner = '
' . $overlay;
if ( ! empty( $attrs['isLink'] ) ) {
$inner = '' . $inner . '';
}
return sprintf(
'%s',
esc_attr( $classes ),
esc_attr( $style ),
$inner
);
}
endif;
add_filter( 'render_block', 'serial_post_featured_image_placeholder', 11, 2 );
if ( ! function_exists( 'serial_add_category_to_template_hierarchy' ) ) :
/**
* Adds a category-specific template to the single post template hierarchy.
*
* Prioritizes 'single-blog' template for posts in the category with the
* 'blog' slug, the same category 'category-blog' applies to. A template
* chosen for the post in the editor still wins.
*
* @since serial 1.0
*
* @param array $templates List of template files to search for.
* @return array Modified list of template files.
*/
function serial_add_category_to_template_hierarchy( $templates ) {
$post = get_queried_object();
if ( ! $post instanceof WP_Post || get_page_template_slug( $post ) ) {
return $templates;
}
$blog_category = get_category_by_slug( 'blog' );
if ( $blog_category && has_category( $blog_category->term_id, $post ) ) {
array_unshift( $templates, 'single-blog' );
}
return $templates;
}
endif;
add_filter( 'single_template_hierarchy', 'serial_add_category_to_template_hierarchy' );
if ( ! function_exists( 'serial_podcast_episode_preload_metadata' ) ) :
/**
* Preloads audio metadata in the Podcast Episode block player.
*
* The block outputs preload="none", so the player shows 0:00 as the
* length until playback starts. Metadata preloading shows the real
* length up front, matching the core Audio block behavior.
*
* @since serial 1.0
*
* @param string $block_content The block markup.
* @return string Modified block markup.
*/
function serial_podcast_episode_preload_metadata( $block_content ) {
return str_replace( 'preload="none"', 'preload="metadata"', $block_content );
}
endif;
add_filter( 'render_block_jetpack/podcast-episode', 'serial_podcast_episode_preload_metadata' );
if ( ! function_exists( 'serial_podcast_episode_critical_styles' ) ) :
/**
* Prints minimal Podcast Episode poster sizing in the document head.
*
* Critical-CSS optimizers such as Jetpack Boost defer the block's
* stylesheet, so the first paint renders the poster at its injected
* width/height attribute size — a flash of a huge cover image. This
* head-inlined guard holds the poster at its intended size until the
* full stylesheet arrives; once loaded, the block's own higher-
* specificity rules take over.
*
* @since serial 1.0
* @return void
*/
function serial_podcast_episode_critical_styles() {
if ( ! is_singular() || ! has_block( 'jetpack/podcast-episode' ) ) {
return;
}
echo '' . "\n";
}
endif;
add_action( 'wp_head', 'serial_podcast_episode_critical_styles', 7 );
if ( ! function_exists( 'serial_read_more_listen_label' ) ) :
/**
* Names icon-only Read More links "Listen: {post title}".
*
* The theme's icon Read More styles show a play icon with no visible
* text (content set to a single space). Core then names the link only
* by its screen-reader span, ": {post title}", which starts with a
* colon and doesn't say what the link does. An aria-label replaces
* that name. Links with visible text keep core's name.
*
* @since serial 1.0
*
* @param string $block_content Rendered block markup.
* @param array $block Parsed block.
* @param WP_Block $instance Block instance.
* @return string Modified block markup.
*/
function serial_read_more_listen_label( $block_content, $block, $instance ) {
if ( ! isset( $block['attrs']['content'] ) || '' !== trim( wp_strip_all_tags( $block['attrs']['content'] ) ) ) {
return $block_content;
}
$post_id = isset( $instance->context['postId'] ) ? (int) $instance->context['postId'] : 0;
$title = $post_id ? trim( wp_strip_all_tags( get_the_title( $post_id ) ) ) : '';
if ( '' === $title ) {
return $block_content;
}
$tags = new WP_HTML_Tag_Processor( $block_content );
if ( ! $tags->next_tag( 'a' ) ) {
return $block_content;
}
/* translators: %s: post title. */
$label = sprintf( __( 'Listen: %s', 'serial' ), html_entity_decode( $title, ENT_QUOTES, get_bloginfo( 'charset' ) ) );
$tags->set_attribute( 'aria-label', $label );
return $tags->get_updated_html();
}
endif;
add_filter( 'render_block_core/read-more', 'serial_read_more_listen_label', 10, 3 );