In this tutorial, we’ll develop a dynamic Testimonial Block together. In our previous slider block tutorial, we focused on block settings (attributes). This time, we’ll be exploring the content of components to build our block.
Table of Contents
Testimonial Slider Block Development
To create a Testimonial Slider Block, we’ll be using the Swiffy Slider library. This lightweight library is easy to implement and super user-friendly.
Before we begin, I recommend downloading and testing the plugin we’ll be building to familiarize yourself with its features and functionality.
After installing this plugin, you’ll find its icon in the editor.
With this block, users can add as many Testimonials as they want. Each Testimonial includes a paragraph to write the testimonial content, along with an image block to display a picture and the customer’s name.
The final appearance of the Testimonial Block on the front end will look like the image below.
Pre-requisites
Before following this tutorial, make sure you have the following pre-requisites.
- Node.js and npm
- You’ll need a local WordPress development environment set up on your machine.
Let’s dive into the code!
First, navigate to your local WordPress plugins folder and run the following command in your terminal to create the necessary files for your plugin:
npx @wordpress/create-block@latest wpcookie-testimonial --variant=dynamic
This command creates a new directory called wpcookie-testimonial
. navigates into that directory and run the following command:
npm run start
This command will watch each file in the /src
folder for changes. The block’s build files will be updated each time you save a file.
Understanding the Project Structure
We’ll be creating four key files that work together to build the testimonial slider block:
block.json
: This file defines the block’s metadata and configuration.edit.js
: This file handles the block’s editing experience within the WordPress block editor.save.js
: This file is responsible for rendering the block’s content on the front-end.render.php
: This file generates the HTML structure for the testimonial slider on the server-side.
Let’s dive into each file and explore the code in detail.
block.json
This file sets the stage for our block by defining its essential properties:
block.json complete code
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "create-block/wpcookie-testimonial",
"version": "0.1.0",
"title": "WPCookie Testimonial",
"category": "text",
"icon": "testimonial",
"description": "Create testimonial slider.",
"example": {},
"supports": {
"html": false
},
"textdomain": "wpcookie-testimonial",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"render": "file:./render.php",
"viewScript": ["file:./view.js", "file:./publice/swiffy-slider.min.js"]
}
name
: Uniquely identifies the block.version
: Indicates the current version of the block.title
: Displays the block’s name in the WordPress block editor.category
: Categorizes the block for easier organization.icon
: Specifies the icon to be used for the block.description
: Provides a brief description of the block’s purpose.supports
: Defines the block’s supported features (in this case, disabling HTML support).textdomain
: Sets the text domain for internationalization.editorScript
,editorStyle
,style
: Specify the scripts and styles to be used for the block’s editor and front-end.render
: Points to the PHP file responsible for rendering the block on the server-side.viewScript
: Lists the JavaScript files required for the block’s front-end functionality, including the Swiffy Slider library.
index.js
Let’s go through the index.js
file step-by-step.
index.js complete code
import { registerBlockType } from '@wordpress/blocks';
import './style.scss';
import Edit from './edit';
import metadata from './block.json';
import save from './save';
registerBlockType( metadata.name, {
edit: Edit,
save
} );
First, we’re importing the registerBlockType
function from the @wordpress/blocks
library. This function is used to register our custom block with WordPress.
Next, we’re importing a SCSS file called style.scss
. This file contains the CSS styles that will be applied to the front-end of our block.
We’re also importing three other files:
edit.js
: This file contains the code that defines the editing experience of our block within the WordPress block editor.block.json
: This file contains the metadata and configuration for our block, such as its name, category, and other properties.save.js
: This file contains the code that defines how the block’s content should be rendered on the front-end of the website.
Finally, we’re calling the registerBlockType
function and passing in two arguments:
metadata.name
: This is the name of our block, which was defined in theblock.json
file.- An object with two properties:
edit
: This is a reference to theEdit
function that was imported from theedit.js
file. This function defines the editing experience of our block.save
: This is a reference to thesave
function that was imported from thesave.js
file. This function defines how the block’s content should be rendered on the front-end.
By calling the registerBlockType
function, we’re essentially telling WordPress about our custom block and providing it with the necessary information and code to make it work within the WordPress block editor and on the front-end of the website.
The index.js
file is the entry point for our custom block, and it ties together all the different pieces of code (the edit.js
, save.js
, and block.json
files) to create a complete and functional block.
This file is an important part of the custom block development process because it’s responsible for registering the block with WordPress and ensuring that all the necessary components are properly connected and integrated.
edit.js
This file defines the block’s editing experience within the WordPress block editor:
edit.js complete code
import { __} from '@wordpress/i18n';
import {
BlockControls,
useBlockProps,
InnerBlocks
} from '@wordpress/block-editor';
import {
ToolbarButton,
ToolbarGroup,
Button
} from '@wordpress/components';
import { createBlock} from '@wordpress/blocks';
import './editor.scss';
import personSvg from './publice/user.png';
export default function Edit({ }) {
const groupOBJ ={
className: 'testimonial-group',
metadata: {
name: "testimonial"
},
templateLock: "insert",
lock: {
move: false,
remove: false
}
};
const columOBJ = {
className: 'testimonial-column',
layout: {
type: "constrained",
wideSize: "600px"
},
style: {
spacing: {
padding: {
top: "30px",
bottom: "30px",
left: "30px",
right: "30px"
}
}
}
};
const paragraphOBJ = {
className: 'testimonial-paragraph',
align: "center",
placeholder: 'I have been looking for a product like this for years!'
};
const imageOBJ = {
className: 'testimonial-image is-style-rounded',
align: "center",
scale: "cover",
sizeSlug: "thumbnail",
width: "55",
height: "55",
url: personSvg,
caption: "Customer name"
};
const defaultBlocks = [
['core/group', groupOBJ,
[
['core/column', columOBJ,
[
['core/paragraph', paragraphOBJ ],
['core/image', imageOBJ ]
]
]
]
]
];
const addGroupBlock = () => {
const selectedBlockClientId = wp.data.select('core/block-editor').getSelectedBlockClientId();
if (selectedBlockClientId) {
const groupBlock = createBlock('core/group', groupOBJ, [
createBlock('core/column', columOBJ, [
createBlock('core/paragraph', paragraphOBJ),
createBlock('core/image', imageOBJ)
])
]);
wp.data.dispatch('core/block-editor').insertBlock(groupBlock, undefined, selectedBlockClientId);
}
};
return (
< >
< div {...useBlockProps()} >
< div className = "wpcookie-testimonial-wrapper" >
< InnerBlocks
template = {defaultBlocks}
renderAppender = {() => {return null}}
/>
< /div>
< /div>
< BlockControls >
< ToolbarGroup >
< Button icon = "plus-alt"
className = "add-testimonial-btn"
onClick = {addGroupBlock} >
Add
< /Button>
< /ToolbarGroup>
< / BlockControls >
< />);
}
First, we’re importing some necessary functions and components from the WordPress libraries. These allow us to create and customize the block within the WordPress block editor.
__
is a function from the@wordpress/i18n
library that helps with internationalization and translation.BlockControls
,useBlockProps
, andInnerBlocks
are components from the@wordpress/block-editor
library, which provide tools for building and managing the block’s structure and appearance.ToolbarButton
,ToolbarGroup
, andButton
are components from the@wordpress/components
library, which give us pre-built UI elements to use in our block.createBlock
is a function from the@wordpress/blocks
library, which allows us to programmatically create new blocks.- We’re also importing a custom SCSS file (
editor.scss
) and an image file (personSvg
) that we’ll use in the block.
export default function Edit({ }) {
// Define the structure and styles for the block
const groupOBJ = {
className: 'testimonial-group',
metadata: {
name: "testimonial"
},
templateLock: "insert",
lock: {
move: false,
remove: false
}
};
const columOBJ = {
className: 'testimonial-column',
layout: {
type: "constrained",
wideSize: "600px"
},
style: {
spacing: {
padding: {
top: "30px",
bottom: "30px",
left: "30px",
right: "30px"
}
}
}
};
const paragraphOBJ = {
className: 'testimonial-paragraph',
align: "center",
placeholder: 'I have been looking for a product like this for years!'
};
const imageOBJ = {
className: 'testimonial-image is-style-rounded',
align: "center",
scale: "cover",
sizeSlug: "thumbnail",
width: "55",
height: "55",
url: personSvg,
caption: "Customer name"
};
Next, we’re defining the structure and styles for the different elements of the testimonial block. We’re creating four objects:
groupOBJ
: This sets the class name, metadata, and lock settings for the overall “testimonial-group” block.columOBJ
: This sets the class name, layout, and padding styles for the “testimonial-column” block.paragraphOBJ
: This sets the class name, alignment, and placeholder text for the “testimonial-paragraph” block.imageOBJ
: This sets the class name, alignment, size, and image details for the “testimonial-image” block.
These objects will be used to define the default structure and appearance of the testimonial block.
const defaultBlocks = [
['core/group', groupOBJ, [
['core/column', columOBJ, [
['core/paragraph', paragraphOBJ],
['core/image', imageOBJ]
]]
]]
];
The defaultBlocks
array defines the initial content of the testimonial block. It includes a “core/group” block with the settings from the groupOBJ
, which contains a “core/column” block with the settings from the columOBJ
. Inside the column, there’s a “core/paragraph” block with the settings from the paragraphOBJ
and a “core/image” block with the settings from the imageOBJ
.
const addGroupBlock = () => {
const selectedBlockClientId = wp.data.select('core/block-editor').getSelectedBlockClientId();
if (selectedBlockClientId) {
const groupBlock = createBlock('core/group', groupOBJ, [
createBlock('core/column', columOBJ, [
createBlock('core/paragraph', paragraphOBJ),
createBlock('core/image', imageOBJ)
])
]);
wp.data.dispatch('core/block-editor').insertBlock(groupBlock, undefined, selectedBlockClientId);
}
};
The addGroupBlock
function is responsible for adding a new “testimonial-group” block when the user clicks the “Add” button in the block controls. It first gets the ID of the currently selected block, then creates a new “core/group” block with the settings from the groupOBJ
, and a “core/column” block with the settings from the columOBJ
(containing a “core/paragraph” and a “core/image” block). Finally, it inserts the new block group at the position of the currently selected block.
return (
<>
<div { ...useBlockProps() }>
<div className="wpcookie-testimonial-wrapper">
<InnerBlocks
template={defaultBlocks}
renderAppender={() => { return null }}
/>
</div>
</div>
<BlockControls>
<ToolbarGroup>
<Button icon="plus-alt"
className="add-testimonial-btn"
onClick={addGroupBlock}>
Add
</Button>
</ToolbarGroup>
</BlockControls>
</>
);
}
Finally, the return
statement defines the structure of the block’s editing experience. It includes:
- A
<div>
with theuseBlockProps()
settings, which contains the “wpcookie-testimonial-wrapper” class and an<InnerBlocks>
component that renders thedefaultBlocks
structure. - A
<BlockControls>
component that includes a<ToolbarGroup>
with a<Button>
that calls theaddGroupBlock
function when clicked, allowing the user to add a new testimonial group.
By putting all these pieces together, you’ve created a custom testimonial block that can be easily added and customized within the WordPress block editor.
save.js
let’s go through the save.js file step-by-step.
save.js complete code
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
export default function save( ) {
return (
<div { ...useBlockProps.save() }>
<InnerBlocks.Content />
</div>
);
}
First, we’re importing two functions from the @wordpress/block-editor
library: InnerBlocks
and useBlockProps
. These functions ensures that the testimonial slider block’s content and styling are saved and displayed correctly on the front end of your website.
export default function save() {
This is the main function of the save.js
file, which is responsible for saving the block’s content on the front-end. The name of the function is save
, and it’s the default export from this file.
export default function save() {
Inside the save
function, we’re returning some HTML code that will be rendered on the front-end of the website.
The <div>
element has a special attribute: { ...useBlockProps.save() }
. This is a way to apply the necessary styles and attributes to the block’s container, so that it looks and behaves correctly on the front-end.
Inside the <div>
, we have the <InnerBlocks.Content />
component. This component is responsible for rendering the actual content of the block, which was defined in the edit.js
file.
So, in a nutshell, the save.js
file is responsible for taking the content and styles defined in the block’s editing experience (the edit.js
file) and saving them correctly on the database of the website.
render.php
let’s go through the render.php file step-by-step
render.php complete code
<?php
$htmlContent = $content;
$dom = new DOMDocument();
$dom->loadHTML($htmlContent);
$xpath = new DOMXPath($dom);
$query = '//div[contains(@class, \'testimonial-group\')]';
$divs = $xpath->query($query);
$testimonialGroup = [];
// Loop through the divs and save their content to the array
foreach ($divs as $div) {
$testimonialGroup[] = $dom->saveHTML($div);
}
?>
<div class="swiffy-slider slider-nav-dark slider-nav-sm slider-nav-visible slider-nav-touch slider-indicators-round slider-indicators-dark slider-indicators-sm slider-nav-animation">
<ul class="slider-container" >
<?php
foreach ($testimonialGroup as $value) {
$key++;
echo '<li >'.$value.'</li>';
} ?>
</ul>
<button type="button" class="slider-nav"></button>
<button type="button" class="slider-nav slider-nav-next"></button>
<ul class="slider-indicators">
<?php
foreach ($testimonialGroup as $value) {
echo '<li></li>';
} ?>
</ul>
</div>
<?php
$htmlContent = $content;
$dom = new DOMDocument();
$dom->loadHTML($htmlContent);
$xpath = new DOMXPath($dom);
$query = '//div[contains(@class, \'testimonial-group\')]';
$divs = $xpath->query($query);
$testimonialGroup = [];
First, we’re taking the content of the block (stored in the $content
variable) and storing it in the $htmlContent
variable.
Then, we’re creating a new DOMDocument
object, which is a way to work with HTML and XML documents in PHP. We’re loading the $htmlContent
into this DOMDocument
object.
Next, we’re creating a new DOMXPath
object, which is a tool that helps us search and extract specific elements from the DOMDocument
.
We’re using the $xpath
object to find all the <div>
elements that have a class name containing the text “testimonial-group”. This query is stored in the $query
variable.
Finally, we’re executing the $query
and storing the resulting <div>
elements in the $divs
variable. We’re also creating an empty array called $testimonialGroup
, which we’ll use to store the HTML content of these <div>
elements.
foreach ($divs as $div) {
$testimonialGroup[] = $dom->saveHTML($div);
}
Now, we’re looping through the $divs
array (which contains the <div>
elements with the “testimonial-group” class). For each <div>
element, we’re using the $dom->saveHTML()
function to save the HTML content of that <div>
and adding it to the $testimonialGroup
array.
?>
<div class="swiffy-slider slider-nav-dark slider-nav-sm slider-nav-visible slider-nav-touch slider-indicators-round slider-indicators-dark slider-indicators-sm slider-nav-animation">
<ul class="slider-container">
<?php
foreach ($testimonialGroup as $value) {
$key++;
echo '<li >' . $value . '</li>';
}
?>
</ul>
<button type="button" class="slider-nav"></button>
<button type="button" class="slider-nav slider-nav-next"></button>
<ul class="slider-indicators">
<?php
foreach ($testimonialGroup as $value) {
echo '<li></li>';
}
?>
</ul>
</div>
In this last part, we’re creating the HTML structure for the testimonial slider. We’re using a <div>
with the class “swiffy-slider” to create the slider container.
Inside the slider container, we have:
- An unordered list (
<ul>
) with the class “slider-container”, which will hold the individual testimonial slides. - Two buttons with the class “slider-nav” to allow the user to navigate the slider.
- Another unordered list (
<ul>
) with the class “slider-indicators”, which will display the pagination dots for the slider.
We’re then using a foreach
loop to iterate through the $testimonialGroup
array, which contains the HTML content of the “testimonial-group” <div>
elements. For each item in the array, we’re:
- Embedding the HTML content inside a
<li>
element, which will be a slide in the slider. - Adding an empty
<li>
element to the “slider-indicators” list.
This way, the testimonial slider will display the content of the “testimonial-group” <div>
elements, and the pagination dots will correspond to each testimonial slide.
Adding JavaScript and CSS files
Adding JavaScript File
In the src
folder, create a new folder called publice
and copy the swiffy-slider.min.js
file into it. We have already introduced this file in block.json
as a part of the viewScript
property:
"viewScript": [
"file:./view.js",
"file:./publice/swiffy-slider.min.js"
]
This ensures that the swiffy-slider.min.js
file will be loaded on the front-end when the block is rendered.
As a placeholder for the customer’s image, copy the user.png
file to the “public” folder. If you look at the edit.js
file, you’ll notice that we have already imported this image there. This placeholder ensures that you have a preview of how the testimonial slider block will look with a customer’s image in place while you’re editing the block.
Adding CSS Styles
In the style.scss
file, we need to include the styles provided by the Swiffy Slider library. You can paste the contents of the swiffy-slider.min.css
file at the end of style.scss
file, after our own style.
style.scss complete code
div.testimonial-group {
img {width: 55px; margin: auto;}
figcaption { width: 250px;}
}
button.slider-nav:hover, button.slider-nav:focus {
background: #ffffff00;
box-shadow: none;
}
.swiffy-slider{
max-width: 650px!important;
direction: ltr;
}
.swiffy-slider{position:relative;display:block;width:100%;--swiffy-slider-snap-align:center;--swiffy-slider-item-width:100%;--swiffy-slider-item-gap:1rem;--swiffy-slider-item-reveal:0rem;--swiffy-slider-item-ratio:2/1;--swiffy-slider-item-count:1;--swiffy-slider-nav-light:#fff;--swiffy-slider-nav-dark:#333;--swiffy-slider-nav-zoom:1;--swiffy-slider-track-opacity:0.1;--swiffy-slider-track-height:0;--swiffy-slider-nav-outside-size:3.5rem;--swiffy-slider-indicator-outside-size:1.5rem;--swiffy-slider-animation-duration:.75s;--swiffy-slider-animation-delay:0s;--swiffy-slider-animation-timing:ease-in-out}.swiffy-slider,.swiffy-slider::after,.swiffy-slider::before{box-sizing:border-box}.swiffy-slider ::-webkit-scrollbar{height:var(--swiffy-slider-track-height)}.swiffy-slider ::-webkit-scrollbar-track{background:rgba(0,0,0,var(--swiffy-slider-track-opacity))}.swiffy-slider ::-webkit-scrollbar-thumb{background:rgba(0,0,0,.4);border-radius:1rem}.swiffy-slider ::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.6)}.slider-container{--swiffy-slider-item-gap-totalwidth:calc(var(--swiffy-slider-item-gap) * (var(--swiffy-slider-item-count) - 1));--swiffy-slider-item-width:calc((100% - var(--swiffy-slider-item-reveal) - var(--swiffy-slider-item-gap-totalwidth)) / var(--swiffy-slider-item-count));overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-scroll-snap-type:x mandatory;scroll-snap-type:x mandatory;scroll-behavior:smooth;display:grid;align-items:center;height:100%;grid:auto/auto-flow -webkit-max-content;grid:auto/auto-flow max-content;grid-auto-rows:100%;grid-auto-columns:var(--swiffy-slider-item-width);grid-auto-flow:column;grid-gap:var(--swiffy-slider-item-gap);list-style:none;margin:0;padding:0;scrollbar-width:none;scrollbar-color:rgba(0,0,0,.4) rgba(0,0,0,var(--swiffy-slider-track-opacity));background-clip:padding-box}.slider-container>*{scroll-snap-align:var(--swiffy-slider-snap-align);position:relative;width:100%;height:100%}.slider-item-helper .slider-container>*{background-size:cover;background-color:#e1e1e1;background-position:50% 50%;display:flex;justify-content:center;align-items:center}.slider-item-helper:not(.slider-item-ratio) .slider-container>*{min-height:20rem}.slider-item-ratio .slider-container>*>*{position:absolute;top:0;left:0;width:100%;height:100%;-o-object-fit:cover;object-fit:cover}.slider-item-ratio-contain .slider-container>*>*{-o-object-fit:contain;object-fit:contain}.slider-item-ratio .slider-container>::after{display:block;padding-top:calc(100% / (var(--swiffy-slider-item-ratio)));content:""}.slider-item-ratio-32x9{--swiffy-slider-item-ratio:32/9}.slider-item-ratio-21x9{--swiffy-slider-item-ratio:21/9}.slider-item-ratio-16x9{--swiffy-slider-item-ratio:16/9}.slider-item-ratio-4x3{--swiffy-slider-item-ratio:4/3}.slider-item-ratio-2x1{--swiffy-slider-item-ratio:2/1}.slider-item-ratio-1x1{--swiffy-slider-item-ratio:1/1}.slider-item-ratio-3x4{--swiffy-slider-item-ratio:3/4}.slider-nav-scrollbar{--swiffy-slider-track-height:0.5rem}.slider-nav-scrollbar .slider-container{scrollbar-width:thin}.slider-nav-nodelay .slider-container{scroll-behavior:auto}.slider-indicators{position:absolute;right:2rem;bottom:0;left:2rem;display:flex;justify-content:center;padding:0;margin-bottom:1rem;list-style:none}.slider-nav-scrollbar .slider-indicators{margin-bottom:calc(1rem + var(--swiffy-slider-track-height))}.slider-indicators>.active{opacity:1}.swiffy-slider.slider-indicators-outside .slider-nav{margin-bottom:var(--swiffy-slider-indicator-outside-size)}.swiffy-slider.slider-indicators-outside{padding-bottom:var(--swiffy-slider-indicator-outside-size)}.swiffy-slider.slider-indicators-outside .slider-indicators,.swiffy-slider.slider-indicators-outside.slider-indicators{margin-bottom:0}.slider-indicators>*{box-sizing:content-box;flex:0 1 auto;width:2rem;height:.2rem;padding:0;border:.4rem solid transparent;cursor:pointer;background-color:#fff;background-clip:padding-box;opacity:.5;transition:opacity .4s ease}.slider-indicators-square .slider-indicators>*,.slider-indicators-square.slider-indicators>*{width:.5rem;height:.5rem;border:.4rem solid transparent}.slider-indicators-round .slider-indicators>*,.slider-indicators-round.slider-indicators>*{width:.5rem;height:.5rem;border:.4rem solid transparent;border-radius:50%}.slider-indicators-highlight .slider-indicators>.active,.slider-indicators-highlight.slider-indicators>.active{border:.33rem solid transparent;padding:.07rem}.slider-nav{position:absolute;top:0;left:0;bottom:0;border:0;background-color:transparent;cursor:pointer;padding:0;visibility:hidden;opacity:.8;transition:visibility .1s,opacity .2s linear;margin-bottom:var(--swiffy-slider-track-height);display:flex;align-items:center;padding:0 .5rem;-webkit-filter:drop-shadow(0 0 .5rem rgba(0, 0, 0, .5));filter:drop-shadow(0 0 .5rem rgba(0, 0, 0, .5));transform:scale(var(--swiffy-slider-nav-zoom))}.slider-nav::before{position:absolute;content:"";padding:.5rem;width:3rem;height:3rem}.slider-nav::after{content:"";-webkit-mask:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'><path fill-rule='evenodd' d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'></path></svg>");mask:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'><path fill-rule='evenodd' d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'></path></svg>");-webkit-mask-size:cover;mask-size:cover;background-color:var(--swiffy-slider-nav-light);background-origin:content-box;width:3rem;height:3rem}.slider-nav-arrow .slider-nav::after{-webkit-mask:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'><path fill-rule='evenodd' d='M12 8a.5.5 0 0 1-.5.5H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H11.5a.5.5 0 0 1 .5.5z'></path></svg>");mask:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'><path fill-rule='evenodd' d='M12 8a.5.5 0 0 1-.5.5H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H11.5a.5.5 0 0 1 .5.5z'></path></svg>")}.slider-nav-chevron .slider-nav::after{-webkit-mask:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'><path fill-rule='evenodd' d='M9.224 1.553a.5.5 0 0 1 .223.67L6.56 8l2.888 5.776a.5.5 0 1 1-.894.448l-3-6a.5.5 0 0 1 0-.448l3-6a.5.5 0 0 1 .67-.223z'></path></svg>");mask:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'><path fill-rule='evenodd' d='M9.224 1.553a.5.5 0 0 1 .223.67L6.56 8l2.888 5.776a.5.5 0 1 1-.894.448l-3-6a.5.5 0 0 1 0-.448l3-6a.5.5 0 0 1 .67-.223z'></path></svg>")}.slider-nav-caret .slider-nav::after{-webkit-mask:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'><path d='M10 12.796V3.204L4.519 8 10 12.796zm-.659.753-5.48-4.796a1 1 0 0 1 0-1.506l5.48-4.796A1 1 0 0 1 11 3.204v9.592a1 1 0 0 1-1.659.753z'></path></svg>");mask:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'><path d='M10 12.796V3.204L4.519 8 10 12.796zm-.659.753-5.48-4.796a1 1 0 0 1 0-1.506l5.48-4.796A1 1 0 0 1 11 3.204v9.592a1 1 0 0 1-1.659.753z'></path></svg>")}.slider-nav-caretfill .slider-nav::after{-webkit-mask:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'><path d='m3.86 8.753 5.482 4.796c.646.566 1.658.106 1.658-.753V3.204a1 1 0 0 0-1.659-.753l-5.48 4.796a1 1 0 0 0 0 1.506z'></path></svg>");mask:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'><path d='m3.86 8.753 5.482 4.796c.646.566 1.658.106 1.658-.753V3.204a1 1 0 0 0-1.659-.753l-5.48 4.796a1 1 0 0 0 0 1.506z'></path></svg>")}.swiffy-slider:hover .slider-nav{visibility:visible}.swiffy-slider.slider-nav-autohide.slider-item-first-visible .slider-nav:not(.slider-nav-next){visibility:hidden}.swiffy-slider.slider-nav-autohide.slider-item-last-visible .slider-nav.slider-nav-next{visibility:hidden}.slider-nav-outside .slider-container{margin:0 var(--swiffy-slider-nav-outside-size)}.slider-nav-outside .slider-nav{padding:0}.swiffy-slider .slider-nav:hover{opacity:1}.slider-nav-square .slider-nav{padding:0}.slider-nav-round .slider-nav::before,.slider-nav-square .slider-nav::before{background-color:var(--swiffy-slider-nav-light)}.slider-nav-round .slider-nav::after,.slider-nav-square .slider-nav::after{background-color:var(--swiffy-slider-nav-dark);width:2rem;height:2rem;margin:.5rem}.slider-nav-round .slider-nav::before{border-radius:50%}.slider-nav-round .slider-nav::after{-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' %3E%3Cpath fill-rule='evenodd' d='M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z'%3E%3C/path%3E%3C/svg%3E");mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' %3E%3Cpath fill-rule='evenodd' d='M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z'%3E%3C/path%3E%3C/svg%3E")}.slider-nav-dark .slider-nav::after{background-color:var(--swiffy-slider-nav-dark)}.slider-nav-dark.slider-nav-round .slider-nav::before,.slider-nav-dark.slider-nav-square .slider-nav::before{background-color:var(--swiffy-slider-nav-dark)}.slider-nav-dark.slider-nav-round .slider-nav::after,.slider-nav-dark.slider-nav-square .slider-nav::after{background-color:var(--swiffy-slider-nav-light)}.slider-nav-sm{--swiffy-slider-nav-zoom:.75;--swiffy-slider-nav-outside-size:2.5rem}.slider-nav.slider-nav-next::after{transform:rotate(180deg)}.slider-nav.slider-nav-next{right:0;left:unset}.slider-nav-visible .slider-nav{visibility:visible}.slider-nav-dark .slider-nav{opacity:.6}.slider-indicators-dark .slider-indicators>*,.slider-indicators-dark.slider-indicators>*{-webkit-filter:invert(1);filter:invert(1)}.slider-item-snapstart{--swiffy-slider-snap-align:start}.slider-item-nosnap{--swiffy-slider-snap-align:unset}.slider-item-nogap{--swiffy-slider-item-gap:0rem}.slider-item-reveal{--swiffy-slider-item-reveal:8rem}.slider-item-snapstart.slider-item-reveal{--swiffy-slider-item-reveal:4rem}.slider-item-show2{--swiffy-slider-item-count:2}.slider-item-show3{--swiffy-slider-item-count:3}.slider-item-show4{--swiffy-slider-item-count:4}.slider-item-show5{--swiffy-slider-item-count:5}.slider-item-show6{--swiffy-slider-item-count:6}.slider-nav-mousedrag .slider-container{cursor:-webkit-grab;cursor:grab}.slider-nav-mousedrag.dragging .slider-container{-ms-scroll-snap-type:unset;scroll-snap-type:unset;scroll-behavior:unset;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.slider-nav-mousedrag.dragging .slider-nav{visibility:hidden}@media (hover:hover){.slider-nav-mousedrag .slider-container::after{content:"";position:absolute;width:100%;height:100%}}@media (prefers-reduced-motion:no-preference){.slider-nav-animation.slider-nav-animation-fast{--swiffy-slider-animation-duration:.25s}.slider-nav-animation.slider-nav-animation-slow{--swiffy-slider-animation-duration:1.25s}.slider-nav-animation .slider-container>*>*{transition:opacity var(--swiffy-slider-animation-duration) var(--swiffy-slider-animation-timing),transform var(--swiffy-slider-animation-duration) var(--swiffy-slider-animation-timing);transition-delay:var(--swiffy-slider-animation-delay)}.slider-nav-animation .slider-container .slide-visible>*{transition:opacity var(--swiffy-slider-animation-duration) var(--swiffy-slider-animation-timing),transform var(--swiffy-slider-animation-duration) var(--swiffy-slider-animation-timing);transition-delay:var(--swiffy-slider-animation-delay)}.slider-nav-animation.slider-nav-animation-fadein .slider-container>*>*{opacity:.5}.slider-nav-animation.slider-nav-animation-scale .slider-container>*>*{transform:scale(.9)}.slider-nav-animation.slider-nav-animation-appear .slider-container>*>*{opacity:.3;transform:scale(.9)}.slider-nav-animation.slider-nav-animation-scaleup .slider-container>*>*{transform:scale(.25)}.slider-nav-animation.slider-nav-animation-zoomout .slider-container>*{overflow:hidden}.slider-nav-animation.slider-nav-animation-zoomout .slider-container>*>*{transform:scale(1.3)}.slider-nav-animation.slider-nav-animation-turn .slider-container>*>*{transform:rotateY(70deg)}.slider-nav-animation.slider-nav-animation-slideup .slider-container>*>*{transform:translateY(60%) scale(.99)}.slider-nav-animation.slider-nav-animation-slideup .slider-container{overflow-y:hidden}.slider-nav-animation .slider-container>.slide-visible>*{opacity:1;transform:none}}@media (min-width:62rem){.slider-item-show2:not(.slider-item-snapstart) .slider-container>*,.slider-item-show4:not(.slider-item-snapstart) .slider-container>*,.slider-item-show6:not(.slider-item-snapstart) .slider-container>*{scroll-snap-align:unset}.slider-item-show2:not(.slider-item-snapstart) .slider-container>::before,.slider-item-show4:not(.slider-item-snapstart) .slider-container>::before,.slider-item-show6:not(.slider-item-snapstart) .slider-container>::before{content:" ";display:block;position:absolute;left:calc((var(--swiffy-slider-item-gap)/2)*-1);top:0;width:1px;height:1px;scroll-snap-align:var(--swiffy-slider-snap-align)}.slider-nav-outside-expand .slider-nav{margin-left:-4rem}.slider-nav-outside-expand .slider-nav.slider-nav-next{margin-right:-4rem}.slider-nav-sm.slider-nav-outside-expand .slider-nav{margin-left:-3.5rem}.slider-nav-sm.slider-nav-outside-expand .slider-nav.slider-nav-next{margin-right:-3.5rem}.slider-indicators-sm.slider-indicators{display:none}}@media (max-width:62rem){.swiffy-slider{--swiffy-slider-track-height:0rem;--swiffy-slider-item-reveal:0rem;--swiffy-slider-item-count:1;--swiffy-slider-nav-zoom:.875}.swiffy-slider .slider-item-show2-sm{--swiffy-slider-item-count:2}.slider-item-reveal{--swiffy-slider-item-reveal:4rem}.slider-item-snapstart.slider-item-reveal{--swiffy-slider-item-reveal:2rem}.slider-item-show6 .slider-container{grid-auto-columns:calc(25% - (var(--swiffy-slider-item-gap)/ 4*3))}.slider-item-show6.slider-item-reveal .slider-container{grid-auto-columns:calc(25% - (var(--swiffy-slider-item-gap)/ 4*3) - .5rem)}.slider-item-show6.slider-item-reveal .slider-container>*{scroll-snap-align:unset}.slider-item-show6.slider-item-reveal .slider-container>::before{content:" ";display:block;position:absolute;left:calc((var(--swiffy-slider-item-gap)/2)*-1);top:0;width:1px;height:1px;scroll-snap-align:center}.slider-nav::after,.slider-nav::before{width:2rem;height:2rem;padding:.3rem}.slider-nav-round .slider-nav::after,.slider-nav-square .slider-nav::after{width:1.75rem;height:1.75rem;margin:.125rem}.slider-nav-outside .slider-container,.slider-nav-outside-expand .slider-container{margin:0 2rem}.slider-nav-outside-expand .slider-container{margin:0 var(--swiffy-slider-nav-outside-size)}.slider-nav-outside-expand .slider-nav{padding:0}.slider-indicators-round .slider-indicators>*,.slider-indicators-round.slider-indicators>*,.slider-indicators-square .slider-indicators>*,.slider-indicators-square.slider-indicators>*{width:.3rem;height:.3rem}.slider-indicators{margin-bottom:.5rem;display:none}.slider-nav-scrollbar .slider-indicators{margin-bottom:0}.slider-indicators>*{width:1rem;height:.125rem;border-width:.25rem}.slider-indicators-sm .slider-indicators,.slider-indicators-sm.slider-indicators{display:flex}}@media (max-width:48rem){.slider-item-show6 .slider-container{grid-auto-columns:calc(50% - (var(--swiffy-slider-item-gap)/ 2))}.slider-item-show6.slider-item-reveal .slider-container{grid-auto-columns:calc(50% - (var(--swiffy-slider-item-gap)/ 2) - 1.5rem)}}@media (hover:none){.swiffy-slider.slider-nav-touch .slider-nav{visibility:visible}.swiffy-slider:not(.slider-nav-touch).slider-nav-outside .slider-container,.swiffy-slider:not(.slider-nav-touch).slider-nav-outside-expand .slider-container{margin:0 0}.slider-item-nosnap-touch{--swiffy-slider-snap-align:unset}}
/*# sourceMappingURL=swiffy-slider.min.css.map */
In the editor.scss
file, we’ll write the styles specifically related to the block editor.
editor.scss complete code
.wpcookie-testimonial-wrapper > div > div {
display: flex;
flex-direction: row;
gap: 5px;
overflow-x: scroll;
}
.wpcookie-testimonial-wrapper figure {
display: flex!important;
flex-direction: column!important;
align-content: center!important;
align-items: center!important;
}
.wpcookie-testimonial-wrapper figcaption {
width: 200px!important;
}
div.my-custom-group {
max-width: 600px;
}
If this article is difficult for you to read in text, you can watch the video version below.
Hi, this code seems to be completely wrong? It\’s also very different from the code in the plugin available for download. Has it not been updated recently?
Hi Eli, thank you for your comment!
The code in the post is part of the development process and includes files like block.json, index.js, edit.js, save.js, and render.php. To get the final plugin, you’ll need to build the block using @wordpress/create-block or a similar setup in your development environment (e.g., VS Code with Node.js). Once you run the build process, it compiles the code into the format you see in the downloadable plugin.