Scroll Highlight Animation Override for Framer

Scroll Highlight Animation Override for Framer

Nandi Muzsik

How can I improve Framer Uni?

Let me know if there’s a missing feature or something that could be improved.

Share feedback

Nandi Muzsik

How can I improve Framer Uni?

Let me know if there’s a missing feature or something that could be improved.

Share feedback

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.

image of Nandi Muzsik
image of Clement Lionne
profile image of Jhey Tompkins

Created by

Scroll Highlight Animation Override for Framer
Scroll Highlight Animation Override for Framer
Scroll Highlight Animation Override for Framer

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.

adding underline to a text in Framer

Adding underline to a text in Framer.

adding underline to a text in Framer

Adding underline to a text in Framer.

adding underline to a text in Framer

Adding underline to a text in Framer.

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;".

editing the scroll highlight override in Framer

editing the scroll highlight override in Framer

editing the scroll highlight override in Framer

editing the scroll highlight override in Framer

editing the scroll highlight override in Framer

editing the scroll highlight override in Framer

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 } : {}}
                />
            </>
        )
    }
}

Framer Navigator

Learn the fundamentals of Framer for free.

Build your ideas with ease by learning the basics of website building with Framer.

Nandi portrait's background
Nandi's portrait

Framer Navigator

Learn the fundamentals of Framer for free.

Build your ideas with ease by learning the basics of website building with Framer.

Nandi portrait's background
Nandi's portrait

Framer Navigator

Learn the fundamentals of Framer for free.

Build your ideas with ease by learning the basics of website building with Framer.

Nandi portrait's background
Nandi's portrait

More resources

More resources

  • Curved grid layout showcasing a variety of product images

    3D Curved Grid Scroll Animation in Framer

    Animation

    Curved grid layout showcasing a variety of product images

    3D Curved Grid Scroll Animation in Framer

    Animation

    Curved grid layout showcasing a variety of product images

    3D Curved Grid Scroll Animation in Framer

    Animation

  • Framer website homepage with navigation menu and pixelated project previews showcasing web design tool capabilities

    Animated Navigation Bar in Framer

    Animation

    Framer website homepage with navigation menu and pixelated project previews showcasing web design tool capabilities

    Animated Navigation Bar in Framer

    Animation

    Framer website homepage with navigation menu and pixelated project previews showcasing web design tool capabilities

    Animated Navigation Bar in Framer

    Animation