📦 Cardboard devlog, what I've done in the last update
Keff

Keff @nombrekeff

Location:
Remote
Joined:
Jun 29, 2019

📦 Cardboard devlog, what I've done in the last update

Publish Date: Mar 17
8 17

Hey there! Hope you're having a great week!

I'm building Cardboard, a straightforward, yet powerful reactive web framework. You can look at the repo, or the other articles in the series if you don't know what Cardboard is about!

This post is a devlog/changelog about what's happened with Cardboard in the last update. What's been added, changed, and improved.

Changes

State

I've re-written the state to use Consumables. I decided to do this as the previous implementation of the state was unstable, hacky and not very intuitive to be honest.

Consumables are custom objects that hold a value, can be listened for changes, and can dispatch new values.

Another inconvenience was that you could only create object/array states, not primitive values. With the new implementation, you can have both.

By using Consumables the new state is simplified. It's also a lot more stable and intuitive.

Before:

let st = state({ count: 0 });
st.count++;
Enter fullscreen mode Exit fullscreen mode

Now:

let count = state(0);
count.value++;
Enter fullscreen mode Exit fullscreen mode

Now we must modify the value of the consumable.

There are many more changes, but not worth mentioning as they're not that interesting :P


Additions

States can now be children

To be able to create reactive text before, you had to use the text function or tag.text method. While that did the job, it was a pain if you just wanted to add the Consumable value without interpolating. Let's look at an example to better understand what I mean:

Before:

const hours = state(0);
const minutes = state(0);

div(text('$hours', { hours }), ':', text('$minutes', { minutes }));
Enter fullscreen mode Exit fullscreen mode

Now:

const hours = state(0);
const minutes = state(0);

div(hours, ':', minutes);
Enter fullscreen mode Exit fullscreen mode

Consumable Intersecting

When using Consumables, you might want to check if it has a value, if it's empty, if it's a certain value, etc...

For this, Consumables can be intercepted, which means that you can create a new Consumable that updates its value based on another Consumable.

Look at this example:

const temperature = createConsumable(32);
const isTooHot = greaterThanOr(temperature, 30);

div().showIf(isTooHot);
Enter fullscreen mode Exit fullscreen mode

As you can see this can be very powerful. isTooHot will be true if the temperature is higher or equal to 30, and false if less.

The div will be shown or hidden based on the value of isTooHot.

Let's look at another example:

const TodoItem = (todo: IConsumable<TodoItem>) => {
  const isComplete = grab(todo, 'complete', false);
  const todoText = grab(todo, 'text', 'Empty Todo');

  return div(
    input().setAttrs({ type: 'checkbox' })
      .changed((self) => {
        todo.value.complete = self.checked;
      }),
    h4(todoText)
      .stylesIf(isComplete, { textDecoration: 'line-through' }),
  );
}
Enter fullscreen mode Exit fullscreen mode

grab will set its value to a specified value from the original consumable. Whenever todo changes, the values of isComplete and todoText will also update.

If the input checkbox is changed, it will set the complete property. When this happens the h4 will react and set the styles accordingly.

I'm interested in knowing what you think about this. Do you think the approach I've taken is good? Or would you suggest going another route?

each

The biggest change has been the addition of the each function. This has taken most of my time and has made me rethink how some aspects of the project work. Most other changes have been made to make each work properly.

each renders lists that update whenever the state changes. The interesting and hard thing is that it's smart and will only update the things that need to be updated. If a value is added, only that value is added to the dom. If a value changes places, only that operation is done.

This was a bit of a challenge, not only creating the actions to be performed but also handling memory.

This is how it works:

const colors = state(['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']);
const selectedColor = state('red');

div(
    each(colors, (color) => 
        button(color)
          .addStyle('color', color)
          .stylesIf(equalTo(selectedColor, color), { fontWeight: 'bold' });
    )
);
Enter fullscreen mode Exit fullscreen mode
  • If a colour is added, removed, or changed places, each will react and update the list accordingly.

If you want me to write a more detailed and technical article about how each works, let me know in the comments and I will get to it. It's pretty interesting!


Improvements

After working on all of the changes mentioned above, and adding more tests a lot of bugs have been squashed, naming has improved, and the overall stability of the project has increased.

Additionally, I've spent a lot of time improving the wiki and docs, as well as the README. I think having a decent Wiki and documentation is key from the get-go.

Summary

It's been a productive couple of weeks. I don't think I've ever spent this amount of time working on a single project (outside of work). But I like how Cardboard is turning out! I think that with a bit more work it can be very powerful and competent.

I'm aiming to have the first stable version finished before the end of the year!

If you find the project interesting please consider helping out!

GitHub logo nombrekeff / cardboard-js

