You know, like a tic-tac-toe board. I was just pondering how to make a table with borders only on the inside the other day, as one does. There are three ways I can think of. One involves a good handful of rules and is the way I intuitively think of, one involves a deprecated attribute, and one is very simple and feels kinda like a CSS trick.

Possibility #1) Removing the Borders You Don’t Need
This is the first way I think of. Add a border everywhere, then remove the border on the:
- The top of every cell in the first row
- The bottom of every cell in the last row
- The left of the first cell in every row
- The right of last cell in every row
table {
border-collapse: collapse;
}
table td {
border: 5px solid black;
}
table tr:first-child td {
border-top: 0;
}
table tr td:first-child {
border-left: 0;
}
table tr:last-child td {
border-bottom: 0;
}
table tr td:last-child {
border-right: 0;
}
rules
Attribute
Possibility #2) The This is not recommended as it’s a deprecated attribute. But, that’s what the rules
attribute was specifically for.
You can control the color with border-color
, but not border-width
or border-style
.
border-style: hidden;
Possibility #3) Using This is the one that feels like a CSS trick to me.
table {
border-collapse: collapse;
border-style: hidden;
}
table td {
border: 5px solid black;
}
MDN has an explanation:
In case of table cell and border collapsing, the hidden value has the highest priority: it means that if any other conflicting border is set, it won’t be displayed.
By putting border-style: hidden;
on the table itself, it means that “hidden” wins on that outside edge, but only the outside edge, not any of the other borders on the inside cells.
outline
property
Possibility #4: Use the Carter Li wrote about this then challenged me:
Maybe something like this tic-tac-toe board Chris put together several years ago could benefit from
outline
, instead of resorting to individually-crafted cell borders. Challenge accepted, Mr. Coyier? 😉
Gotta step to that challenge! Looks like thanks to outline-offset
it can be done!
table {
outline: 2px solid white;
outline-offset: -2px;
}
table td {
outline: 2px solid black;
}
Possibility #5: Gradients
Bonus! Temani Afif cooked up a way to do it with a singular conic gradient. It blows my mind so I won’t attempt to explain it.
Can you think of other ways?
No overrides needed for either of these techniques, meaning you could combine them with other rules that set the surrounding table border and not having to worry about source order or specificity.
Adjacent selector (
td + td
andtr + tr td
)Not first-child selector (
td:not(:first-child)
andtr:not(:first-child) td
)This seems like the cleanest solution to me
This is definitely how I’d do it. Rather than set a rule and then override it, just apply the styling where it’s needed once.
Adjacent selector was my first thought as well.
TIL about the rules attribute! That last one is pretty smart—I’ll have to try this out, I’ve been doing the border-cancelling methods like the top of the article lately
Ah! I didn’t know the last one. Tables are so old, and yet there’s always something to learn, it seems.
Yes, I can thing of other ways, but they’re not as pretty haha:
Here’s another simple way but again is a bit of a hack.
http://codepen.io/paulobrien/full/wgdmZx/
All that’s needed is to add a transparent border to the table element that is 1px wider than the border on the cells and as per border conflict resolution the wider border should be used in favour of the narrower border.
Rule 3 here:
https://www.w3.org/TR/REC-CSS2/tables.html#border-conflict-resolution
How about adding a
double
border to overrule the outside?It gives you some weird slanted line ends in IE, but otherwise seems to work fine.