Explore our consultancy services designed to drive meaningful change. We offer expert guidance in carbon reduction, modern slavery compliance, sustainable procurement, and crafting robust sustainability strategies. Our tailored solutions help organisations improve environmental performance, ensure human rights, and integrate social value into their operations. Dive deeper into our services to see how we can support your journey towards sustainability and operational excellence.
Learn MoreMeasure and enhance your sustainability efforts with our advanced Sustainability Tool. Our platform offers comprehensive solutions for collecting, managing, and reporting sustainability data. From ESG performance and carbon footprint analysis to EDI metrics, our tools provide the insights needed to refine your strategy and drive impactful change across your operations and supply chain. Discover how our solutions streamline reporting and support your journey towards sustainability.
Learn MoreOur Supply Chain Sustainability School offers customised learning materials, interactive workshops, and expert collaboration. We enhance sustainability skills and knowledge across diverse sectors and regions, supporting organisations in improving sustainability in their supply chains.
Learn More
@import "../../resources/scss/util/variables";
@import "../../resources/scss/util/mixins";
.block-solutions-animated-block {
padding-top: clamp(40px, 8vw, 140px);
padding-bottom: clamp(4rem, 8vw, 140px);
overflow: visible;
@include bp($lg, true) {
padding-left: 1rem;
padding-right: 1rem;
}
@media (min-width: 1100px) and (max-height: 559px) {
padding-bottom: 1.5rem;
padding-top: 2rem;
}
.container {
position: relative;
max-width: 98%;
padding-top: calc(var(--site-header-height) / 2);
@include bp($lg, true) {
position: initial;
}
@media (min-width: 1100px) and (max-height: 559px) {
padding-top: 2.5rem;
}
}
&__skip {
position: absolute;
bottom: 1rem;
right: 1rem;
color: var(--white);
border-color: var(--white) !important;
text-decoration: none !important;
z-index: 2;
@include bp($lg, true) {
/* bottom: 0; */
left: 2rem;
right: unset;
}
@media (max-width: $lg) and (min-height: 700px) {
left: 3rem;
}
&:hover {
background-color: var(--secondary);
color: var(--primary);
}
}
&__navigation {
position: absolute;
top: 8rem;
right: 2rem;
z-index: 2;
display: flex;
flex-direction: row;
gap: 1rem;
button {
color: var(--white);
width: 44px;
height: 44px;
border-radius: 100px;
border: solid 1px var(--text-color);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: .3s ease-in-out;
background-position: center;
background-repeat: no-repeat;
background-size: 24px;
background-image: url("data:image/svg+xml,%3Csvg width='27' height='18' viewBox='0 0 27 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7.16895 16.7598L0.768945 8.75977L7.16895 0.759765' stroke='%23fff'/%3E%3Cpath d='M0.768555 8.75977L26.7686 8.75977' stroke='%23fff'/%3E%3C/svg%3E%0A");
&.next {
transform: rotate(180deg);
}
&:hover,
&:focus {
background-color: var(--text-color);
background-position: center;
background-repeat: no-repeat;
background-size: 24px;
background-image: url("data:image/svg+xml,%3Csvg width='27' height='18' viewBox='0 0 27 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7.16895 16.7598L0.768945 8.75977L7.16895 0.759765' stroke='%23383551'/%3E%3Cpath d='M0.768555 8.75977L26.7686 8.75977' stroke='%23383551'/%3E%3C/svg%3E%0A") !important;
}
}
@include bp($lg, true) {
bottom: 1rem;
top: unset;
right: 2rem;
}
@media (max-width: $lg) and (min-height: 700px) {
right: 3rem;
}
}
.row {
height: 80vh;
@media (min-width: 1100px) and (max-height: 559px) {
height: 95vh;
}
.col-12 {
@include bp($lg, true) {
height: 45vh;
}
}
}
.solution-wrapper,
.case-study-wrapper {
position: relative;
display: flex;
align-items: center;
z-index: 2;
&__inner {
transition: opacity .75s, visibility .75s;
}
.heading {
font-weight: 600;
@media (min-width: 1100px) and (max-height: 559px) {
font-size: 2rem;
}
}
.heading,
a {
position: relative;
z-index: 2;
}
}
.solution-wrapper {
justify-content: left;
font-size: 40px;
width: 100%;
&__inner {
opacity: 0;
position: absolute;
transform: translateY(40vh);
padding: rem-calc(0 80);
transition: opacity .75s, visibility .75s;
@include bp($lg, true) {
transform: translateY(50%);
padding: rem-calc(25 8);
}
@media (max-width: $lg) and (min-height: 700px) {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: calc(50vh - var(--site-header-height));
p.content {
margin-bottom: 2rem !important;
font-size: 1.25rem !important;
text-align: center;
}
.heading {
text-align: center;
}
}
@media (min-width: 1100px) and (max-height: 700px) {
padding-left: rem-calc(70);
padding-right: rem-calc(70);
transform: translateY(45vh);
}
.heading {
font-weight: 600;
@media (min-width: 1100px) and (max-height: 700px) {
font-size: 2rem;
}
@include bp($sm, true) {
font-size: 1.4rem;
}
}
p.content {
font-size: clamp(14px, 2vw, 22px);
margin-bottom: 0;
@media (min-width: 1100px) and (max-height: 700px) {
font-size: 1.25rem;
}
@include bp($sm, true) {
font-size: 1rem !important;
}
}
a:hover {
color: var(--white);
}
}
}
.case-study-wrapper {
justify-content: center;
width: 100%;
&__inner {
opacity: 0;
text-align: center;
position: absolute;
transform: translateY(40vh);
padding: 0 7.5rem;
@media (min-width: 1100px) and (max-height: 700px) {
transform: translateY(45vh);
}
.btn {
color: var(--white);
text-transform: uppercase;
text-decoration: none;
font-weight: 600;
margin-top: 0.5rem;
&:after {
content: '';
display: inline-block;
width: rem-calc(30);
height: rem-calc(30);
border-radius: 50%;
background-color: transparent;
border: 1px solid var(--white);
position: absolute;
left: 110%;
/* top: 50%; */
bottom: -30%;
transition: all 0.2s ease-out;
background-image: url("data:image/svg+xml,%3Csvg width='18' height='27' viewBox='0 0 18 27' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.870117 19.6006L8.87012 26.0006L16.8701 19.6006' stroke='%23fff'/%3E%3Cpath d='M8.87011 26L8.87012 0' stroke='%23fff'/%3E%3C/svg%3E%0A");
background-size: 60% 60%;
background-position: center;
background-repeat: no-repeat;
transform: rotate(-90deg);
}
&:hover {
color: var(--secondary);
}
}
@include bp($lg, true) {
transform: translateY(20vh);
padding: rem-calc(25 40);
}
}
.heading {
/* clamp to 4 lines */
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
@include bp($lg, true) {
max-width: rem-calc(400);
}
}
}
.has-secondary-background-color {
border-top-left-radius: 40px;
border-bottom-left-radius: 40px;
position: relative;
@include bp($lg, true) {
border-radius: 40px 40px 0 0 ;
}
.heading {
color: var(--primary);
}
}
.has-primary-background-color {
--text-color: var(--white);
border-top-right-radius: 40px;
border-bottom-right-radius: 40px;
position: relative;
@include bp($lg, true) {
border-radius: 0 0 40px 40px;
padding-bottom: 4rem;
}
.heading {
color: var(--white);
}
}
.case-study-image-wrapper {
z-index: -1;
opacity: 0.2;
pointer-events: none;
border-radius: 50px;
img {
border-radius: 50px;
}
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle, transparent 40%, var(--primary) 40.001%);
z-index: 1;
pointer-events: none;
border-radius: 50px;
}
}
&__animation {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 0;
pointer-events: none;
svg {
width: 100%;
height: 100%;
max-width: 90%;
max-height: 90%;
circle {
transition: stroke-dasharray 1s;
}
#white-path {
stroke-width: 2px;
stroke: var(--white);
fill: transparent;
stroke-dasharray: 100 0;
stroke-opacity: 0.6;
}
#dotted-path {
stroke-width: 2px;
stroke-dasharray: 50 50;
stroke: var(--secondary);
transform: rotate(-90deg);
fill: var(--primary);
}
#solid-path {
stroke-width: 8px;
/* stroke-dasharray: calc(1 * var(--solution-progress)), calc(100 - (1 * var(--solution-progress))); */
stroke: var(--secondary);
transform: rotate(-90deg);
transform-origin: center;
fill: transparent;
}
.solution-circle {
fill: var(--white);
transition: r 1s;
&.active {
r: 30;
fill: var(--secondary);
}
&.been-active {
fill: var(--secondary);
}
}
}
#targets circle {
cx: 500;
cy: 50;
stroke-width: 2px;
}
.arc {
transition: transform 1s;
transform-origin: center;
}
@include bp($md, true) {
display: none;
}
}
.content {
color: var(--primary);
font-family: 'Source Sans 3', sans-serif;
font-size: clamp(14px, 1vw, 22px);
@include bp($sm, true) {
max-height: rem-calc(230);
overflow: scroll;
font-size: 1rem !important;
}
}
.has-secondary-background-color {
background-color: var(--secondary);
}
}
class SolutionsAnimatedBlock {
block;
hotspotQuantity = 7;
startAt = -90;
center = {x:100, y:100};
radius = 100;
currentIndex = 0;
constructor(block) {
this.block = block;
this.init();
}
init() {
const headings = this.block.querySelectorAll('.solution-wrapper__inner');
const nums = this.block.querySelectorAll('.case-study-wrapper__inner');
const images = this.block.querySelectorAll('.case-study-image-wrapper');
const solutionCircles = this.block.querySelectorAll('.solution-circle');
const arc = this.block.querySelector('.arc');
const block = this.block;
const skip = this.block.querySelector('.block-solutions-animated-block__skip');
const numOfTransitions = headings.length
const singleDuration = 750;
this.block.style.setProperty('--solution-progress', '0');
const solidPath = this.block.querySelector('.solid-path');
solidPath.style.strokeDasharray = `0 100`;
block.setAttribute('data-current-index', '0');
arc.style.transform = `rotate(0deg)`;
const scrollBuffer = (4 * window.innerHeight) / headings.length;
if(!headings.length) return;
const self = this;
headings.forEach((heading, index) => {
if(index < 1) {
solutionCircles.forEach((circle, i) => {
if(i < index) {
circle.classList.add('been-active');
} else if(index === i) {
circle.classList.add('active');
} else {
circle.classList.remove('active');
circle.classList.remove('been-active');
}
});
solutionCircles[index].classList.add('active');
nums[index].style.opacity = 1;
headings[index].style.opacity = 1;
images[index].style.opacity = 0.2;
block.setAttribute('data-current-index', index);
}
});
function setArc(index) {
solutionCircles[index].classList.add('active');
// we need to rotate .arc clockwise by 360deg/numOfTransitions * i
arc.style.transform = `rotate(${360 / numOfTransitions * index}deg)`;
// Use self.block instead of this.block
self.block.style.setProperty('--solution-progress', `${(index / numOfTransitions) * 100}`);
const progress = (index / numOfTransitions) * 100;
// Use self.block instead of this.block
const solidPath = self.block.querySelector('.solid-path');
solidPath.style.strokeDasharray = `${progress} ${(100 - progress)}`;
}
// Add event listeners for the .prev and .next buttons to scroll through the timeline by one duration
const prevButton = this.block.querySelector('.prev');
const nextButton = this.block.querySelector('.next');
prevButton.addEventListener('click', (e) => {
e.preventDefault();
const currentIndex = parseInt(block.getAttribute('data-current-index'));
console.log('currentIndex', currentIndex);
/*
window.scrollTo({
top : tl.scrollTrigger.labelToScroll('step-'+prevIndex),
behavior: 'smooth',
});
*/
if (currentIndex === 0) return;
const prevIndex = currentIndex - 1;
nums[currentIndex].style.opacity = 0;
headings[currentIndex].style.opacity = 0;
images[currentIndex].style.opacity = 0;
nums[prevIndex].style.opacity = 1;
headings[prevIndex].style.opacity = 1;
images[prevIndex].style.opacity = 0.2;
if (prevIndex === 0) {
prevButton.classList.add('disabled');
} else {
prevButton.classList.remove('disabled');
}
nextButton.classList.remove('disabled');
setArc(prevIndex);
block.setAttribute('data-current-index', prevIndex);
});
nextButton.addEventListener('click', (e) => {
e.preventDefault();
const currentIndex = parseInt(block.getAttribute('data-current-index'));
// if the current index is the last one, we don't want to go to the next one
if(currentIndex === numOfTransitions - 1) return;
const nextIndex = currentIndex + 1;
/*
window.scrollTo({
top: tl.scrollTrigger.labelToScroll('step-'+nextIndex) + scrollBuffer,
behavior: 'smooth',
});
*/
// set data-current-index to the next index
nums[currentIndex].style.opacity = 0;
headings[currentIndex].style.opacity = 0;
images[currentIndex].style.opacity = 0;
nums[nextIndex].style.opacity = 1;
headings[nextIndex].style.opacity = 1;
images[nextIndex].style.opacity = 0.2;
if (nextIndex === numOfTransitions - 1) {
nextButton.classList.add('disabled');
} else {
nextButton.classList.remove('disabled');
}
prevButton.classList.remove('disabled');
setArc(nextIndex);
block.setAttribute('data-current-index', nextIndex);
});
// when the block enters the viewport, we want to refresh scrolltrigger - do this using a observer
const observer = new IntersectionObserver((entries) => {
if(entries[0].isIntersecting) {
ScrollTrigger.refresh();
observer.disconnect();
}
});
observer.observe(block);
}
}
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.block-solutions-animated-block').forEach((block) => {
new SolutionsAnimatedBlock(block);
})
});
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "strategiq/solutions-animated-block",
"title": "Solutions Animated Block",
"description": "Example block to be used as a template",
"category": "strategiq",
"icon": "strategiq",
"acf": {
"mode": "preview",
"renderTemplate": "block-solutions-animated-block.php"
},
"supports": {
"anchor": true,
"align": false,
"color": {
"background": true,
"text": false,
"gradients": true
},
"spacing": {
"padding": [
"top",
"bottom"
],
"margin": [
"top",
"bottom"
]
}
},
"example": {
"attributes": {
"mode": "preview",
"data": {
"heading_type": "h2",
"heading_text": "Example - Solutions Animated Block",
"content": "This is some example content to represent what the content will look like"
}
}
},
"style": "file:../../assets/css/solutions-animated-block/block-solutions-animated-block.css",
"viewScript": ["gsap","scrolltrigger", "solutions-animated-block"]
}