Add clean, consistent inner borders to a Webflow grid without a single combo class or workaround — just a tiny gap and a background color. Webflow has no built-in “inner border” control, but you don’t need one: shrink the grid’s gap to a hairline, color the grid behind it, and the gaps between your cells read as crisp divider lines. It works for images, text, and CMS collections alike.
The catch is that those lines are really the grid’s background showing through, so the technique lives or dies on making every cell fully opaque and every leftover space filled. That’s where most of the real work is — handling mismatched image aspect ratios, the empty cell you get when you drop a column, and the accessibility of the invisible boxes you add to patch it.
None of it is heavy. A few properties do the visual part, and a couple of small decisions keep it responsive and friendly to screen readers.
How it works
The border is an illusion. You set the grid’s gap to 1px instead of the usual 16, then give the grid element itself a background-color. Now the only place that color is visible is the 1px gutters between cells — and that reads as a thin, perfectly even border between every item. Change the gap and you change the border thickness; change the background and you change the line color. That’s the entire foundation.
Because the “border” is just the grid’s background peeking through, the effect breaks the moment a cell isn’t fully opaque. Images with different aspect ratios don’t fill their cells evenly, so the gutters look uneven — the fix is to set each image to width: 100%, height: 100%, and object-fit: cover, which makes it fill its cell without distortion. Text elements have no background of their own (their box is effectively transparent), so the grid’s color floods in behind them; giving the text cells a solid background that matches the area around the grid restores the effect.
Responsiveness is where the gotchas cluster. If you drop from three columns to two on a smaller breakpoint and you have an odd number of items, the last cell sits empty and the grid background shows through it. You patch that with a placeholder cell whose background matches the surrounding area — but a placeholder is an extra item, so on wider breakpoints (still three columns) it spills onto a new row and reveals a strip of background. The answer is to control the placeholder’s display per breakpoint: hide it where it isn’t needed, show it only where a cell would otherwise be left empty. Keep Webflow’s cascade in mind — a value set on a larger breakpoint flows down to smaller ones unless you override it, so double-check each breakpoint below the one where the placeholder appears.
That empty placeholder is also an accessibility trap. It’s invisible to sighted users, but screen readers don’t automatically ignore empty boxes inside a content structure like a grid — they can announce it as a meaningless element with no context. Adding aria-hidden="true" to the placeholder tells assistive tech to skip it completely: not announced, not focusable. One thing to remember — that attribute lives on the element, not on the class, so if you have a placeholder in more than one grid you have to add it to each one by hand.
CMS grids follow the same logic with one twist: you can’t drop a static element inside a collection list. Instead you add a placeholder item to the collection (one with no image) and solve three problems at once. For accessibility, add a plain-text CMS field (say, one that holds true or false) and bind the item’s aria-hidden attribute to it — false for real items, true for the placeholder. To hide the background in the empty cell, put a placeholder div inside the list item and use conditional visibility to show it only when the image field is empty. And to stop the placeholder sorting to the top, add a sort rule on that field first (alphabetical, so false comes before true and the placeholder drops to the end), then layer your real sort on top.
How to use it
-
Clone the project. Grab the Webflow cloneable — it has the image grid, the text grid, and the CMS grid all set up so you can pull them apart and rebuild them.
-
Make the borders. Select your grid, set its
gapto1px, and give the grid abackground-color. Pick something bright at first (a yellow-orange is easy to see) so you can watch the effect land, then swap it for your real border color. -
Make image cells opaque. Select an image (they can share one class), set
widthandheightto100%, then set the fit to cover. Each image now fills its cell cleanly and the gutters stay even. -
Make text cells opaque. Give your text elements a background color that matches the area surrounding the grid (white, in the cloneable). Without it, the grid’s color shows through the transparent text boxes and kills the effect.
-
Handle the empty cell on smaller breakpoints. When you reduce columns and an odd cell is left empty, add a placeholder div in the grid, style its background to match the surrounding area, and set its
width/heightto100%. Then toggle itsdisplayper breakpoint —nonewhere it isn’t needed,blockonly where a cell would otherwise be empty. Remember the cascade flows downward, so re-check each smaller breakpoint. -
Make the placeholder accessible. On the placeholder element, add the custom attribute
aria-hiddenwith the valuetrue. Add it to every placeholder — it’s set per element, not per class. -
Do it in the CMS. Reuse the grid class on your collection list. Add a placeholder collection item with no image. Add a plain-text field for the aria-hidden value and bind the list item’s
aria-hiddenattribute to it (falsefor real items,truefor the placeholder). Put a placeholder div inside the list item and use conditional visibility to show it only when the image is not set. Finally, add a sort rule on the aria-hidden field (alphabetical) so the placeholder always lands last, then add any sort you actually want on top of it.