Home
Softono

Css Scope Inline

Open source MIT HTML
690
Stars
17
Forks
2
Issues
15
Watchers
5 months
Last Commit

 About Css Scope Inline

🌘 Scope your inline style tags in pure vanilla CSS! Only 16 lines. No build. No dependencies.

Platforms

Web Self-hosted

Languages

HTML

Need Help Installing Css Scope Inline?

We provide expert installation service for this software. Our team will install, configure, and secure Css Scope Inline on your server. plans start at just $30.

Css Scope Inline

View on GitHub

🌘 CSS Scope Inline

cover (Art by shahabalizadeh)

Why does this exist?

  • You want an easy inline vanilla CSS experience without Tailwind CSS.
  • Hate creating unique class names over and over.. to use once.
  • You want to co-locate your styles for ⚑️ Locality of Behavior (LoB)
  • You wish this would work in <style> tags.
  • Want all CSS features: Nesting, animations. Get scoped @keyframes!
  • You wish @media queries were shorter for responsive design.
  • Only 16 lines. No build step. No dependencies.
  • Pairs well with htmx and Surreal
  • Want fewer layers, less complexity. Are aware of the cargo cult. ✈️

✨ Want to also scope your <script> tags? See our companion project Surreal

πŸ‘οΈ How does it look?

<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.

🌘 How does it work?

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 leaves your existing styles untouched. Mix in scoped <style> at your leisure.

🎁 Install

βœ‚οΈ copy + πŸ“‹ paste the snippet into <script> in your <head>

Or, πŸ“₯ download into your project, and add <script src="https://raw.githubusercontent.com/gnat/css-scope-inline/main/script.js"></script> in your <head>

Or, 🌐 CDN: <script src="https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js"></script>

πŸ€” Why consider this over Tailwind CSS?

Use whatever you'd like, but there's a few advantages with this approach over Tailwind, Twind, UnoCSS:

  • No repeat styles on child elements (no more @apply, no [&>thing]).
  • No repeat prefixes for media queries, hover, focus, etc.
  • No visual noise on every <div>. Use <style> on groups of elements.
  • Share real CSS between inline <style> and .css files. Just replace me
  • Regain your "inspect + edit styles + paste" workflow in your browser!
  • No suffering from lost syntax highlighting on properties and units.
  • No high risk of eventually requiring a build step.
  • No chance of deprecations. 16 lines is infinitely maintainable.
  • No suffering from FOUC (a flash of unstyled content).
  • No special binaries, no add-ons, no plugins to install.

⚑ Workflow Tips

  • Use vanilla CSS variables for your design system.
  • Flattened 1 rule = 1 line can be small like Tailwind. See examples
  • Positional selectors may be easier using div[n1] for <div n1> (instead of div:nth-child(1))
  • Child combinator can be very useful for granular control. Example: div>button
  • Use the short @media queries for responsive design.
    • Based on Tailwind breakpoints.
    • 🟒 = Default, no breakpoint required. See the Live Example!
    • Mobile First (flows above Default)
      • 🟒 Default xs sm md lg xl xx 🏁 End
    • Desktop First (flows below Default)
      • 🏁 End xs- sm- md- lg- xl- xx- 🟒 Default

πŸ‘οΈ Showdown: CSS Scope Inline vs Tailwind CSS

Basic example.

<!-- CSS Scope Inline -->
<div>
    <style>
        me { background: red; }
        me div { background: green; }
        me [n1] { background: yellow; }
        me [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>

Variables and child elements example.

At first glance, Tailwind Example 2 looks very promising, but:

  • πŸ”΄ Every child style requires an explicit selector.
    • Tailwinds' shorthand advantages start to disappear.
    • Tailwind becomes larger than vanilla CSS when more child elements are added.
    • This limited example is the best case scenario for Tailwind.
  • πŸ”΄ Not visible on github: no highlighting for properties and units becomes painful.
<!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>

πŸ”Ž Technical FAQ

  • Why do you use querySelectorAll() and not just process the MutationObserver results directly?
    • This was indeed the original design; it will work well up until you begin recieving subtrees (ex: DOM swaps with htmx, ajax, jquery, etc.) which requires walking all subtree elements to ensure we do not miss a <style>. This unfortunately involves re-scanning thousands of repeated elements. This is why querySelectorAll() ends up the performance (and simplicity) winner.

🌘 Change Log

1.1.0

  • Started change log.
  • Fix for domain names ending in .me when used url() properties.