Adding subtle and visually appealing effects on the web can greatly improve user experience and engagement. In this tutorial, we’ll walk you through creating a glowing SVG text combined with a marquee effect using only HTML, CSS, and the clip-path property.
You can see this effect in action on the website of Darkroom by Kaploom ® Creative House, an award-winning design membership service.
In this tutorial, we’ll show you how to prepare your design assets in Figma and create an animation using a combination of SVG and CSS. The final result looks like this:
So let’s get started!
assets
Our assets include Three things:
- Image to use for glow effect
- Blurred versions of these images to preserve the shape of the sparkles
- SVG format title
image
Make sure to export your images in a format that supports transparency (e.g. PNG, WEBP, AVIF, etc.).
Blurred
Then I add a “layer blur” to each image in Figma and export them individually, exporting them at a relatively small resolution (50px wide) because browser image rendering blurs images by default.
Preparing the SVG title
SVG titles are used in a marquee effect called “rails”, which requires at least one duplicate of the title so that it always covers the entire screen.
Don’t worry about color in this step, as we will apply it through code and using the blur image we exported earlier.
It’s important to make sure the spacing between your title and duplicates matches the spacing of your end frames, so you get a smooth marquee effect without any unexpected jumps later on.
When exporting SVG from Figma, apply a border to your title and check the “Simplify Strokes” box in the export settings.
Path conversion
Now that we have our SVG, we need to convert the paths from userSpaceOnUse to objectBoundingBox, which can be done using this online converter.
<svg width="1137" height="104" viewBox="0 0 1137 104" fill="none" xmlns="
<path d="M13.2057 88.8793V89.3793H13.7057H30.9417C40.7369 89.3793 48.6776 81.4387..." fill="white" stroke="white"/>
</svg>
The objectBoundingBox path looks like this:
M0.012,0.867 V0.872 H0.012 H0.027 C0.036,0.872,0.043,0.794,0.043,0.699 V0.314 C0.043,0.218,0.036,0.141,0.027,0.141 H0.012 H0.012 V0.146...
Creating Rails
This component requires the blur image asset you exported earlier and the objectBoundingBox path to use for the SVG mask.
The basic structure of a Rails component is as follows:
<div class="rail">
<div class="rail_container">
<div class="rail_clip">
<div class="rail_color">
<div class="rail_gradients">
/* Blurred images go here */
<img
src="img/gradient-core.png"
width="50"
height="50"
alt="Core Gradient"
class="rail_gradient -core"
/>
<img
src="img/gradient-pro.png"
width="50"
height="50"
alt="Pro Gradient"
class="rail_gradient -pro"
/>
</div>
</div>
</div>
/* Rail sizing used for precise aspect ratio */
<svg
width="1137"
height="104"
viewBox="0 0 1137 104"
fill="none"
xmlns="
class="rail_sizing"
></svg>
/* SVG Mask */
<svg class="rail_mask">
<clipPath id="contentTitle" clipPathUnits="objectBoundingBox">
<path
d="M0.012,0.86 V0.861 H0.012 H0.027 C0.036,0.861,0.042,0.786,0.042,0.693 V0.311 C0.042,0.219,0.036,0.144,0.027,0.144 H0.012 H0.012 V0.145 V0.86 M0.13,0.524 H0.13 V0.525 V0.583 V0.584 L0.13,0.584 L0.167,0.992 H0.15 L0.13,0.77 L0.13,0.769 V0.771 V0.992 H0.118 V0.013 L0.139,0.013 C0.152,0.013,0.162,0.127,0.162,0.268 C0.162,0.409,0.152,0.524,0.139,0.524 H0.13 M0.13,0.144 H0.13 V0.145 V0.392 V0.393 H0.13 L0.139,0.393 C0.145,0.393,0.15,0.337,0.15,0.268 C0.15,0.199,0.145,0.144,0.139,0.144 L0.13,0.144 M0.213,0.013 H0.228 L0.198,0.502 L0.198,0.502 L0.198,0.503 L0.228,0.992 H0.213 L0.188,0.568 L0.188,0.568 H0.188 H0.183 H0.183 V0.569 V0.992 L0.171,0.992 V0.013 L0.183,0.013 V0.436 V0.437 H0.183 H0.188 H0.188 L0.188,0.437 L0.213,0.013 M0.07,0.807 H0.07 L0.07,0.808 L0.067,0.992 H0.055 L0.072,0.013 H0.097 L0.114,0.992 H0.102 L0.099,0.808 L0.099,0.807 H0.099 H0.07 M0.072,0.675 L0.072,0.676 H0.072 H0.096 H0.097 L0.096,0.675 L0.087,0.144 L0.087,0.144 H0.087 H0.082 H0.082 L0.082,0.144 L0.072,0.675 M0.054,0.693 C0.054,0.858,0.042,0.992,0.027,0.992 H0 V0.013 H0.027 C0.042,0.013,0.054,0.147,0.054,0.311 V0.693 M0.512,0.86 V0.861 H0.512 H0.527 C0.536,0.861,0.542,0.786,0.542,0.693 V0.311 C0.542,0.219,0.536,0.144,0.527,0.144 H0.512 H0.512 V0.145 V0.86 M0.63,0.524 H0.63 V0.525 V0.583 V0.584 L0.63,0.584 L0.667,0.992 H0.65 L0.63,0.77 L0.63,0.769 V0.771 V0.992 H0.618 V0.013 L0.639,0.013 C0.652,0.013,0.662,0.127,0.662,0.268 C0.662,0.409,0.652,0.524,0.639,0.524 H0.63 M0.63,0.144 H0.63 V0.145 V0.392 V0.393 H0.63 L0.639,0.393 C0.645,0.393,0.65,0.337,0.65,0.268 C0.65,0.199,0.645,0.144,0.639,0.144 L0.63,0.144 M0.713,0.013 H0.728 L0.698,0.502 L0.698,0.502 L0.698,0.503 L0.728,0.992 H0.713 L0.688,0.568 L0.688,0.568 H0.688 H0.683 H0.683 V0.569 V0.992 L0.671,0.992 V0.013 L0.683,0.013 V0.436 V0.437 H0.683 H0.688 H0.688 L0.688,0.437 L0.713,0.013 M0.57,0.807 H0.57 L0.57,0.808 L0.567,0.992 H0.555 L0.572,0.013 H0.597 L0.614,0.992 H0.602 L0.599,0.808 L0.599,0.807 H0.599 H0.57 M0.572,0.675 L0.572,0.676 H0.573 H0.596 H0.597 L0.597,0.675 L0.587,0.144 L0.587,0.144 H0.587 H0.582 H0.582 L0.582,0.144 L0.572,0.675 M0.554,0.693 C0.554,0.858,0.542,0.992,0.527,0.992 H0.5 V0.013 H0.527 C0.542,0.013,0.554,0.147,0.554,0.311 V0.693 M0.243,0.524 H0.243 V0.525 V0.583 V0.584 L0.243,0.584 L0.28,0.992 H0.263 L0.243,0.77 L0.243,0.769 V0.771 V0.992 H0.231 V0.013 L0.252,0.013 C0.265,0.013,0.275,0.127,0.275,0.268 C0.275,0.409,0.265,0.524,0.252,0.524 H0.243 M0.243,0.144 H0.243 V0.145 V0.392 V0.393 H0.243 L0.252,0.393 C0.258,0.393,0.263,0.337,0.263,0.268 C0.263,0.199,0.258,0.144,0.252,0.144 L0.243,0.144 M0.455,0.013 H0.478 V0.992 H0.466 V0.145 V0.144 H0.466 H0.465 H0.465 L0.465,0.145 L0.45,0.992 H0.428 L0.414,0.145 L0.414,0.144 H0.414 H0.413 H0.413 V0.145 V0.992 H0.401 V0.013 H0.424 L0.438,0.86 L0.438,0.861 H0.438 H0.44 H0.44 L0.44,0.86 L0.455,0.013 M0.383,0.701 V0.305 C0.383,0.211,0.376,0.134,0.367,0.134 C0.359,0.134,0.352,0.211,0.352,0.305 V0.701 C0.352,0.795,0.359,0.872,0.367,0.872 C0.376,0.872,0.383,0.795,0.383,0.701 M0.34,0.305 C0.34,0.138,0.352,0.003,0.367,0.003 C0.383,0.003,0.395,0.138,0.395,0.305 V0.701 C0.395,0.868,0.383,1,0.367,1 C0.352,1,0.34,0.868,0.34,0.701 V0.305 M0.323,0.701 V0.305 C0.323,0.211,0.316,0.134,0.307,0.134 C0.299,0.134,0.292,0.211,0.292,0.305 V0.701 C0.292,0.795,0.299,0.872,0.307,0.872 C0.316,0.872,0.323,0.795,0.323,0.701 M0.28,0.305 C0.28,0.138,0.292,0.003,0.307,0.003 C0.322,0.003,0.335,0.138,0.335,0.305 V0.701 C0.335,0.868,0.322,1,0.307,1 C0.292,1,0.28,0.868,0.28,0.701 V0.305 M0.743,0.524 H0.743 V0.525 V0.583 V0.584 L0.743,0.584 L0.78,0.992 H0.763 L0.743,0.77 L0.743,0.769 V0.771 V0.992 H0.731 V0.013 L0.752,0.013 C0.765,0.013,0.775,0.127,0.775,0.268 C0.775,0.409,0.765,0.524,0.752,0.524 H0.743 M0.743,0.144 H0.743 V0.145 V0.392 V0.393 H0.743 L0.752,0.393 C0.758,0.393,0.763,0.337,0.763,0.268 C0.763,0.199,0.758,0.144,0.752,0.144 L0.743,0.144 M0.955,0.013 H0.978 V0.992 H0.966 V0.145 V0.144 H0.966 H0.965 H0.965 L0.965,0.145 L0.95,0.992 H0.928 L0.914,0.145 L0.914,0.144 H0.914 H0.913 H0.913 V0.145 V0.992 H0.901 V0.013 H0.924 L0.938,0.86 L0.938,0.861 H0.938 H0.94 H0.94 L0.94,0.86 L0.955,0.013 M0.883,0.701 V0.305 C0.883,0.211,0.876,0.134,0.867,0.134 C0.859,0.134,0.852,0.211,0.852,0.305 V0.701 C0.852,0.795,0.859,0.872,0.867,0.872 C0.876,0.872,0.883,0.795,0.883,0.701 M0.84,0.305 C0.84,0.138,0.852,0.003,0.867,0.003 C0.883,0.003,0.895,0.138,0.895,0.305 V0.701 C0.895,0.868,0.883,1,0.867,1 C0.852,1,0.84,0.868,0.84,0.701 V0.305 M0.823,0.701 V0.305 C0.823,0.211,0.816,0.134,0.807,0.134 C0.799,0.134,0.792,0.211,0.792,0.305 V0.701 C0.792,0.795,0.799,0.872,0.807,0.872 C0.816,0.872,0.823,0.795,0.823,0.701 M0.78,0.305 C0.78,0.138,0.792,0.003,0.807,0.003 C0.822,0.003,0.835,0.138,0.835,0.305 V0.701 C0.835,0.868,0.822,1,0.807,1 C0.792,1,0.78,0.868,0.78,0.701 V0.305 M1,1 L1,1 H1 V1"
></path>
</clipPath>
</svg>
</div>
</div>
Masking, Sizing, and Animation
Use CSS for rails masking clip-path
Set the properties and ID and then .rail_clip
class.
.rail_clip
position: absolute;
inset: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
-webkit-clip-path: url("#contentTitle");
clip-path: url("#contentTitle");
To check <strong>clip-path</strong>
Maintains the correct aspect ratio for added <strong>.rail_sizing</strong>
The position is relative and also determines the overall size of the Rail component.
.rail_sizing
position: relative;
opacity: 0;
height: auto;
width: 200vw;
The marquee effect uses a simple CSS animation that loops infinitely, with the speed depending on the width of the SVG and the length of the CSS animation.
.rail_clip
animation: clip-anim 20s linear infinite;
@keyframes clip-anim
from
transform: translateX(0%);
to
transform: translateX(-50%);
Colors and gradients
Now for the fun part: color!
First, apply a background color .rail_color
A class that allows the title to be displayed even without a gradient.
.rail_color
position: absolute;
inset: 0;
height: 100%;
width: 100%;
background-color: #0c0c0e;
Because the whole thing is animated .rail_clip
and .rail_color
You’ll notice that the background color animates out of sight because it’s a child element… we want the background color to be still!
What is needed is: .rail_color
In the opposite direction it looks like this:
.rail_color
animation: color-anim 20s linear infinite;
@keyframes color-anim
from
transform: translateX(0%);
to
transform: translateX(50%);
Now we’re ready to go, we can add our blurred image inside the .rail_color div and position it wherever we want.
Accessibility
Last but not least, let’s add some title text for screen readers to the top of our Rails component and style it accordingly.
<div class="rail">
<div class="screen-reader-text">DARKROOM</div>
<div class="rail_container">
...
</div>
</div>
.screen-reader-text
position: absolute !important;
overflow: hidden;
clip: rect(0 0 0 0);
margin: 0;
padding: 0;
width: 1px;
height: 1px;
border: 0;
Finishing
All that’s left to do is add the main image asset to the Rail component, align it with the masked gradient, and add a little floating animation to each box.
@keyframes float-core
from
transform: translateY(0%);
to
transform: translateY(5%);
@keyframes float-pro
from
transform: translateY(4%);
to
transform: translateY(0%);
To blend better with the Rail component, I added a slight glow behind each box, using the same blur image, this time without the clip mask, but with the opacity turned down significantly.
It’s subtle, but makes a big difference!
Check out the complete demo with intro animation added:
If you want to see the whole site in action, head over to Darkroom.
Inspirational Website Roundup: Webflow Special #5