Open pub-trivia.app and look at the block of six feature cards. There is a one pixel grid between them, and the lines of that grid do not stop at the edge of the block. They carry on for about half a centimetre past each corner and fade out.
Nothing in there is a border. There are 60 linear-gradient declarations in the markup of that page, and the grid itself is drawn by a background colour showing through gaps.
$ curl -s https://pub-trivia.app/ | grep -o 'linear-gradient' | wc -l 104 Sixty in the HTML and 44 in the React flight payload underneath it, which is the same elements described twice, once for the browser and once for hydration.
The grid lines are a background, not a border
The whole technique is this one element:
<div className="grid gap-px border sm:grid-cols-2 lg:grid-cols-3" style={{ backgroundColor: BORDER, borderColor: BORDER }}> {features.map((f) => ( <div key={f.title} className="bg-background p-6 space-y-3 ..."> The container is painted the border colour. The children are painted the page background. gap-px leaves one pixel between them, and what you see in that pixel is the container showing through. One declaration gives you every internal line at once, they are automatically correct when the column count changes at a breakpoint, and there are no doubled-up borders where two cells meet and no clever :last-child rules.
Compared with divide-x divide-y, which is Tailwind's answer to the same problem, it has one advantage that turns out to matter: the lines are not attached to the cells. They belong to the container. Which is what makes the next part possible.
The overhang is eight gradients
A border cannot extend past the element it is on. These lines do, so they are not borders. Every grid on the site is wrapped in a relative container with eight absolutely positioned one pixel elements in it, two per corner:
const BLEED = 48 const BORDER = `hsl(var(--border) / 0.4)` {[ { corner: "top-left", x: "0%", y: "0%" }, { corner: "top-right", x: "100%", y: "0%" }, { corner: "bottom-left", x: "0%", y: "100%" }, { corner: "bottom-right", x: "100%", y: "100%" }, ].map(({ corner, x, y }) => ( <div key={corner} className="absolute pointer-events-none" style={{ left: x, top: y }}> <div className="absolute" style={{ width: "1px", height: BLEED, transform: "translateX(-0.5px)", ...(corner.startsWith("top") ? { bottom: 0 } : { top: 0 }), background: corner.startsWith("top") ? `linear-gradient(to top, ${BORDER}, transparent)` : `linear-gradient(to bottom, ${BORDER}, transparent)`, }} /> ... Each arm is 48 pixels long, one pixel wide, and fades from the border colour to transparent in the direction it travels, so it reads as the grid line running out of the frame rather than as a stub.
Three details in there do all the work.
translateX(-0.5px) centres a one pixel line on a zero width boundary. Without it every arm sits half a pixel to one side of the line it continues, which on a non-retina screen is the difference between one crisp line and two grey ones.
pointer-events: none on every decorative element, because eight invisible 48 pixel strips hanging off the corners of a clickable grid is a way to eat clicks that nobody will ever debug.
And the gradient direction is computed from the corner name rather than written out four times, which is the only reason this is 20 lines instead of 60.
The full width dividers between rows and columns use the same idea with the fade at both ends:
background: `linear-gradient(to right, transparent, ${BORDER} ${BLEED}px, ${BORDER} calc(100% - ${BLEED}px), transparent)` Solid for everything except the first and last 48 pixels. The same number as the arm length, so the fade in from the left and the arm hanging off the corner are the same length and look like one gesture.
The one place it drew a grey rectangle
The hairline trick has a failure mode that follows directly from how it works: the lines are the container showing through, so an empty cell is not empty. It is a hole with the border colour behind it.
The block at the foot of every content page that links sideways to related pages used to be a two column grid built this way. Its own source comment now explains what happened:
* A divided list rather than a grid, because the number of links is whatever a * page's `related` happens to hold. The first version was a two-column grid * using the hairline trick from FeaturesGrid, `gap-px` over a border-coloured * background, which works there because six features fill three columns * exactly. Here three links leave a fourth cell with nothing in it, and that * cell is not empty: it is the container's border colour showing through, so an * odd number of links rendered a grey rectangle on the page. Six feature cards fill a three column grid exactly, and a pricing section with two plans fills two columns exactly, which is why the technique survived everywhere it is still used. The related links block takes however many paths a page's related array holds, and plenty of pages have three. So one page in a cluster looked fine and the next one had a grey block bottom right, which reads as a broken image rather than as a layout bug.
It is a list now, and you can see the result at the foot of any content page, for instance how to host a pub quiz or the session history feature. Those pages contain zero gradients and zero gap-px:
$ curl -s https://pub-trivia.app/features/session-history | grep -c 'linear-gradient' 0 The lesson I took is narrower than "do not use this trick". It is that this trick requires a cell count you control. Fixed content, fine. A list whose length comes from data, not fine, and the symptom is not a missing line, it is an extra shape.
BLEED = 48, seven times
$ grep -rn "const BLEED = 48" app components app/dashboard/plans/loading.tsx:3 app/dashboard/billing/loading.tsx:3 app/dashboard/billing/page.tsx:11 components/HowItWorksGrid.tsx:28 components/FeaturesGrid.tsx:17 components/PricingSection.tsx:7 components/PricingCards.tsx:9 Seven files, plus an eighth copy in our animated product demo under the names DEMO_ROW_BLEED and DEMO_ROW_BORDER. The eight corner arms are copy pasted alongside them, which means the same 20 line block appears eight times in the repository with the only difference being which breakpoints get which dividers.
Two of those files are loading skeletons, and that is the one part of the duplication I would defend. A skeleton has to draw the same frame as the page it stands in for, or the layout jumps when the real content arrives, and a skeleton that imports from the page it is a skeleton for is a worse kind of coupling than a copied constant.
The other five want to be one component. Something like:
<HairlineGrid cols={{ base: 1, sm: 2, lg: 3 }}>{cells}</HairlineGrid> which computes the dividers from the column counts instead of having three hand written sets guarded by hidden sm:block lg:hidden. The reason it has not happened is that each of the five has slightly different breakpoints, and a component that takes a breakpoint map is not obviously easier to read than the explicit version. That is the usual argument for leaving duplication alone, and it is usually wrong after the fifth copy.
The line this all quietly depends on
const BORDER = `hsl(var(--border) / 0.4)` That is a CSS colour function assembled in JavaScript from a custom property, and it only works because of how the property is stored:
--border: 0 0% 18%; Three bare HSL channels, no function around them. That is the shadcn convention, and it exists precisely so that hsl() can be wrapped around the variable with an alpha appended, which is what both the Tailwind config and all eight of these files do.
It is also the one thing in this whole system that could break silently. The modern Tailwind v4 idiom is to store colours as complete oklch() values. The day somebody migrates the palette to that, hsl(var(--border) / 0.4) becomes hsl(oklch(0.2 0 0) / 0.4), which is not a colour, so every arm and every divider renders nothing. Meanwhile border-border and every other utility keeps working perfectly, because the config would have been updated with the palette.
Nothing would throw. No test would fail; we have no component rendering tests that assert on computed styles. The hairlines would just quietly go away, and the first report would be somebody saying the homepage looks flatter than they remember.
So if you want to check whether that has happened, it is one line in a browser console on the homepage:
getComputedStyle(document.querySelector('[style*="linear-gradient"]')).background If it names a colour, the hairlines are still there. The view source version works too: 60 gradients in the markup of the homepage, 18 on /pricing, and zero on all nine of the content pages I checked, out of 69, which is a reasonable summary of where we decided decoration was worth bytes.