In the last Road To KiwiEngine post, I talked about Juice getting close to version 1.0.
Juice has reached a point where I understand its responsibility pretty clearly.
It takes design intent, project configuration, and attribute-based styling rules and turns those things into the visual language of an application.
But interfaces aren't static.
Things change.
Users click buttons.
Data updates.
Components respond.
State changes.
Parts of the interface need to update without rebuilding everything around them.
That's where another KiwiEngine library has been quietly becoming one of the more complete pieces of the ecosystem.
Sig.
Juice Makes It Look Right. Sig Makes It Alive.
This is probably the simplest way I can describe the relationship.
Juice is concerned with presentation.
Sig is concerned with reactivity.
They're related because they both participate in the user interface, but I don't want them becoming the same system.
That's an important architectural boundary.
Juice shouldn't need to understand application state just because an element has styling.
Sig shouldn't need to understand a project's design system just because something on the screen changed.
They can work together without owning each other's responsibilities.
That's becoming a recurring theme throughout KiwiEngine.
Sig Is Signal-Based
At the center of Sig is the idea of signals.
A signal represents a value that can change.
Conceptually, something like:
const count = signal(0);
Now the interesting part isn't simply storing 0.
It's establishing that count is something the interface can react to.
If that value changes, the parts of the UI depending on it can respond.
That gives me a much cleaner mental model for reactive interfaces:
Something changed. Update what depends on it.
I like that.
It's small.
It's understandable.
And it doesn't require the entire application to become one giant state-management problem.
I Didn't Want Reactivity Everywhere
This is one of the architectural decisions I've become more confident about.
When you're building a framework, it's tempting to make every system aware of every other system.
The UI needs state.
The state affects rendering.
Rendering affects components.
Components have styles.
Styles have configuration.
Suddenly everything knows everything.
That's exactly the kind of coupling I'm trying to avoid.
Sig owns reactivity.
Juice owns styling.
Nectarine can own configuration.
Seltzer can own HTTP concerns.
WebEngine can coordinate the larger application lifecycle.
The pieces can communicate.
But communication doesn't require collapsing them into one enormous abstraction.
Sig Has Its Own JSX Runtime
This is where Sig becomes more than a tiny signals utility.
It also has its own JSX runtime.
That matters because JSX gives me a really expressive way to describe interfaces while Sig controls how those interfaces actually become reactive.
Instead of depending on another UI framework to interpret the application, KiwiEngine can have a rendering model that belongs to its own architecture.
That's a significant step.
Because now Sig isn't simply helping another framework manage state.
It's becoming the reactive UI runtime itself.
JSX Isn't The Architecture
At the same time, I don't want JSX to become the point of Sig.
Syntax is useful.
But syntax isn't architecture.
The important part is the relationship underneath it.
A component expresses an interface.
Signals represent changing values.
The renderer understands those relationships.
When state changes, the system updates what needs to change.
JSX gives developers a convenient way to express that.
But the real value is the reactive model underneath it.
That's the engine.
Reactivity Should Feel Boring
I mean that as a compliment.
When I click something and a value changes, I don't want to think about the rendering engine every time.
I don't want every feature to require another conversation about state synchronization.
I don't want to manually coordinate every DOM update.
The system should establish the relationship.
Then it should do its job.
That's where libraries start becoming infrastructure.
When they're new, you think about them constantly.
When they're mature, you think about the thing you're building.
Sig getting closer to version 1 means I want more of that second experience.
The Renderer Is Where The Abstraction Gets Tested
A reactive API can look incredibly elegant in isolation.
The real test happens when you start rendering actual applications.
Components nest.
State changes.
Lists change.
Elements appear and disappear.
Events fire.
Values depend on other values.
Cleanup matters.
Lifecycle matters.
The happy-path demo stops being enough.
That's where the implementation has to prove that the abstraction actually works.
And that's part of why building real KiwiEngine applications matters so much.
I'm not trying to design Sig from hypothetical examples forever.
The applications need to pressure-test it.
If something becomes awkward repeatedly, that's information.
If an abstraction only works in a demo, that's information too.
The application is part of the design process.
Sig Shouldn't Become Everything Either
The lesson I learned with Juice applies here too.
A library becomes more mature when I understand what doesn't belong in it.
Sig doesn't need to become a styling framework.
It doesn't need to become an HTTP client.
It doesn't need to become a configuration system.
It doesn't need to absorb every capability an application might eventually require.
Its responsibility is already substantial:
Make reactive interfaces work well.
That's enough.
Other KiwiEngine systems can handle the rest.
This Is Why Separate Libraries Matter
Sometimes it would be easier to put everything into one giant package.
Install KiwiEngine.
Get everything.
UI.
CSS.
HTTP.
Configuration.
Infrastructure.
Rendering.
All living inside the same enormous abstraction.
But that's not really the engine I want.
I want systems with understandable responsibilities.
Juice can mature independently.
Sig can mature independently.
Seltzer can mature independently.
Nectarine can mature independently.
Then WebEngine can compose those capabilities into something larger.
That means improvements to one system don't necessarily require redesigning everything else.
It also means developers can understand KiwiEngine one piece at a time.
Juice And Sig Show What KiwiEngine Is Becoming
This is where the architecture is starting to feel real to me.
Juice can answer:
How should this interface express its design?
Sig can answer:
How should this interface respond when something changes?
Those are different questions.
They deserve different systems.
But when they're composed, something interesting happens.
Now I have an interface that can express design intent and react to application state.
Neither library needs to become the other.
That's the kind of composition I've been trying to reach.
Getting Close To Version 1 Changes The Questions
Just like Juice, getting Sig closer to version 1 isn't making me ask:
What else can I cram into this thing?
I'm asking:
Is the responsibility clear?
Is the reactive model predictable?
Does the renderer behave consistently?
Are the lifecycle boundaries understandable?
Can I build real interfaces without constantly fighting the abstraction?
Those questions matter more to me than the size of the feature list.
Version 1 doesn't mean there will never be another feature.
It means the foundation should be dependable enough to build on.
Another Piece Of The Engine Is Settling Into Place
This is why I've been saying KiwiEngine is finally becoming an engine.
It's not because one giant package suddenly became capable of doing everything.
It's almost the opposite.
The individual systems are figuring out what they're responsible for.
Juice is becoming the design engine.
Sig is becoming the reactive UI engine.
And as those responsibilities stabilize, WebEngine gains dependable pieces it can compose instead of experimental ideas it has to compensate for.
That's progress I can actually build on.
Because eventually I don't want to spend all my time thinking about signals.
I want to think about the music application using them.
The storefront using them.
The artist platform using them.
The electronics dashboard using them.
The things I'm actually trying to create.
A good reactive engine shouldn't become the application.
It should make the application feel alive.
Top comments (0)