A very simple, yet powerful reactive framework, to create web applications. All of this with, no HTML, CSS, JSX, and no required build.

📦 Carboard.js

Tests Main Project Status

Welcome to Cardboard. An extremely light (around 18kb), performant, and very simple reactive framework. It offers almost everything you'd expect from a complete framework. Like managing state, components, logic, and the rest. But with a twist, you don't need to write any HTML, CSS, or JSX if you don't want to. See what it can do.

It's similar in philosophy to VanJS, if that rings a bell, but with many more features, and a more extensive API.

!NOTE!: Cardboard is in development, use it with caution.
You can check the v1.0.0 milestone for a view on the development state - help is much appreciated!

const Counter = () => {
  const count = state(0);
  return button()
    .text(`Clicked $count times`, { count })
    .addStyle('color', 'gray')
    .
Enter fullscreen mode Exit fullscreen mode

Comments 17 total

  • WORMSS
    WORMSSMar 18, 2025

    Congrats you invented Vue/Solid.js

    • leob
      leobMar 18, 2025

      Might seem pointless, but I'm convinced you can learn a LOT doing this (even if you end up being the only user of the framework) ...

      • WORMSS
        WORMSSMar 18, 2025

        Oh, I totally agree with that one.. It's very similar to something I made for a static SSR generator... (just not the state tracking/changing) with everything being chainable. (Which I actually like)..
        Not a massive fan of the each() part, but I can completely understand WHY it's that way.. But, to be fair, I hate the way React does the same thing with JSX.. I am obviously a very hateful person.. LOL..
        As a learning endeavour though, this will be invaluable.

        • Keff
          KeffMar 18, 2025

          I'm also quite hateful if I'm honest, that's why I built it.

          each is simple, something I value a lot. Cardboard is simple. Might seem weird at first but it's very simple in essence.

          It was more than a learning endeavour, it was an experiment to see if it could be done in another way.

          I explain all of this and why I did what I did in some other post if you're interested.

          • Keff
            KeffMar 18, 2025

            I always thought that a performant vanilla js Framework could be made without all the fuf that most frameworks have. Jsx always seemed excessive to me. And html extremely redundant. So that's why I tried to build this.

            • leob
              leobMar 18, 2025

              I don't know if JSX is "excessive", but what I do know is that React isn't performant, at all - unless as an application developer you make it so ... :D

              • Keff
                KeffMar 18, 2025

                Yup, most frameworks aren't performant without the developer's help. They add to much overhead and features not used in many scenarios. It can be fixed in some ways using tree shaking and so on, but even then.

                Well not excessive per-se, but I've always thought there could be a better way. For me it feels wrong I'm some weird way.

                • Joshua Amaju
                  Joshua AmajuMar 19, 2025

                  If you dislike HTML then you're in the wrong industry my friend. Maybe you should do mobile dev or something

                  • Keff
                    KeffMar 19, 2025

                    I have, I'd consider myself a mobile developer now. But still enjoy web even though I don't like html.

    • Keff
      KeffMar 18, 2025

      Not really, main point of cardboard was to remove the need to write html tags. That was the main point. I hate writing html. I don't know what solid.js is, but Vue requires you to write html tags or jsx. Also cardboard is surprisingly fast, and lightweight at around 20kb. Of course other frameworks offer much more stuff, but most things can already be done with cardboard.

    • Joshua Amaju
      Joshua AmajuMar 19, 2025

      What's the point of your comment? To discourage them or what exactly?

      • Keff
        KeffMar 19, 2025

        Don't worry, I've been in the industry and here for a while. I'm used to it, and don't really care. I find it somewhat funny 🤣

      • WORMSS
        WORMSSMar 19, 2025

        Do you feel inventing something as good as vue and solid JS as an insult?

  • Rishi U
    Rishi UMar 19, 2025

    How would you write a figure with a fig caption, a field set with a label, all wrapped within a form and each having their own styles. Wont it all get well a bit spaghetti i, and neglect separation of concerns? -- Overall its pretty interesting and could really work well for small embedded projects.

    • Keff
      KeffMar 20, 2025

      In theory it's possible to do everything you could with vanilla HTML/JS/CSS.

      Well, yes. It could be a bit spaghetti, the same way it could in other frameworks I guess. For now it's up to the developer to organize and structure the code. Maybe the issue is calling cardboard a framework, it's probably more of a library than a framework at the moment. It offers the tools to do things, but it does not force you to do thins in some specific way.

      My idea was to use it in small projects that need reactivity and basic SPA functionality with a really small footprint. All of this in a very light package that is also very performant by design. Of course it's limited for big apps, but that's not it's primary use-case.

  • izaias
    izaiasMar 19, 2025

    Parabéns

  • Mineko GmbH
    Mineko GmbH Mar 21, 2025

    Good job!

Add comment