My favorite component I don't use anymore
PublishedAround April 2024, I quickly fell in love with Marko 6: the most concise framework, with the most concise output.
I rushed to rewrite this website:

I went back to Astro by 2025.
For one, Marko 6 was not even in beta at that time, so I ran into bugs daily. Hot reload was especially horrific, I remember having to restart the dev server a lot.
Perhaps the worst part was Marko Run, which had bugs in the static adapter still not resolved, and was clearly not built for the kinds of things I was using Astro for. I had to write my blog posts in Marko and use a sidecar for metadata.
A search for @marko/run-adapter-static revealed Jack.cab was the second Marko Run static site on GitHub, after https://marko.run.
As part of the port, I created bento.marko, my favorite component.
I used it like this:
bento=[
["a", "b", "b"],
["a", "c", "d"],
["e", "f", "f"]
]
@a -- tall, top left
@b -- wide, top right
@c -- center
@d -- middle right
@e -- bottom left
@f -- bottom right, wide
For comparsion, here’s what I do now:
<Bento layout={[
["a", "b", "b"],
["a", "c", "d"],
["e", "f", "f"]
]}>
<Section id="a">tall, top left</Section>
<Section id="b">wide, top right</Section>
<Section id="c">center</Section>
<Section id="d">middle right</Section>
<Section id="e">bottom left</Section>
<Section id="f">bottom right, wide</Section>
</Bento>
Line-by-line
import type { AttrStyle } from "marko/tags-html"
export type Input = {
// This is technically incorrect, but it's the only way to get anything remotely typesafe
[key: string]: Omit<Marko.AttrTag<Marko.HTML.FieldSet>, "id"> | string[][] | AttrStyle
In Marko, attributes to a component can be provided in three ways:
- Normal attributes
value, the default attribute, which you would use like<bento=foo />- Attribute tags, like snippets in Svelte
Here, I tried to get each attribute tag to be fieldsets where the ID and grid-area get set to their name. However, setting this type affects the other attributes, so I settled for this. I remember spending way, way too much time trying and failing to fix this.
value: string[][]
I thought this would be great for grid-template-areas.
In practice, I mixed and matched ["element","element","element"] with ["element element element"], and I still did after porting it back to Astro. Oops!
style: AttrStyle
}
I added support for a style attribute when I was experimenting with a square section to link to a cancelled YouTube project and needed to manually adjust the sizes of columns. I never used it in a deployed commit of Jack.cab. Bloat!
div [
class="bento"
style=[
{
"grid-template-areas": input.value
.map((row) => row.join(" "))
.map((row) => `'${row}'`)
.join(""),
},
input.style,
]
role="none"
]
I spammed role="none" because my goal was that to screen readers, my website should look like there are no containers and that the legends are headings. Nobody blind is going to visit this website anyway, I remember my linter telling me role="heading" on a <legend> is ignored, but perhaps ChatGPT will love it?
for|[gridArea, block]| of=Object.entries(input).filter(([area]) => area !== "value" && area !== "style")
It’s not a Jack5079 project without a call to Object.entries.
fieldset role="none" id=gridArea ...block style=[{ "grid-area": gridArea }, block.style]
${block}
Notice the order of attributes. I had fucked that up at first, I forgot how long it took me to realize what was wrong.
style --
@media (min-width: calc(800px + 40px)) {
.bento {
display: grid;
grid-gap: 1em;
}
}
On narrow enough displays, like a phone, I disable the grid since it would make no sense on that viewport.
This is probably a bad idea since the order can be different from the bento; in fact right now my blog is at the bottom of the bento but at the top on mobile. I’m too lazy to fix this.
fieldset {
border-color: #ff4539;
border-radius: 1ch;
border-style: solid;
align-content: center;
transition: opacity 0.3s, border-color 0.3s;
}
fieldset:target {
border-color: Highlight;
}
In 2024, I used <system-color>s on my website, out of a belief that it would make my site easier to read and better fit on operating systems. Then, I opened it on GNOME Web and weeped.
div:has(fieldset:target) fieldset:not(:target) {
opacity: 0.5;
}
I played around with doing this with hover. Eventually I realized how extremely distracting it was and removed it.
Feel free to reuse this component! It works on the latest version of Marko unchanged. Check the playground link above the example snippet.
I license bento.marko under CC0, except lines 35 ..= 37 which I am keeping for myself.