At some point I realized I was spending more time reading class strings than reading the UI itself.
Not CSS. Not components. Just giant piles of utility classes.
That frustration is what led me to build Juice, an attribute-based UI system for the web.
It’s still in alpha, but the core idea is already clear:
what if UI styling lived in readable attributes instead of class-heavy markup?
The Problem
Utility-first CSS solved a real problem.
It made it easier to move fast without constantly dropping into separate stylesheet files.
But it also created another one.
Markup gets noisy fast.
<section class="flex flex-col items-center justify-center gap-4 p-8 bg-gradient-to-r from-lime-200 to-green-400 rounded-xl shadow-md">
<h1 class="text-4xl font-bold text-green-950">Stewarded Infrastructure</h1>
<p class="max-w-2xl text-center text-green-900">
Governed execution for modern platforms.
</p>
<button class="rounded-lg border border-green-700 px-6 py-3 text-green-900 hover:bg-green-100">
Get Started
</button>
</section>
That works.
But it also asks you to mentally parse layout, spacing, alignment, color, sizing, and interaction from a long utility list every time you read the file.
It gets worse as systems grow.
Not because the approach is wrong, but because the markup starts feeling like a compressed styling language instead of a readable interface.
That was the part I wanted to rethink.
The Idea
Juice uses attributes instead of classes.
Not because attributes are magical.
Because they make the UI read more like intent.
<section stack centered gap="2" padding="2rem" gradient="citrusmint-300" rounded="lg" shadow depth="md">
<h1 font="korolev-rounded-bold" fontSize="xxl" fontColor="green-950">
Stewarded Infrastructure
</h1>
<p width="50%" align="center" fontColor="green-900">
Governed execution for modern platforms.
</p>
<div center>
<button btn="outline" theme="citrusmint-300" scale="lg">Get Started</button>
</div>
</section>
That is the core mental model.
- wrappers control placement
- components control structure
- tokens control visual language
- attributes describe the UI directly
That split feels much cleaner to me.
The Difference
Here’s the comparison more directly.
Tailwind-style
<section class="flex flex-col items-center justify-center gap-4 p-8 bg-gradient-to-r from-lime-200 to-green-400 rounded-xl shadow-md">
<h1 class="text-4xl font-bold text-green-950">Stewarded Infrastructure</h1>
<p class="max-w-2xl text-center text-green-900">
Governed execution for modern platforms.
</p>
<button class="rounded-lg border border-green-700 px-6 py-3 text-green-900 hover:bg-green-100">
Get Started
</button>
</section>
Juice
<section stack centered gap="2" padding="2rem" gradient="citrusmint-300" rounded="lg" shadow depth="md">
<h1 font="korolev-rounded-bold" fontSize="xxl" fontColor="green-950">
Stewarded Infrastructure
</h1>
<p width="50%" align="center" fontColor="green-900">
Governed execution for modern platforms.
</p>
<div center>
<button btn="outline" theme="citrusmint-300" scale="lg">Get Started</button>
</div>
</section>
I’m not claiming one is universally better for every team.
I am saying the second version is easier for me to read, reason about, and scale into a design system.
What Juice Is
Juice is an attribute-based UI system.
It’s part of a broader ecosystem I’m building around KiwiEngine and related CitrusWorx projects.
The focus is pretty simple:
- expressive syntax
- simple composition
- scalable patterns
- readable markup
A few things it already supports:
- layout attributes
- spacing and sizing
- gradients and color tokens
- cards, buttons, forms, and nav patterns
- responsive behavior
- icon support
- motion/animation attributes
It works standalone, but it can also sit next to framework code just fine.
Animations
Juice includes attribute-driven motion too.
Simple cases stay simple.
<h1 motion="blink">Animated headline</h1>
<div gradient="citrusmint-300" motion="flow"></div>
That covers the “give this element some life” cases without adding another layer of complexity.
For more advanced motion work, I’m also building support for optional GSAP integration.
So the rough split is:
- use Juice motion attributes for quick built-in effects
- use GSAP when you need timelines, orchestration, or app-driven animation logic
Why I Built It
I didn’t build Juice because I thought the world needed another CSS framework.
Honestly, that would be a bad reason.
I built it because I wanted a UI system that matched how I think about interfaces.
I wanted markup to stay readable.
I wanted layout and structure to feel explicit.
I wanted styling to scale without turning every component into a wall of utility syntax.
And I wanted it to fit into a bigger system, not just exist as an isolated styling experiment.
Juice is really an attempt at building a better interface language for the kind of systems I’m already making.
Current State
Juice is in alpha.
That matters.
It means:
- the core direction is solid
- the API is still evolving
- parts of the system are stronger than others
- real usage is actively shaping it
I’m not trying to pretend it’s finished.
I’m building it in the open because I’d rather evolve a real system honestly than present something polished and fake-complete.
If this sounds interesting
If you’ve also felt the pain of class-heavy UI code, Juice might be worth a look.
If nothing else, I think the attribute-based approach is an interesting design-space to explore.
If you try it, I’d love feedback.
- what feels clearer
- what feels awkward
- what breaks down at scale
- what you’d want from a system like this
Repo: Juice
It’s early, but it’s real.
This article was originally published by DEV Community and written by Drew Marshall.
Read original article on DEV Community