<div class="spinner"><div>
.spinner {
color: black;
font-size: 5rem;
}
.spinner::before {
display: inline-block;
text-align: center;
font-family: monospace;
width: 5rem;
content: "";
animation: spinner .8s infinite steps(4);
}
@keyframes spinner {
0%, 100% { content: "\2014"; }
25% { content: "\\"; }
50% { content: "|"; }
75% { content: "/"; }
}
<div class="spinner"><div>
.spinner {
color: black;
font-size: 5rem;
font-family: monospace;
}
const block = document.querySelector('.spinner');
const text = [ '\u2014', '\\', '|', '/' ];
let index = -1;
setInterval(() => {
index = (index + 1) % text.length;
block.textContent = text[index];
}, 200);
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<g font-size="60" font-family="monospace" fill="black" visibility="hidden">
<text x="30" y="60">
—
<animate attributeName="visibility" from="hidden" to="visible" begin="0.2s; hide4.begin" dur="0.2s" id="show1" />
<animate attributeName="visibility" from="visible" to="hidden" begin="show1.begin + 0.2s" dur="0.2s" id="hide1" />
</text>
<text x="30" y="60">
\
<animate attributeName="visibility" from="hidden" to="visible" begin="hide1.begin" dur="0.2s" id="show2" />
<animate attributeName="visibility" from="visible" to="hidden" begin="hide1.begin + 0.2s" dur="0.2s" id="hide2" />
</text>
<text x="30" y="60">
|
<animate attributeName="visibility" from="hidden" to="visible" begin="hide2.begin" dur="0.2s" id="show3" />
<animate attributeName="visibility" from="visible" to="hidden" begin="hide2.begin + 0.2s" dur="0.2s" id="hide3" />
</text>
<text x="30" y="60">
/
<animate attributeName="visibility" from="hidden" to="visible" begin="hide3.begin" dur="0.2s" id="show4" />
<animate attributeName="visibility" from="visible" to="hidden" begin="hide3.begin + 0.2s" dur="0.2s" id="hide4" />
</text>
</g>
</svg>