Here is a creative solution using mostly CSS, with minimal JavaScript required for splitting content on hard line breaks. The CSS variable can be customized or adapted for legacy browser support.
const target = document.querySelector('#target')
const lines = target.textContent.split('\n').map(str => {
const div = document.createElement('div')
div.classList.add('line')
div.textContent = str
return div
})
target.textContent = ''
lines.forEach(line => target.appendChild(line))
:root {
--line-height: 20px;
}
#target {
white-space: pre-wrap
}
.line {
display: flex;
padding-right: var(--line-height);
line-height: var(--line-height);
position: relative;
}
.line:nth-child(odd) {
background: #f8f8f8;
}
.line::after {
content: '';
position: absolute;
right: 0;
height: calc(100% - var(--line-height));
background-repeat: repeat-y;
background-size: var(--line-height) var(--line-height);
width: var(--line-height);
font-weight: bold;
color: #269;
opacity: .3;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Cg%3E%3Crect fill='none' id='canvas_background' height='42' width='42' y='-1' x='-1'/%3E%3C/g%3E%3Cg%3E%3Cpolygon id='svg_1' points='16.397236347198486,20.219151496887207 9.397236347198486,25.219...
}
<pre id="target">Lorem ipsum dolor sit amet.
Consectetur adipisicing elit.
Debitis vero dolorem officia omnis nulla molestiae perferendis sequi illo.
Molestiae error inventore perspiciatis maxime?
At, illum!
Cupiditate consequuntur harum minima perferendis recusandae alias, rem, vitae...</pre>