You would be surprised to see many bloated alternatives to this simple task. Even in the age of the WordPress block editor, most solutions out there are overkill for what you actually need.
I have implemented a logo carousel in a few WordPress and non-WordPress websites, either as a JavaScript logo carousel or a CSS logo carousel, if you are interested.
I have implemented the logo carousel using PHP and custom post types in my Saturn theme, but let’s break down the code, and you can create your own plugin to add settings for speed, logo count and so on. You can see a demo of the Saturn theme here (scroll down until you see the carousel).
The fastest way to do it is by using custom post types. So let’s create a logo
post type.
The code below creates a new custom post type, and adds support for title
, editor
, thumbnail
, and custom-fields
. We will use the featured image (thumbnail
) for the actual logo.
We might not need the editor
or the custom-fields
support, but depending on your implementation, it’s good to have them.
Note that the logos are not publicly queryable, meaning they are not visible in sitemaps or in Google, which is good.
<?php
function whiskey_cpt_logo() {
$labels = [
'name' => __( 'Logos', 'whiskey' ),
'singular_name' => __( 'Logo', 'whiskey' ),
'menu_name' => __( 'Logos', 'whiskey' ),
'name_admin_bar' => __( 'Logo', 'whiskey' ),
'archives' => __( 'Logo Archives', 'whiskey' ),
'attributes' => __( 'Logo Attributes', 'whiskey' ),
'parent_item_colon' => __( 'Parent Logo:', 'whiskey' ),
'all_items' => __( 'All Logos', 'whiskey' ),
'add_new_item' => __( 'Add New Logo', 'whiskey' ),
'add_new' => __( 'Add New', 'whiskey' ),
'new_item' => __( 'New Logo', 'whiskey' ),
'edit_item' => __( 'Edit Logo', 'whiskey' ),
'update_item' => __( 'Update Logo', 'whiskey' ),
'view_item' => __( 'View Logo', 'whiskey' ),
'view_items' => __( 'View Logos', 'whiskey' ),
'search_items' => __( 'Search Logo', 'whiskey' ),
'not_found' => __( 'Not found', 'whiskey' ),
'not_found_in_trash' => __( 'Not found in Trash', 'whiskey' ),
'featured_image' => __( 'Featured Image', 'whiskey' ),
'set_featured_image' => __( 'Set featured image', 'whiskey' ),
'remove_featured_image' => __( 'Remove featured image', 'whiskey' ),
'use_featured_image' => __( 'Use as featured image', 'whiskey' ),
'insert_into_item' => __( 'Insert into logo', 'whiskey' ),
'uploaded_to_this_item' => __( 'Uploaded to this logo', 'whiskey' ),
'items_list' => __( 'Logos list', 'whiskey' ),
'items_list_navigation' => __( 'Logos list navigation', 'whiskey' ),
'filter_items_list' => __( 'Filter logos list', 'whiskey' ),
];
$args = [
'label' => __( 'Logo', 'whiskey' ),
'description' => __( 'Logos', 'whiskey' ),
'labels' => $labels,
'supports' => [ 'title', 'editor', 'thumbnail', 'custom-fields' ],
'hierarchical' => false,
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'menu_icon' => 'dashicons-slides',
'show_in_admin_bar' => false,
'show_in_nav_menus' => false,
'can_export' => true,
'has_archive' => false,
'exclude_from_search' => true,
'publicly_queryable' => false,
'rewrite' => false,
'capability_type' => 'post',
'show_in_rest' => true,
];
register_post_type( 'logo', $args );
}
add_action( 'init', 'whiskey_cpt_logo', 0 );
This is what it looks so far:
Next, we need to add the code that powers the carousel, in the form of a shortcode. Note how I set a default of 4
visible slides. I have also placed the CSS inline, as it’s pretty small. The HTML code also contains useful Schema parameters.
<?php
function whiskey_get_logo_carousel( $atts, $content = null ) {
$attributes = shortcode_atts(
[
'per-page' => 4,
],
$atts
);
$per_page = ( (int) $attributes['per-page'] > 0 ) ? (int) $attributes['per-page'] : 4;
$out = '<style>
:root {
--items-per-page: ' . $per_page . ';
}
.carousel {
width: 100%;
overflow: hidden;
position: relative;
}
.carousel-container {
/* Prevent logos from wrapping to the next line */
white-space: nowrap;
display: flex;
align-items: center;
}
.carousel-item {
display: inline-block;
min-width: calc(100% / var(--items-per-page));
box-sizing: border-box;
padding: 2em;
text-align: center;
}
.carousel-item .wrap-content img,
.carousel-item img {
max-width: 100%;
width: 80%;
height: auto;
}
</style>';
$out .= '<div class="carousel" id="carousel" role="region" aria-label="Logo Carousel">
<div class="carousel-container" id="carouselContainer" role="list">';
$args = [
'post_type' => 'logo',
'posts_per_page' => 128, // Show 128 only (instead of -1) for performance reasons
'order' => 'DESC',
'orderby' => 'date',
'no_found_rows' => true,
];
$logos = new WP_Query( $args );
if ( $logos->have_posts() ) {
while ( $logos->have_posts() ) {
$logos->the_post();
$logo_id = get_the_ID();
if ( has_post_thumbnail() ) {
$out .= '<div class="carousel-item" role="listitem"><img src="' . get_the_post_thumbnail_url( $logo_id, 'full' ) . '" alt="' . get_the_title( $logo_id ) . '" loading="eager"></div>';
}
}
}
$out .= '</div>
</div>';
return $out;
}
add_shortcode( 'logo-carousel', 'whiskey_get_logo_carousel' );
And the final step is to add the JavaScript which powers the carousel. The general idea is to use a CSS animation and to clone the carousel in order to create the illusion of a loop, or infinite scrolling.
The code is pretty self-explanatory, and variables can be changed, such as the speed or the frames per second (currently at 60, for a smooth motion).
document.addEventListener('DOMContentLoaded', () => {
if (document.querySelector('.carousel-container')) {
const carousels = document.querySelectorAll('.carousel-container');
carousels.forEach(carouselContainer => {
// Clone the carousel content to create a continuous loop
const carouselItems = carouselContainer.innerHTML;
carouselContainer.innerHTML += carouselItems;
// Set up animation
let scrollLeft = 0;
const scrollSpeed = 4; // Adjust the scroll speed as needed
function animateCarousel(timestamp) {
if (!lastTimestamp) {
lastTimestamp = timestamp;
}
const deltaTime = timestamp - lastTimestamp;
lastTimestamp = timestamp;
scrollLeft += scrollSpeed * deltaTime / 60; // Normalize speed
if (scrollLeft >= carouselContainer.scrollWidth / 2) {
scrollLeft = 0;
}
carouselContainer.style.transform = `translateX(-${scrollLeft}px)`;
requestAnimationFrame(animateCarousel);
}
let lastTimestamp = null;
requestAnimationFrame(animateCarousel);
});
}
}, false);
You can add the code above to your existing JavaScript code (in a plugin, or a theme), or enqueue it.
function whiskey_enqueue() {
// Use this for themes
wp_enqueue_script( 'carousel-init', get_stylesheet_directory_uri() . '/js/carousel.js', [], '1.0.0', true );
// Use this for plugins
wp_enqueue_script( 'carousel-init', plugins_url( 'js/carousel.js', __FILE__ ), [], '1.0.0', true );
}
add_action( 'wp_enqueue_scripts', 'whiskey_enqueue' );
Depending on your implementation, make sure you use either the theme version or the plugin version, not both. Also, make sure to use the correct file path.
[…] If you want to use the logo carousel as a WordPress plugin, see How to Create a Logo Carousel as a WordPress Plugin. […]
[…] If you want to use the logo carousel as a WordPress plugin, see How to Create a Logo Carousel as a WordPress Plugin. […]