I don't think you can do the "less than 8th" with just CSS, but you can convert the NodeList from querySelectorAll into an array and grab just the first seven via .slice(0, 7), then filter out the ones with the class you don't want (bad in your sample data I think):
const list = [...document.querySelectorAll("ul.unstyled li")]
.slice(0, 7)
.filter(li => !li.classList.contains("bad"));
Live Example:
const list = [...document.querySelectorAll("ul.unstyled li")].slice(0, 7).filter(li => !li.classList.contains("bad"));
for (const li of list) {
console.log(li.textContent);
}
<ul class="unstyled">
<li class="good">0</li>
<li class="bad">1</li>
<li class="bad">2</li>
<li class="bad">3</li>
<li class="bad">4</li>
<li class="good">5</li>
<li class="bad">6</li>
<li class="bad">7</li>
<li class="bad">8</li>
<li class="bad">9</li>
<li class="good">10</li>
<li class="bad">11</li>
<li class="bad">12</li>
</ul>
The above assumes that NodeList is iterable on your browser, as it is on most. If it isn't, but your environment supports ES2015+, you can easily polyfill it using my answer here. Alternatively, use Array.prototype.slice directly as in this ES5 example:
var list = Array.prototype.slice.call(
document.querySelectorAll("ul.unstyled li"),
0, 7
).filter(function(li) { return !li.classList.contains("bad"); });