CSS - UV Gloss finish example
by PhilQ
HTML
<div id="container">
<div
class="card"
...
SCSS
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #20262E;
padding: 50px;
font-family: Helvetica;
}
#container {
position: relative;
background: #fff;
border-radius: 4px;
padding: 20px;
margin: 0 auto;
width: 400px;
height: 225px;
box-sizing: content-box;
}
.card {
position: relative;
width: 100%;
height: 100%;
display: flex;
background-repeat: no-repeat;
background-size: contain;
background-position: center center;
> div {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
}
}
#gloss1 {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
backdrop-filter: saturate(125%) contrast(175%);
// mask-image: url(#test);
// mask-image: url(https://mdn.github.io/shared-assets/images/examples/mask-star.svg);
// mask-type: luminance;
mask-mode: luminance;
mask-repeat: no-repeat;
mask-position: 50% 50%;
mask-size: contain;
// mask-size: 50px 50px;
// mask-image:
// linear-gradient(45deg, black 25%, transparent 25%, transparent 75%, black 75%, black),
// linear-gradient(45deg, black 25%, transparent 25%, transparent 75%, black 75%, black),
// ;
// mask-position: 0 0, 50px 50px;
}
#gloss2, #gloss3 {
--color: rgba(255, 255, 255, 0.80);
z-index: 2;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
mask-mode: luminance;
mask-repeat: no-repeat;
mask-position: 50% 50%;
mask-size: contain;
background-image: linear-gradient(to right, rgba(255, 255, 255, 0) 47%, var(--color) 48%, var(--color) 52%, rgba(255, 255, 255, 0) 53%);
background-position: 0% 0%;
background-repeat: no-repeat;
background-size: 300% 100%;
animation-name: move-x;
animation-duration: 5s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-direction: alternate;
}
#gloss3 {
...
JavaScript
const setSVGMask = (elementSelector, maskSelector, invert = false, includeLuminanceToAlpha = false) => {
const svgNS = "http://www.w3.org/2000/svg"
const svg = document.createElementNS(svgNS, "svg")
const g = document.createElementNS(svgNS, "g")
svg.setAttribute("width", "200")
svg.setAttribute("height", "200")
svg.setAttribute("viewBox", "0 0 200 200")
svg.setAttribute("overflow", "visible")
if (includeLuminanceToAlpha) {
// create a <filter><feColorMatrix type="luminanceToAlpha"/>
const alpha = document.createElementNS(svgNS, "feColorMatrix")
alpha.setAttribute("type", "luminanceToAlpha")
const filter = document.createElementNS(svgNS, "filter")
filter.setAttribute("id", "alpha")
filter.setAttribute("filterUnits", "userSpaceOnUse");
filter.append(alpha)
svg.append(filter)
// luminanceToAlpha disregards semi-transparent pixels,
// so we add a black background to have no such thing
const bg = document.createElementNS(svgNS, "rect")
bg.setAttribute("width", "100%")
bg.setAttribute("height", "100%")
bg.setAttribute("fill", "#000000")
g.append(bg)
}
// apply the filter on all the <mask> content
if (invert || includeLuminanceToAlpha) {
// g.setAttribute("filter", `${invert ? "invert() " : ""}${includeLuminanceToAlpha ? "url(#alpha)" : ""}`)
g.setAttribute("filter", includeLuminanceToAlpha ? "url(#alpha)" : "")
}
const mask = document.querySelector(maskSelector).cloneNode(true)
g.append(...mask.children)
svg.append(g)
const markup = new XMLSerializer().serializeToString(svg)
let maskImage = "url('data:image/svg+xml;charset=utf8," + encodeURIComponent(markup) + "')"
document.querySelectorAll(elementSelector).forEach((el) => {
if (invert) {
const compStyles = window.getComputedStyle(el);
el.style.setProperty("mask-size", `${compStyles.getPropertyValue("mask-size")}, 100% 100%`)
...