DEV Community

Devanshu Biswas
Devanshu Biswas

Posted on

A Flexbox playground in one state object: container + item props live CSS, and the main/cross axis trick

Flexbox is the layout tool everyone reaches for and almost nobody feels fully in control of. So I built a playground: toggle every container and item property, watch the boxes rearrange in real time, and copy the CSS that produced the layout. The surprise while building it was how little code a tool like this needs — the browser does the actual layout, so the whole app is a state object applied as inline styles plus a serialiser that prints it back as CSS. Here is the shape of it, and the one concept that makes Flexbox click.

The whole app is one state object

You store the source of truth once: a container object for the six container properties, an items array (five properties each), and which item is selected. The preview and the CSS output are both just views of this state.

let container = {
  flexDirection: "row",  flexWrap: "nowrap",
  justifyContent: "flex-start", alignItems: "stretch",
  alignContent: "stretch", gap: 10
};
const box = () => ({ grow:0, shrink:1, basis:"auto", alignSelf:"auto", order:0 });
let items = [ box(), box(), box(), box() ];
let selected = 0;
Enter fullscreen mode Exit fullscreen mode

The preview is the CSS

There is no rendering engine to write. You copy each container property straight onto the flex parent's inline style, rebuild the children from the array, and the browser reflows. That is the entire "live preview".

function applyContainer(){
  const f = document.getElementById("flexbox");
  f.style.display        = "flex";
  f.style.flexDirection  = container.flexDirection;
  f.style.justifyContent = container.justifyContent;
  f.style.alignItems     = container.alignItems;
  f.style.gap            = container.gap + "px";
}
Enter fullscreen mode Exit fullscreen mode

Even the toggle chips are data-driven — every keyword property is a fixed list of values, so one tiny function generates every control in the app: one chip per allowed value, each writing that value into state and re-rendering. Presets need no special code either; a "preset" is just a canned state object you drop in and re-render, so centering, a nav bar and a card row are three lines of data, not three features.

The one idea to hold: main axis vs cross axis

This is the concept that trips everyone, and the playground is built to hammer it home. justify-content and align-items are not "horizontal" and "vertical". justify-content always works on the main axis; align-items on the cross axis. And flex-direction is what decides which is which — so the same property means "left/right" in a row but "up/down" in a column.

// flex-direction: row     -> main = horizontal, cross = vertical
// flex-direction: column  -> main = vertical,   cross = horizontal
//   justify-content  ==> MAIN axis
//   align-items      ==> CROSS axis
// Flip the direction and the two swap meaning automatically.
Enter fullscreen mode Exit fullscreen mode

Learn the axes, not the directions, and Flexbox stops being a guessing game. The playground prints a live axis guide under the preview that flips its arrows the moment you switch to a column, so you see justify-content change from moving boxes left/right to moving them up/down.

The flex shorthand, and the gotchas

Three item properties decide sizing along the main axis: flex-basis is the starting size, then any leftover space is shared in proportion to flex-grow and any overflow removed in proportion to flex-shrink. The shorthand flex: 1 means 1 1 0 — which is exactly why flex:1 items come out equal width regardless of content. A few others that surprise people, all demonstrable in the tool:

  • align-content does nothing on a single line. It distributes lines, so you need flex-wrap:wrap and enough items to actually wrap. This is the number-one "why isn't this working?".
  • order is visual only. It reorders how boxes paint, but the DOM, tab order and screen-reader order stay in source order — great for responsive tweaks, dangerous if you use it to fix real reading order.
  • margin:auto is a Flexbox superpower — on an item it eats all free space on that side, so margin-left:auto pushes it and everything after to the far end, the cleanest nav bar there is.

The deliverable is a serialiser

The export is the whole point: print the container object as a .container rule and the selected item as an .item-N rule. It is pure string-building — the same values you applied inline, formatted as CSS.

function toCss(){
  const c = container, it = items[selected];
  return `.container {
  display: flex;
  flex-direction: ${c.flexDirection};
  justify-content: ${c.justifyContent};
  align-items: ${c.alignItems};
  gap: ${c.gap}px;
}
.item-${selected + 1} {
  flex: ${it.grow} ${it.shrink} ${it.basis};
  align-self: ${it.alignSelf};
  order: ${it.order};
}`;
}
Enter fullscreen mode Exit fullscreen mode

The lesson underneath: Flexbox is not a bag of magic keywords. It is two axes set by flex-direction, with justify-content on the main axis, align-* on the cross, and grow/shrink/basis deciding how each item sizes. Learn the axes and the rest is just naming.

Flip every property and copy the CSS:
https://dev48v.infy.uk/solve/day40-flexbox-playground.html

Top comments (0)