Skip to content

Instantly share code, notes, and snippets.

@grebenyuksv-preply
Last active November 6, 2020 16:19
Show Gist options
  • Save grebenyuksv-preply/fb7e6b488b72fabeca347cedfd1d983d to your computer and use it in GitHub Desktop.
Save grebenyuksv-preply/fb7e6b488b72fabeca347cedfd1d983d to your computer and use it in GitHub Desktop.
CLS Debug Info
import { useCLS } from './useCLS';
import styles from './styles.less';
// the CLS indicator on the bottom right of the screen
export const CLSDebugInfo = () => {
const cls = useCLS();
return <div className={styles.CLSDebugInfo}>{cls && cls.value.toFixed(5)}</div>;
};
.CLSDebugInfo {
visibility: hidden;
position: fixed;
bottom: 0;
right: 0;
z-index: 99;
background: white;
&:before {
content: 'CLS: ';
}
// the important part
// only show the indicator inside .hotjar-css-hover, which is inside the HotJar player
:global(.hotjar-css-hover) & {
visibility: visible;
}
}
.fade-away() {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
}
// the important part
// only show the highlights inside .hotjar-css-hover, which is inside the HotJar player
.CLSElement(@animation) {
:global(.hotjar-css-hover) & {
position: relative;
&:before {
content: '';
position: absolute;
left: 0%;
top: 0;
width: 100%;
height: 100%;
opacity: 0;
box-shadow: 0px 0px 0px 10px rgba(217, 156, 255, 1);
animation: @animation;
}
}
}
.CLSElement1 {
@keyframes fade-away-1 {
.fade-away();
}
.CLSElement(fade-away-1 1s);
}
.CLSElement2 {
@keyframes fade-away-2 {
.fade-away();
}
.CLSElement(fade-away-2 1s);
}
import { useState, useEffect } from 'react';
import { getCLS, ReportHandler, Metric } from 'web-vitals';
import styles from './styles.less';
// subscribe on CLS updates to the rest of the app
const getCLSCallbacks = new Set<ReportHandler>();
let entryIdx = 0;
getCLS((metric: Metric) => {
getCLSCallbacks.forEach(cb => cb.call(null, metric));
for (; entryIdx < metric.entries.length; entryIdx += 1) {
const entry = metric.entries[entryIdx] as LayoutShift;
if (entry.sources) {
for (const { node } of entry.sources) {
if (node instanceof HTMLElement) {
// highlight the node
// https://css-tricks.com/restart-css-animation/
node.classList.add(styles.CLSElement1);
// classList.toggle isn't cross-browser yet
if (node.classList.contains(styles.CLSElement2)) {
node.classList.remove(styles.CLSElement2);
} else {
node.classList.add(styles.CLSElement2);
}
}
}
}
}
}, true);
// the hook which exposes the current CLS value to the indicator
export const useCLS = () => {
const [lastMetric, setLastMetric] = useState<Metric>();
// web-vitals seems to be modifying the same object and passing it into callbacks!
const setLastMetricCopy = (metric: Metric) => {
setLastMetric({ ...metric });
};
useEffect(() => {
getCLSCallbacks.add(setLastMetricCopy);
return () => {
getCLSCallbacks.delete(setLastMetricCopy);
};
}, []);
return lastMetric;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment