Liquid Chrome Text
The letters turn to polished liquid metal: a mirrored horizon rolls up and down inside them, bright streaks slide across, and a gentle ripple keeps the surface moving like mercury. Comes in chrome, gold and rose.
Liquid metal
How to use it
- Paste the snippet just before
</head>. - Add
data-liquid-chrome-textto a big, bold heading. Dark backgrounds make the shine stand out.
Options
Add any of these attributes to the same element.
data-metal | chrome (default), gold or rose. |
data-speed | How fast the reflections flow. Default 1. |
data-ripple | How much the surface wobbles. Default 0.05 em; 0 keeps the edges still. |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Liquid Chrome Text: the letters look like polished liquid metal. A
// mirrored horizon and bright streaks flow over them, and a gentle ripple
// keeps the surface (and the edges) moving like mercury.
// Usage: <h1 data-liquid-chrome-text>Chrome</h1>
// Optional: data-metal (chrome (default), gold or rose), data-speed (how
// fast the reflections flow; default 1), data-ripple (how much the surface
// wobbles, in em; default 0.05, 0 for none).
var DEFAULTS = { metal: 'chrome', speed: 1, ripple: 0.05 };
var count = 0;
var METALS = {
chrome: ['#f4f8ff', '#a9b8cc', '#ffffff', '#1f2430', '#596273', '#c9d2de', '#ffffff', '#7d8799'],
gold: ['#fff8dc', '#e3b54f', '#fffbe8', '#5a3b0c', '#a87822', '#f2cf7a', '#fff6d5', '#b38631'],
rose: ['#fff1f2', '#e8a3ad', '#ffffff', '#5b2430', '#a6576a', '#f3c2cb', '#fff5f6', '#b97483']
};
var css =
'.lqc-text{display:inline-block;-webkit-background-clip:text;background-clip:text;color:transparent;padding:0 .06em;-webkit-box-decoration-break:clone;box-decoration-break:clone}' +
'.lqc-svg{position:absolute;width:0;height:0;overflow:hidden}';
var style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
function reduced() { return window.matchMedia('(prefers-reduced-motion: reduce)').matches; }
function setup(el) {
var d = el.dataset;
var m = METALS[d.metal] || METALS[DEFAULTS.metal];
var speed = d.speed !== undefined ? +d.speed : DEFAULTS.speed;
var ripple = d.ripple !== undefined ? +d.ripple : DEFAULTS.ripple;
var text = el.textContent.replace(/\s+/g, ' ').trim();
el.setAttribute('aria-label', text);
el.textContent = '';
var span = document.createElement('span');
span.className = 'lqc-text';
span.setAttribute('aria-hidden', 'true');
span.textContent = text;
el.appendChild(span);
// Layers, top first: two soft streaks of light, then the mirrored
// horizon (sky, bright line, dark ground) that gives metal its look.
var streaks =
'linear-gradient(105deg, transparent 38%, rgba(255,255,255,.85) 47%, rgba(255,255,255,0) 56%),' +
'linear-gradient(75deg, transparent 42%, rgba(255,255,255,.5) 50%, transparent 58%),';
function horizon(angle) {
return 'linear-gradient(' + angle.toFixed(1) + 'deg, ' + m[0] + ' 0%, ' + m[1] + ' 24%, ' + m[2] + ' 44%, ' + m[3] + ' 49%, ' + m[4] + ' 58%, ' + m[5] + ' 72%, ' + m[6] + ' 86%, ' + m[7] + ' 100%)';
}
span.style.backgroundSize = '260% 100%, 300% 100%, 100% 170%';
span.style.backgroundRepeat = 'no-repeat, no-repeat, repeat';
var fs = parseFloat(getComputedStyle(el).fontSize);
var turb = null, disp = null, id = null;
if (ripple > 0) {
id = 'lqc-ripple-' + (++count);
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('class', 'lqc-svg');
svg.setAttribute('aria-hidden', 'true');
svg.innerHTML =
'<filter id="' + id + '" x="-5%" y="-10%" width="110%" height="120%">' +
'<feTurbulence type="fractalNoise" numOctaves="2" seed="' + Math.floor(Math.random() * 100) + '" result="n"/>' +
'<feDisplacementMap in="SourceGraphic" in2="n" xChannelSelector="R" yChannelSelector="G"/>' +
'</filter>';
el.appendChild(svg);
turb = svg.querySelector('feTurbulence');
disp = svg.querySelector('feDisplacementMap');
}
var shadow = ' drop-shadow(0 .05em .03em rgba(0,0,0,.28))';
function size() {
fs = parseFloat(getComputedStyle(el).fontSize) || fs;
if (disp) disp.setAttribute('scale', (fs * ripple).toFixed(2));
span.style.filter = (id ? 'url(#' + id + ')' : '') + shadow;
}
size();
new ResizeObserver(size).observe(el);
function frame(t) {
var s = t * 0.001 * speed;
// The horizon rolls up and down, the streaks sweep across at their own pace.
var hy = 50 + Math.sin(s * 0.9) * 30 + Math.sin(s * 2.3) * 8;
// The horizon tilts back and forth, like a reflection on a moving surface.
span.style.backgroundImage = streaks + horizon(180 + Math.sin(s * 0.6) * 9 + Math.sin(s * 1.7) * 3);
var a = ((s * 0.22) % 1.6) - 0.3;
var b = 1.3 - ((s * 0.15 + 0.5) % 1.6);
span.style.backgroundPosition = (a * 100).toFixed(1) + '% 0, ' + (b * 100).toFixed(1) + '% 0, 0 ' + hy.toFixed(1) + '%';
if (turb) {
// A slowly changing noise makes the surface swell and settle.
var fx = (0.9 + 0.25 * Math.sin(s * 0.7)) / (fs * 1.6);
var fy = (1.6 + 0.4 * Math.cos(s * 0.55)) / (fs * 1.6);
turb.setAttribute('baseFrequency', fx.toFixed(5) + ' ' + fy.toFixed(5));
}
}
frame(1800);
if (reduced()) return;
var clock = 1800, prev = 0;
var ticker = anime.createTimer({
loop: true,
duration: 1000,
autoplay: false,
onUpdate: function () {
var now = performance.now();
clock += prev ? Math.min(50, now - prev) : 16;
prev = now;
frame(clock);
}
});
new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) { prev = 0; ticker.play(); }
else ticker.pause();
}).observe(el);
}
function init() {
document.querySelectorAll('[data-liquid-chrome-text]').forEach(function (el) {
if (el._lqc) return;
el._lqc = true;
setup(el);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><h1 data-liquid-chrome-text>Liquid metal</h1>
<!-- molten gold: -->
<h1 data-liquid-chrome-text data-metal="gold">Solid gold</h1>