This site uses the “Sinatra” free WordPress theme as its base. Sinatra includes a single “Hero Header” the row of 3 animated featured posts on the home page which changes background images as you mouse over post titles within it.I wanted this to be multi-row, a grid instead of just one row. The code natively contained an option to include up to 12 featured posts, which could easily be made to wrap around to new rows, but the problem was, the background of the entire section containing all rows changed when you moused over a post, not just the single row, and I wanted not just a row-by-row background, but I wanted potentially unlimited rows.I moved the code that generates the rows into a function, and then called it repeatedly, once for each row I want. At some point I may make it automatically add as many rows as are required to display all posts, but as of this writing, every time I add one more post than there is room for, I edit the bottom of this file to add another row.Furthermore, originally, the theme CSS was really sloppy: 1.) There’s a lot of identifying things by element, such as anchor or div tags, which the CSS then had to look up the ancestry of to see which formatting applied (as in rules similar to, “#page .hero-row .hero-slidera {blah-blah:blah}”, which is horribly inefficient compared to just giving things a class. 2.) All the changing images were done using CSS background images on <div> elements, making them hard to lazy-load and killing page performance. 3.) Plus there were a lot of animations on non-compositor-only attributes, making Google PageSpeed complain.So a lot of CSS got rewritten. I don’t mind telling you: it took a while. There are still a lot of inefficiencies, like divs directly nested as the only child of other divs, that can be cleaned up. Unfortunately that’s going to take a while as a lot of things are done with flexboxes, which makes refactoring all the formatting a lot tougher. So for right now I’m leaving that alone, but it’s on my “future plans” list.I’m proud to say that as of this writing (July 2, 2024) my great, big, overcomplicated (over 5000 elements) animated home page gets a 99 out of 1000 performance score on Google PageSpeed. See for yourself: Pagespeed ReportHere, then, is my current updated version of Sinatra theme’s /template-parts/hero/hero-hover-slider.php. This is dynamically read from the actual site files, this is what’s currently in live use on the front page: <?php/** * The template for displaying Hero Hover Slider. * * @package Sinatra * @author Sinatra Team <hello@sinatrawp.com> * @since 1.0.0 */ $postsperrow = 3;$sinatra_hero_categories = !empty($sinatra_hero_categories) ? implode(", ", $sinatra_hero_categories) : "";// Setup Hero posts.//function herorow($postsperpage,$offset,$order,$sinatra_hero_categories) {return '<h2>hi '.$offset.'</h2>';}function count_total_post_media( $id ) { $data = getFunctionTransient(__FUNCTION__, $id); if ( $data != null) {return $data;} // from https://wordpress.stackexchange.com/questions/246055/count-total-number-of-images-in-post-and-echo-results-as-number $array = get_post_galleries( $id, false ); $key = 0; $theOut=array(); // $src = 0; while ( $key < count( $array ) ){ // $src += count( $array[$key]['src'] ); $theOut= array_merge($theOut,explode(',',$array[$key]['ids'])); $key++; }$newarray=array_column(get_children($id),'ID'); //get_children returns an array of obects, which has to be handled differently than ordinary arrays, because otherwise it wouldn't be PHP/*DEBUGGING if(is_user_logged_in()) {echo "<!-- ".print_r($newarray)." -->";echo "<!-- ".get_post_thumbnail_id($id, "full")." -->"; } */ $keynew = 0; // $srcnew = 0; $theOutnew=array(); while ( $keynew < count( $newarray ) ){ // $src += count( $array[$keynew]['src'] ); $theOutnew[]= $newarray[$keynew]; $keynew++; }$theFinalArray=array_unique(array_merge($theOut,$theOutnew));if(has_post_thumbnail( $id )) {if (($key = array_search(get_post_thumbnail_id($id) , $theFinalArray)) !== false) { unset($theFinalArray[$key]); } /* else { echo "<!-- ".array_search(get_the_post_thumbnail_url($id, "full") , $theFinalArray)." -->";} */} /* NOTE: Tested, this already doesn't detect LJ-divider because it's added by a shortcode */$theFinalCount=count($theFinalArray); // was "$theFinalCount=count($theFinalArray)-$hasThumb;" at the end, but this appears to be leftover from something removed, $hasthumb isn't defined anywhere// if(is_user_logged_in()) {echo "<!-- ".print_r($theOut)." -->"; } // return intval( $src ); // setFunctionTransient(__FUNCTION__, $theFinalCount ,$id); return intval( $theFinalCount ); }function herorow($postsperpage, $offset, $order, $sinatra_hero_categories,$theHeroTag){ $sinatra_args = [ "post_type" => "post", "post_status" => "publish", "order" => $order, //'ASC', 'orderby' => 'date',//'post_modified', "posts_per_page" => $postsperpage, //3 //sinatra_option( 'hero_hover_slider_post_number' ), // phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page "offset" => $offset, "ignore_sticky_posts" => true, 'tag' => $theHeroTag, "tax_query" => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query [ "taxonomy" => "post_format", "field" => "slug", "terms" => ["post-format-quote"], "operator" => "NOT IN", ], ], ]; $sinatra_hero_categories = sinatra_option("hero_hover_slider_category");$atts=array($postsperpage, $offset, $order, $sinatra_hero_categories,$theHeroTag );$data = getFunctionTransient(__FUNCTION__, $atts); if ( $data != null) {return /*"<!-- transient ".__FUNCTION__." code for atts ". json_encode( $atts ) ." -->".*/ $data;} if (!empty($sinatra_hero_categories)) { $sinatra_args["category_name"] = implode( ", ", $sinatra_hero_categories ); } $sinatra_args = apply_filters( "sinatra_hero_hover_slider_query_args", $sinatra_args ); $sinatra_posts = new WP_Query($sinatra_args); // No posts found. if (!$sinatra_posts->have_posts()) { return false; } $sinatra_hero_bgs_html = ""; $sinatra_hero_items_html = ""; $sinatra_hero_elements = (array) sinatra_option( "hero_hover_slider_elements" ); $sinatra_hero_readmore = isset($sinatra_hero_elements["read_more"]) && $sinatra_hero_elements["read_more"] ? " si-hero-readmore" : ""; while ($sinatra_posts->have_posts()): $sinatra_posts->the_post();$this_image_id = get_post_thumbnail_id();$this_image_alt = get_post_meta($this_image_id, '_wp_attachment_image_alt', TRUE);$this_image_title = get_the_title($this_image_id); if (get_the_title()) {$thistitle = get_the_title(); } $pre_alt_tag = $this_image_alt?($this_image_alt.", feature article by Mike Kupietz"):($thistitle?($thistitle.", feature art by Mike Kupietz"):"feature art by Mike Kupietz"); $this_alt_tag= (strpos($pre_alt_tag, ': ') !== false) ? substr($pre_alt_tag, strpos($pre_alt_tag, ': ') + 1) : $pre_alt_tag; $thumb = wp_get_attachment_image_src( get_post_thumbnail_id(get_the_ID()), 'full' ); $url = $thumb['0']; $width = $thumb['1']; $height = $thumb['2']; $midthumb = wp_get_attachment_image_src( get_post_thumbnail_id(get_the_ID()), 'medium' ); $midurl = $midthumb['0']; $midwidth = $midthumb['1']; $midheight = $midthumb['2']; // Background images HTML markup. $sinatra_hero_bgs_html = // 1px included below to prompt the lazy loader to fetch the images. Silly, I know. For a while I used full size images with just 0.001 opacity but sometimes it ignored the opacity. '<div class="hover-slide-bg lazyload">' . '<img alt="'.htmlspecialchars($this_alt_tag).'" class="mk-hero-row-bg" src="'. /*get_the_post_thumbnail_url(get_the_ID(), "full")*/$url .'" width="'.$width.'" height="'.$height.'" />' . '</div>' . $sinatra_hero_bgs_html; //reversing the order. Must be reversed below too. // Post items HTML markup. // ob_start();$postImgUrl=$midurl;//get_the_post_thumbnail_url(get_the_ID(), "medium"); ob_start(); echo '<div class="col-xs-' . intdiv(12, $postsperpage) . " xmkwrapper hover-slider-item-wrapper h-entry"; echo esc_attr($sinatra_hero_readmore); echo '" itemscope itemtype="https://schema.org/Article"><link itemprop="image" href="'.$postImgUrl.'"><img class="mk-hero-row-bg-mobile" alt="'.$this_alt_tag.'" src="'. $postImgUrl .'" width="'.$midwidth.'" height="'.$midheight.'" /> <section style="display: none;" class="p-author h-card vcard"> <span class="p-name fn"> Michael Kupietz | ARTS+CODE - Featured articles </span> </section>'. /* u-card was originally: <section style="display: none;" class="p-author h-card vcard"> <span class="p-name fn"> <span class="p-given-name given-name">Michael</span> <span class="p-family-name family-name">Kupietz</span> | ARTS+CODE - Featured articles </span> <a href="https://michaelkupietz.com/" class="u-url url">michaelkupietz.com</a> <ul class="p-org h-card org"> <li> <a href="https://michaelkupietz.com/" class="p-name u-url p-organization-name organization-name">Michael Kupietz | ARTS+CODE</a> </li> <!-- li> <img src="/image/logo/kupietzlogo-256x256.png" alt="Logotype for michaelkupietz.com" class="u-logo logo"> </li --> </ul> </section>*/ '<div class="hover-slide-item xmkparent-div"> <div class="slide-inner xmkpost-div">'; if ( isset($sinatra_hero_elements["category"]) && $sinatra_hero_elements["category"] ) { //echo '<div class="post-category">'; sinatra_entry_meta_category(" ", true); echo " "; sinatra_entry_meta_genre(" ", false,false,0); // echo "</div>"; } if (get_the_title()) { echo '<h2 class="hero_h3">'; $thepre = get_post_meta(get_the_ID(), "hero_pre", true); if ($thepre) { echo '<p class="hero_pre_p">' . $thepre . "</p>"; } $theRel = get_post_meta(get_the_ID(), 'rel', true ); $theRel .= ($theRel?' ':'').'bookmark'; /* need bookmark for h-feed */ echo '<a class="hero_h3_a u-url" title="'.addslashes($thistitle).'" itemprop="headline" '. (($theRel)?(' rel="'.$theRel.'" '):'') . 'href="'; echo esc_url(sinatra_entry_get_permalink()); echo '">'; /* was the_title();...which includes echo() in executing. */ //moving this up to use in alt tags $thistitle = get_the_title(); $thiscategory = get_the_category(get_the_ID()); $thiscategoryterm = ($thiscategory[0]->name)=='feature'?($thiscategory[1]->term_id):($thiscategory[0]->term_id); /* BugFu::log("title ",false); BugFu::log($thistitle,false); BugFu::log("thiscat ",false); BugFu::log($thiscategory,false); BugFu::log("thiscategoryterm ",false); BugFu::log($thiscategoryterm,false); */ $titlepre = get_post_meta(get_the_ID(), "titleprefix", true); $colonpos = strpos($thistitle, ":"); //I don't think this is ever even used anymore. Check. if ($titlepre && !$thepre) { /* added && !$thepre in 2024aug6 to prevent both wihite italic pre and H6-style titlepre */ $thistitle='<span class="titleprefix">' .$titlepre.'</span>'.'<span class="p-name">'.$thistitle.'</span>'; } elseif ($colonpos !== false && !$thepre) { /* added && !$thepre in 2024aug6 to prevent both wihite italic pre and H6-style titlepre */ $thistitle = '<span class="titleprefix'.($thepre?' displaynone':'').'">' . substr_replace($thistitle, ":</span>", $colonpos, strlen(":")); /* str_replace(":", ":</span>"."<div class=\"" . getClassOfCatOrParentCat(get_the_category(get_the_ID())[0]->term_id)."\"></div>", $thistitle);*/ } else {$thistitle= $theParentCatClass.$thistitle;} // echo "<!-- id ".get_the_ID()." cat ".print_r(get_the_category(get_the_ID()))." -->"; echo ($thistitle); $thepost = get_post_meta(get_the_ID(), "hero_post", true); if ($thepost) { echo '<p class="hero_pre_p"><i class="hero_h3_i">' . $thepost . "</i></p>"; } /* end replacement */ echo "</a>"; /* WAS HERE $thepost = get_post_meta(get_the_ID(), "hero_post", true); if ($thepost) { echo '<p class="heropre" style="font-size:.6em;padding:.25em 0;margin:0;"><i class="hero_h3_i">' . $thepost . "</i></p>"; } */ echo "</h2>"; } if ( isset($sinatra_hero_elements["meta"]) && $sinatra_hero_elements["meta"] ) { echo ' <div class="slider-entry-meta entry-meta"> <div class="slider-entry-meta-elements entry-meta-elements">';echo '<div class="herosummary p-summary" itemprop="description">'.get_the_excerpt().'</div>'; //sinatra_entry_meta_author(); /* sinatra_entry_meta_date([ "show_modified" => false, "published_label" => "", ]); */ if (function_exists("bac_post_word_count")) { // '<span class="wordcount">'; bac_post_word_count(); echo " words"; //echo "</span>"; } $theMediaCount =/* count(get_attached_media(get_the_ID())) + count(get_children(get_the_ID())) + */ count_total_post_media(get_the_ID()); // - count(get_post_galleries(get_the_ID(),false)) ; //length(get_attached_media()) + if ($theMediaCount) {//if(is_user_logged_in()) {echo "<!-- ".var_dump(get_children(get_the_ID())).". -->"; } echo ' | '; //'<span class="mediacount">'; echo $theMediaCount; echo " media item"; echo $theMediaCount == 1 ? "" : "s"; // echo "</span>"; }// echo '<span class="updateddate">' ; echo ' | '; echo '<time datetime='.get_the_modified_date('Y-m-d',get_the_ID()).' class="dt-published">Updated '.get_the_modified_date('M j, Y',get_the_ID())/*publish date get_the_date('',get_the_ID())*/ /*get_post_modified_date_except(get_the_ID(), '2024-07-31')*/ .'</time>' /* must be <time>, not <span> for indieweb h-feeds */; /*echo '</span>'; */ #semrush says I'm using my name too much echo ' <span itemprop="author" itemscope itemtype="http://schema.org/Person"><span class="hero-author-name author-name" itemprop="name"><a class="url fn n" title="View all posts by Michael Kupietz" href="https://michaelkupietz.com/author/mike-kupietz/" rel="author" itemprop="url">Mike Kupietz</a></span></span>'; echo '</div>'; echo'</div>';//<!-- END .entry-meta -->'; } if ($sinatra_hero_readmore) { echo '<a href="'; echo esc_url(sinatra_entry_get_permalink()); echo '" class="slider-read-more read-more si-btn btn-small btn-outline btn-uppercase" role="button"><span>'; echo esc_html_e("Click to Read More...", "sinatra"); echo "</span></a>"; } echo '</div>' . comment('<!-- END .slide-inner -->'). '</div>' . comment('<!-- END .hover-slide-item -->'). '</div>' . comment('<!-- END .hover-slider-item-wrapper -->'); $sinatra_hero_items_html = ob_get_clean() . $sinatra_hero_items_html; //reverse the order because I feel like having it that way. Remember to fix the order of the background images too, above, if yu change it back. endwhile; // Restore original Post Data. wp_reset_postdata(); // Hero container. $sinatra_hero_container = sinatra_option("hero_hover_slider_container"); $sinatra_hero_container = "full-width" === $sinatra_hero_container ? "si-container si-container__wide" : "si-container"; // Hero overlay. $sinatra_hero_overlay = absint(sinatra_option("hero_hover_slider_overlay")); $htmloutfinal = '<div class="si-hover-slider slider-overlay-' . esc_attr($sinatra_hero_overlay) . '" style="z-index:-' . $offset . '"> <div class="hover-slider-backgrounds">' . wp_kses_post($sinatra_hero_bgs_html) . '</div>' . comment('<!-- END .hover-slider-items -->'). '<div class="si-hero-container ' . esc_attr($sinatra_hero_container) . '"> <div class="si-flex-row hover-slider-items">' . //wp_kses_post ($sinatra_hero_items_html) . '</div>' . comment('<!-- END .hover-slider-items -->'). '</div> <div class="si-spinner visible"> <div></div> <div></div> </div></div>' . comment('<!-- END .si-hover-slider -->');setFunctionTransient(__FUNCTION__, $htmloutfinal ,$atts) ;return /*"<!-- orignal code for atts ". json_encode( $atts ) ." -->".*/$htmloutfinal;}function get_post_modified_date_except($post_id, $cutoff_date = '2024-07-31') { /* this was intended to cover for a bunch of records that had the mod date reseet to 7/31/24 by showing the last revision before that instead. But I went ahead and reverted them in the database instead so not using this. */ $post = get_post($post_id); $modified_date = get_the_modified_date('Y-m-d', $post_id); if ($modified_date === $cutoff_date) { $revisions = wp_get_post_revisions($post_id, array( 'before' => $cutoff_date, 'limit' => 1, 'orderby' => 'date', 'order' => 'DESC' )); if (!empty($revisions)) { $last_revision = reset($revisions); $modified_timestamp = strtotime($last_revision->post_modified); // Check if the last revision's modified date is also the cutoff date if (date('Y-m-d', $modified_timestamp) === $cutoff_date) { // If the last revision's modified date is the cutoff date, get the previous revision $previous_revision = next($revisions); if ($previous_revision) { $modified_timestamp = strtotime($previous_revision->post_modified); } } $modified_date = date('Y-m-d', $modified_timestamp); } } return date('M j, Y', strtotime($modified_date));}function add_query_vars_filter( $vars ) { $vars[] = "herotag"; return $vars;}add_filter( 'query_vars', 'add_query_vars_filter' );$theHeroTag = $_GET['featured'] ?? "";echo '<div class="featuredheader">Featured work: <a '.(($theHeroTag=="")?('class="heroactive"'):'').' href="/">All</a><a '.(($theHeroTag=="site-info")?('class="heroactive"'):'').' href="/?featured=site-info">Site Info</a><a '.(($theHeroTag=="images")?('class="heroactive"'):'').' href="/?featured=images">Images</a><a '.(($theHeroTag=="music-sounds")?('class="heroactive"'):'').' href="/?featured=music-sounds">Music & Sounds</a><a '.(($theHeroTag=="photojournals")?('class="heroactive"'):'').' href="/?featured=photojournals">Photojournals</a><a '.(($theHeroTag=="writing")?('class="heroactive"'):'').' href="/?featured=writing">Writing</a><a '.(($theHeroTag=="code-algorithms")?('class="heroactive"'):'').' href="/?featured=code-algorithms">Code & Algorithms</a></div>';$featured_posts_for_count = get_posts(array( 'cat' => 51, 'numberposts' => -1,));$featured_post_count_for_hero = count($featured_posts_for_count) - 1;//$featured_post_count = count($featured_posts)-1; //3 posts should still return 0 since we want to add arow if there's 4 posts, not 4 $hero_height = 1+floor($featured_post_count_for_hero / 3);for ($i = 0; $i <= $hero_height; $i++) { echo herorow($postsperrow, $postsperrow * $i, "DESC", $sinatra_hero_categories, $theHeroTag);}?> Mike Kupietz , a reluctant scion of the postmodern age, is larger on the inside than the outside: perhaps not a composer, but a producer and arranger of sounds; nor a writer, but an avid writer-down; an occasional author of doggerel; an erstwhile urban hermit; and privately a man of very great ardor. He is, if now resigned to never succeeding at those personal and artistic pursuits he holds most dear, unwavering in his determination to fail at them as entertainingly as possible. He is currently in what he calls the "red bathrobe period" of his life. If you're wondering what all this has to do with FileMaker development or IT consulting: you done taken the wrong turn, this river don't go to Aintry—Mike's professional services are on his San Francisco FileMaker Pro consulting website. View All PostsPost navigationPrevious Post RobGAN Hitchcock — AI-assisted lyrical illustrations galleryNext PostAbout This Site — An Introduction