Everyone wants a "Bento Grid" portfolio these days. It’s the design style popularized by Apple and Linear: a grid of boxes, high information density, and sleek rounded corners.
But building one that actually works on mobile is a headache.
Usually, developers reach for heavy JavaScript libraries like Masonry.js to handle the layout. But I wanted a portfolio that scores 100/100 on Lighthouse, so I decided to build it using pure CSS and Tailwind.
Here is how I solved the responsive puzzle.
The Problem: The "Swiss Cheese" Effect When you use a standard CSS Grid with items of different sizes (some spanning 2 rows, some spanning 2 columns), you often end up with ugly gaps in your layout—especially when resizing the window.
The Solution: grid-auto-flow: dense The magic property…
Everyone wants a "Bento Grid" portfolio these days. It’s the design style popularized by Apple and Linear: a grid of boxes, high information density, and sleek rounded corners.
But building one that actually works on mobile is a headache.
Usually, developers reach for heavy JavaScript libraries like Masonry.js to handle the layout. But I wanted a portfolio that scores 100/100 on Lighthouse, so I decided to build it using pure CSS and Tailwind.
Here is how I solved the responsive puzzle.
The Problem: The "Swiss Cheese" Effect When you use a standard CSS Grid with items of different sizes (some spanning 2 rows, some spanning 2 columns), you often end up with ugly gaps in your layout—especially when resizing the window.
The Solution: grid-auto-flow: dense The magic property that solves 90% of Bento layouts is dense. This tells the browser to fill in the holes in the grid with smaller items if they fit, even if it changes the visual order slightly.
Here is the Tailwind implementation I used for my portfolio:
// The Main Container
{/* The items go here */}
Key Breakdown:
grid-cols-1: On mobile, it’s just a vertical feed (one column). No complex reordering needed.
md:grid-cols-3: On tablets and desktops, it expands to a 3-column grid.
auto-rows-[250px]: This forces every row to have a fixed height, making the calculation of "tall" items (spanning 2 rows) predictable.
Handling the "Span" Items To make it look like a Bento box, some items need to be bigger. I created a reusable component that accepts colSpan and rowSpan props.
// Inside your Card Component
{children}
Why Avoid Flexbox for this? I tried Flexbox first. The problem with Flexbox is that it doesn’t align items on both the X and Y axis simultaneously. You end up with columns of uneven height. CSS Grid is the only way to get that perfect "aligned" Bento look without using JavaScript to calculate positions.
Performance Result By avoiding client-side libraries for layout:
0 Layout Shift (CLS) on load.
Javascript bundle size remains minimal.
SEO: Google reads the content perfectly because it’s just HTML divs.
Conclusion You don’t need complex libraries to build modern layouts. CSS Grid has matured enough to handle the "Bento" style natively.
đź‘‹ By the way...
I spent the weekend polishing this system into a reusable Next.js template (Dark mode, animations, and responsive components included).
If you want to save time and just grab the code, you can check it out here: https://veloxweb.gumroad.com/l/launch-ui
Otherwise, happy coding!