🌘 Scope your inline style tags in pure vanilla CSS! Only 16 lines. No build. No dependencies.
(Art by shahabalizadeh)
this
would work in <style>
tags.@keyframes
!@media
queries were shorter for responsive design.✨ Want to also scope your <script>
tags? See our companion project Surreal
<div>
<style>
me { background: red; } /* ✨ this & self also work! */
me button { background: blue; } /* style child elements inline! */
</style>
<button>I'm blue</button>
</div>
See the Live Example! Then view source.
This uses MutationObserver
to monitor the DOM, and the moment a <style>
tag is seen, it scopes the styles to whatever the parent element is. No flashing or popping.
This method also leaves your existing styles untouched, allowing you to mix and match at your liesure.
✂️ copy + 📋 paste the snippet into <script>
in your <head>
Or, 📥 download into your project, and add <script src="script.js"></script>
in your <head>
Or, 🌐 the CDN: <script src="https://cdn.jsdelivr.net/gh/gnat/css-scope-inline/script.js"></script>
Use whatever you'd like, but there's a few advantages with this approach over Tailwind, Twind, UnoCSS:
[&>thing]
per style). It's just CSS!<div>
. Use a local <style>
per group..css
files. Just replace me
@media
queries for responsive design.
sm
md
lg
xl
xx
🏁xs-
sm-
md-
lg-
xl-
🟢 None
xx
not 2xl
to not break CSS highlighters.div[n1]
for <div n1>
instead of div:nth-child(1)
Tailwind verbosity goes up with more child elements.
<!-- CSS Scope Inline -->
<div>
<style>
me { background: red; }
me div { background: green; }
me div[n1] { background: yellow; }
me div[n2] { background: blue; }
</style>
red
<div>green</div>
<div>green</div>
<div>green</div>
<div n1>yellow</div>
<div n2>blue</div>
<div>green</div>
<div>green</div>
</div>
<!-- Tailwind -->
<div class="bg-[red]">
red
<div class="bg-[green]">green</div>
<div class="bg-[green]">green</div>
<div class="bg-[green]">green</div>
<div class="bg-[yellow]">yellow</div>
<div class="bg-[blue]">blue</div>
<div class="bg-[green]">green</div>
<div class="bg-[green]">green</div>
</div>
<!doctype html>
<html>
<head>
<style>
:root {
--color-1: hsl(0 0% 88%);
--color-1-active: hsl(214 20% 70%);
}
</style>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js"></script>
</head>
<body>
<!-- CSS Scope Inline -->
<div>
<style>
me { margin:8px 6px; }
me div a { display:block; padding:8px 12px; margin:10px 0; background:var(--color-1); border-radius:10px; text-align:center; }
me div a:hover { background:var(--color-1-active); color:white; }
</style>
<div><a href="#">Home</a></div>
<div><a href="#">Team</a></div>
<div><a href="#">Profile</a></div>
<div><a href="#">Settings</a></div>
<div><a href="#">Log Out</a></div>
</div>
<!-- Tailwind Example 1 -->
<div class="mx-2 my-4">
<div><a href="#" class="block py-2 px-3 my-2 bg-[--color-1] rounded-lg text-center hover:bg-[--color-1-active] hover:text-white">Home</a></div>
<div><a href="#" class="block py-2 px-3 my-2 bg-[--color-1] rounded-lg text-center hover:bg-[--color-1-active] hover:text-white">Team</a></div>
<div><a href="#" class="block py-2 px-3 my-2 bg-[--color-1] rounded-lg text-center hover:bg-[--color-1-active] hover:text-white">Profile</a></div>
<div><a href="#" class="block py-2 px-3 my-2 bg-[--color-1] rounded-lg text-center hover:bg-[--color-1-active] hover:text-white">Settings</a></div>
<div><a href="#" class="block py-2 px-3 my-2 bg-[--color-1] rounded-lg text-center hover:bg-[--color-1-active] hover:text-white">Log Out</a></div>
</div>
<!-- Tailwind Example 2 -->
<div class="mx-2 my-4
[&_div_a]:block [&_div_a]:py-2 [&_div_a]:px-3 [&_div_a]:my-2 [&_div_a]:bg-[--color-1] [&_div_a]:rounded-lg [&_div_a]:text-center
[&_div_a:hover]:bg-[--color-1-active] [&_div_a:hover]:text-white">
<div><a href="#">Home</a></div>
<div><a href="#">Team</a></div>
<div><a href="#">Profile</a></div>
<div><a href="#">Settings</a></div>
<div><a href="#">Log Out</a></div>
</div>
</body>
</html>
QuerySelectorAll()
and not just process the MutationObserver
results directly?
MutationObserver
results will work well until you begin recieving subtrees (ex: DOM swap, htmx, ajax, jquery) which requires you to walk all subtree child elements to not miss a <style>
. This can involve re-scanning thousands of repeated elements, and QuerySelectorAll()
ends up the simplicty and performance winner.