Animation
Scroll Highlight Animation Override for Framer
This is a Framer implementation of the scroll highlight animation, shared by Jhey Tompkins. Scroll down to learn how you can apply it to your Framer websites.
About the resource
This is a Framer code override that you can apply to any text layer to achieve the scroll highlight animation effect.
Here's how it works: it looks for parts of your text that have an underline decoration. So, if you want to highlight a sentence, you need to select it, head over to styles, hit the plus button, and add an underline decoration style.
About the resource
This is a Framer code override that you can apply to any text layer to achieve the scroll highlight animation effect.
Here's how it works: it looks for parts of your text that have an underline decoration. So, if you want to highlight a sentence, you need to select it, head over to styles, hit the plus button, and add an underline decoration style.
About the resource
This is a Framer code override that you can apply to any text layer to achieve the scroll highlight animation effect.
Here's how it works: it looks for parts of your text that have an underline decoration. So, if you want to highlight a sentence, you need to select it, head over to styles, hit the plus button, and add an underline decoration style.
Next, you ought to copy the code override (from below) and implement it into your project. You can do this by heading to assets, scrolling down to code, and clicking the plus button.
As you can see, the code override contains three "export function" instances. Each one presents the same effect, but with different highlight colors.
Below, you will see sections highlighted in blue where you can modify the highlight color. You're required to provide the RGBA values of the color in these spots, where "0.5" represents 50% opacity.
The part of the code highlighted in orange represent place where you can modify the color of the highlighted text. If you prefer to retain the text color, you can simply replace this with "color: inherit;".
Next, you ought to copy the code override (from below) and implement it into your project. You can do this by heading to assets, scrolling down to code, and clicking the plus button.
As you can see, the code override contains three "export function" instances. Each one presents the same effect, but with different highlight colors.
Below, you will see sections highlighted in blue where you can modify the highlight color. You're required to provide the RGBA values of the color in these spots, where "0.5" represents 50% opacity.
The part of the code highlighted in orange represent place where you can modify the color of the highlighted text. If you prefer to retain the text color, you can simply replace this with "color: inherit;".
Next, you ought to copy the code override (from below) and implement it into your project. You can do this by heading to assets, scrolling down to code, and clicking the plus button.
As you can see, the code override contains three "export function" instances. Each one presents the same effect, but with different highlight colors.
Below, you will see sections highlighted in blue where you can modify the highlight color. You're required to provide the RGBA values of the color in these spots, where "0.5" represents 50% opacity.
The part of the code highlighted in orange represent place where you can modify the color of the highlighted text. If you prefer to retain the text color, you can simply replace this with "color: inherit;".
Code override for scroll highlight animation
Feel free to copy the code from below and create a code override for yourself in your Framer project.
Code override for scroll highlight animation
Feel free to copy the code from below and create a code override for yourself in your Framer project.
Code override for scroll highlight animation
Feel free to copy the code from below and create a code override for yourself in your Framer project.
import { ComponentType, useEffect, useState } from "react"
export function withHighlightColor1(Component): ComponentType {
return (props: any) => {
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
const elements = document.querySelectorAll(
`.${props.className} [style*="--framer-text-decoration"]`
)
// Now, let's add a parent <mark> tag to the selected elements
elements.forEach((element) => {
element.style.removeProperty("--framer-text-decoration")
const markElement = document.createElement("mark")
const spanElement = document.createElement("span")
// Wrap the element with mark
element.parentNode.insertBefore(markElement, element)
markElement.appendChild(element)
// Wrap the content of the element with span
while (element.firstChild) {
spanElement.appendChild(element.firstChild)
}
element.appendChild(spanElement)
setIsVisible(true)
})
const handle = (entries) => {
entries.forEach((entry) => {
const highlightedValue =
entry.target.style.getPropertyValue("--highlighted")
console.log("handle")
if (!highlightedValue || parseInt(highlightedValue) === 0) {
entry.target.style.setProperty(
"--highlighted",
entry.isIntersecting ? "1" : "0"
)
}
})
}
const observer = new IntersectionObserver(handle, {
threshold: 1.0,
})
elements.forEach((M) => observer.observe(M))
}, [props])
return (
<>
<style>{`
.${props.className} mark {
--highlighted: 0;
--highlight: rgba(0, 128, 255, 0.5);
background: transparent;
}
.${props.className} mark span {
background: linear-gradient(120deg, var(--highlight) 50%, transparent 50%) 110% 0 / 200% 100% no-repeat;
background-position: calc((1 - var(--highlighted)) * 110%) 0;
transition: background-position 1s;
color: white;
}
`}</style>
<Component
{...props}
style={!isVisible ? { opacity: 0 } : {}}
/>
</>
)
}
}
export function withHighlightColor2(Component): ComponentType {
return (props: any) => {
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
const elements = document.querySelectorAll(
`.${props.className} [style*="--framer-text-decoration"]`
)
// Now, let's add a parent <mark> tag to the selected elements
elements.forEach((element) => {
element.style.removeProperty("--framer-text-decoration")
const markElement = document.createElement("mark")
const spanElement = document.createElement("span")
// Wrap the element with mark
element.parentNode.insertBefore(markElement, element)
markElement.appendChild(element)
// Wrap the content of the element with span
while (element.firstChild) {
spanElement.appendChild(element.firstChild)
}
element.appendChild(spanElement)
setIsVisible(true)
})
const handle = (entries) => {
entries.forEach((entry) => {
const highlightedValue =
entry.target.style.getPropertyValue("--highlighted")
if (!highlightedValue || parseInt(highlightedValue) === 0) {
entry.target.style.setProperty(
"--highlighted",
entry.isIntersecting ? "1" : "0"
)
}
})
}
const observer = new IntersectionObserver(handle, {
threshold: 1.0,
})
elements.forEach((M) => observer.observe(M))
}, [props])
return (
<>
<style>{`
.${props.className} mark {
--highlighted: 0;
--highlight: rgba(255, 195, 0, 0.8);
background: transparent;
}
.${props.className} mark span {
background: linear-gradient(120deg, var(--highlight) 50%, transparent 50%) 110% 0 / 200% 100% no-repeat;
background-position: calc((1 - var(--highlighted)) * 110%) 0;
transition: background-position 1s;
color: black;
}
`}</style>
<Component
{...props}
style={!isVisible ? { opacity: 0 } : {}}
/>
</>
)
}
}
export function withHighlightColor3(Component): ComponentType {
return (props: any) => {
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
const elements = document.querySelectorAll(
`.${props.className} [style*="--framer-text-decoration"]`
)
// Now, let's add a parent <mark> tag to the selected elements
elements.forEach((element) => {
element.style.removeProperty("--framer-text-decoration")
const markElement = document.createElement("mark")
const spanElement = document.createElement("span")
// Wrap the element with mark
element.parentNode.insertBefore(markElement, element)
markElement.appendChild(element)
// Wrap the content of the element with span
while (element.firstChild) {
spanElement.appendChild(element.firstChild)
}
element.appendChild(spanElement)
setIsVisible(true)
})
const handle = (entries) => {
entries.forEach((entry) => {
const highlightedValue =
entry.target.style.getPropertyValue("--highlighted")
if (!highlightedValue || parseInt(highlightedValue) === 0) {
entry.target.style.setProperty(
"--highlighted",
entry.isIntersecting ? "1" : "0"
)
}
})
}
const observer = new IntersectionObserver(handle, {
threshold: 1.0,
})
elements.forEach((M) => observer.observe(M))
}, [props])
return (
<>
<style>{`
.${props.className} mark {
--highlighted: 0;
--highlight: rgba(140, 0, 255, 0.5);
background: transparent;
}
.${props.className} mark span {
background: linear-gradient(120deg, var(--highlight) 50%, transparent 50%) 110% 0 / 200% 100% no-repeat;
background-position: calc((1 - var(--highlighted)) * 110%) 0;
transition: background-position 1s;
color: white;
}
`}</style>
<Component
{...props}
style={!isVisible ? { opacity: 0 } : {}}
/>
</>
)
}
}
import { ComponentType, useEffect, useState } from "react"
export function withHighlightColor1(Component): ComponentType {
return (props: any) => {
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
const elements = document.querySelectorAll(
`.${props.className} [style*="--framer-text-decoration"]`
)
// Now, let's add a parent <mark> tag to the selected elements
elements.forEach((element) => {
element.style.removeProperty("--framer-text-decoration")
const markElement = document.createElement("mark")
const spanElement = document.createElement("span")
// Wrap the element with mark
element.parentNode.insertBefore(markElement, element)
markElement.appendChild(element)
// Wrap the content of the element with span
while (element.firstChild) {
spanElement.appendChild(element.firstChild)
}
element.appendChild(spanElement)
setIsVisible(true)
})
const handle = (entries) => {
entries.forEach((entry) => {
const highlightedValue =
entry.target.style.getPropertyValue("--highlighted")
console.log("handle")
if (!highlightedValue || parseInt(highlightedValue) === 0) {
entry.target.style.setProperty(
"--highlighted",
entry.isIntersecting ? "1" : "0"
)
}
})
}
const observer = new IntersectionObserver(handle, {
threshold: 1.0,
})
elements.forEach((M) => observer.observe(M))
}, [props])
return (
<>
<style>{`
.${props.className} mark {
--highlighted: 0;
--highlight: rgba(0, 128, 255, 0.5);
background: transparent;
}
.${props.className} mark span {
background: linear-gradient(120deg, var(--highlight) 50%, transparent 50%) 110% 0 / 200% 100% no-repeat;
background-position: calc((1 - var(--highlighted)) * 110%) 0;
transition: background-position 1s;
color: white;
}
`}</style>
<Component
{...props}
style={!isVisible ? { opacity: 0 } : {}}
/>
</>
)
}
}
export function withHighlightColor2(Component): ComponentType {
return (props: any) => {
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
const elements = document.querySelectorAll(
`.${props.className} [style*="--framer-text-decoration"]`
)
// Now, let's add a parent <mark> tag to the selected elements
elements.forEach((element) => {
element.style.removeProperty("--framer-text-decoration")
const markElement = document.createElement("mark")
const spanElement = document.createElement("span")
// Wrap the element with mark
element.parentNode.insertBefore(markElement, element)
markElement.appendChild(element)
// Wrap the content of the element with span
while (element.firstChild) {
spanElement.appendChild(element.firstChild)
}
element.appendChild(spanElement)
setIsVisible(true)
})
const handle = (entries) => {
entries.forEach((entry) => {
const highlightedValue =
entry.target.style.getPropertyValue("--highlighted")
if (!highlightedValue || parseInt(highlightedValue) === 0) {
entry.target.style.setProperty(
"--highlighted",
entry.isIntersecting ? "1" : "0"
)
}
})
}
const observer = new IntersectionObserver(handle, {
threshold: 1.0,
})
elements.forEach((M) => observer.observe(M))
}, [props])
return (
<>
<style>{`
.${props.className} mark {
--highlighted: 0;
--highlight: rgba(255, 195, 0, 0.8);
background: transparent;
}
.${props.className} mark span {
background: linear-gradient(120deg, var(--highlight) 50%, transparent 50%) 110% 0 / 200% 100% no-repeat;
background-position: calc((1 - var(--highlighted)) * 110%) 0;
transition: background-position 1s;
color: black;
}
`}</style>
<Component
{...props}
style={!isVisible ? { opacity: 0 } : {}}
/>
</>
)
}
}
export function withHighlightColor3(Component): ComponentType {
return (props: any) => {
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
const elements = document.querySelectorAll(
`.${props.className} [style*="--framer-text-decoration"]`
)
// Now, let's add a parent <mark> tag to the selected elements
elements.forEach((element) => {
element.style.removeProperty("--framer-text-decoration")
const markElement = document.createElement("mark")
const spanElement = document.createElement("span")
// Wrap the element with mark
element.parentNode.insertBefore(markElement, element)
markElement.appendChild(element)
// Wrap the content of the element with span
while (element.firstChild) {
spanElement.appendChild(element.firstChild)
}
element.appendChild(spanElement)
setIsVisible(true)
})
const handle = (entries) => {
entries.forEach((entry) => {
const highlightedValue =
entry.target.style.getPropertyValue("--highlighted")
if (!highlightedValue || parseInt(highlightedValue) === 0) {
entry.target.style.setProperty(
"--highlighted",
entry.isIntersecting ? "1" : "0"
)
}
})
}
const observer = new IntersectionObserver(handle, {
threshold: 1.0,
})
elements.forEach((M) => observer.observe(M))
}, [props])
return (
<>
<style>{`
.${props.className} mark {
--highlighted: 0;
--highlight: rgba(140, 0, 255, 0.5);
background: transparent;
}
.${props.className} mark span {
background: linear-gradient(120deg, var(--highlight) 50%, transparent 50%) 110% 0 / 200% 100% no-repeat;
background-position: calc((1 - var(--highlighted)) * 110%) 0;
transition: background-position 1s;
color: white;
}
`}</style>
<Component
{...props}
style={!isVisible ? { opacity: 0 } : {}}
/>
</>
)
}
}
import { ComponentType, useEffect, useState } from "react"
export function withHighlightColor1(Component): ComponentType {
return (props: any) => {
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
const elements = document.querySelectorAll(
`.${props.className} [style*="--framer-text-decoration"]`
)
// Now, let's add a parent <mark> tag to the selected elements
elements.forEach((element) => {
element.style.removeProperty("--framer-text-decoration")
const markElement = document.createElement("mark")
const spanElement = document.createElement("span")
// Wrap the element with mark
element.parentNode.insertBefore(markElement, element)
markElement.appendChild(element)
// Wrap the content of the element with span
while (element.firstChild) {
spanElement.appendChild(element.firstChild)
}
element.appendChild(spanElement)
setIsVisible(true)
})
const handle = (entries) => {
entries.forEach((entry) => {
const highlightedValue =
entry.target.style.getPropertyValue("--highlighted")
console.log("handle")
if (!highlightedValue || parseInt(highlightedValue) === 0) {
entry.target.style.setProperty(
"--highlighted",
entry.isIntersecting ? "1" : "0"
)
}
})
}
const observer = new IntersectionObserver(handle, {
threshold: 1.0,
})
elements.forEach((M) => observer.observe(M))
}, [props])
return (
<>
<style>{`
.${props.className} mark {
--highlighted: 0;
--highlight: rgba(0, 128, 255, 0.5);
background: transparent;
}
.${props.className} mark span {
background: linear-gradient(120deg, var(--highlight) 50%, transparent 50%) 110% 0 / 200% 100% no-repeat;
background-position: calc((1 - var(--highlighted)) * 110%) 0;
transition: background-position 1s;
color: white;
}
`}</style>
<Component
{...props}
style={!isVisible ? { opacity: 0 } : {}}
/>
</>
)
}
}
export function withHighlightColor2(Component): ComponentType {
return (props: any) => {
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
const elements = document.querySelectorAll(
`.${props.className} [style*="--framer-text-decoration"]`
)
// Now, let's add a parent <mark> tag to the selected elements
elements.forEach((element) => {
element.style.removeProperty("--framer-text-decoration")
const markElement = document.createElement("mark")
const spanElement = document.createElement("span")
// Wrap the element with mark
element.parentNode.insertBefore(markElement, element)
markElement.appendChild(element)
// Wrap the content of the element with span
while (element.firstChild) {
spanElement.appendChild(element.firstChild)
}
element.appendChild(spanElement)
setIsVisible(true)
})
const handle = (entries) => {
entries.forEach((entry) => {
const highlightedValue =
entry.target.style.getPropertyValue("--highlighted")
if (!highlightedValue || parseInt(highlightedValue) === 0) {
entry.target.style.setProperty(
"--highlighted",
entry.isIntersecting ? "1" : "0"
)
}
})
}
const observer = new IntersectionObserver(handle, {
threshold: 1.0,
})
elements.forEach((M) => observer.observe(M))
}, [props])
return (
<>
<style>{`
.${props.className} mark {
--highlighted: 0;
--highlight: rgba(255, 195, 0, 0.8);
background: transparent;
}
.${props.className} mark span {
background: linear-gradient(120deg, var(--highlight) 50%, transparent 50%) 110% 0 / 200% 100% no-repeat;
background-position: calc((1 - var(--highlighted)) * 110%) 0;
transition: background-position 1s;
color: black;
}
`}</style>
<Component
{...props}
style={!isVisible ? { opacity: 0 } : {}}
/>
</>
)
}
}
export function withHighlightColor3(Component): ComponentType {
return (props: any) => {
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
const elements = document.querySelectorAll(
`.${props.className} [style*="--framer-text-decoration"]`
)
// Now, let's add a parent <mark> tag to the selected elements
elements.forEach((element) => {
element.style.removeProperty("--framer-text-decoration")
const markElement = document.createElement("mark")
const spanElement = document.createElement("span")
// Wrap the element with mark
element.parentNode.insertBefore(markElement, element)
markElement.appendChild(element)
// Wrap the content of the element with span
while (element.firstChild) {
spanElement.appendChild(element.firstChild)
}
element.appendChild(spanElement)
setIsVisible(true)
})
const handle = (entries) => {
entries.forEach((entry) => {
const highlightedValue =
entry.target.style.getPropertyValue("--highlighted")
if (!highlightedValue || parseInt(highlightedValue) === 0) {
entry.target.style.setProperty(
"--highlighted",
entry.isIntersecting ? "1" : "0"
)
}
})
}
const observer = new IntersectionObserver(handle, {
threshold: 1.0,
})
elements.forEach((M) => observer.observe(M))
}, [props])
return (
<>
<style>{`
.${props.className} mark {
--highlighted: 0;
--highlight: rgba(140, 0, 255, 0.5);
background: transparent;
}
.${props.className} mark span {
background: linear-gradient(120deg, var(--highlight) 50%, transparent 50%) 110% 0 / 200% 100% no-repeat;
background-position: calc((1 - var(--highlighted)) * 110%) 0;
transition: background-position 1s;
color: white;
}
`}</style>
<Component
{...props}
style={!isVisible ? { opacity: 0 } : {}}
/>
</>
)
}
}