Dev.to · 6 min read

The one tell of AI-built sites, and a CSS block that fixes it

The one tell of AI-built sites, and a CSS block that fixes it

Every site I built with a coding agent this year had the same problem, and it took me a while to name it. The layout was fine. The type was fine. The copy was fine. But every section sat on the same solid color, nothing was in front of or behind anything, and the whole page read as a mockup that had been colored in. Adding components doesn't fix it. Another gradient doesn't fix it. What's missing is depth: a ground for the page to sit on, one light source, and a few sections mounted above the rest. So I wrote a skill that adds exactly that, as one CSS block. What the block does unflat is an Agent Skill: a SKILL.md plus six reference files that Claude Code, Cursor or Codex reads. The agent audits your page, pulls out your tokens (background, text, accent, corner radius) and appends one block to your global stylesheet, between two comments. Nine moves: Ground. The page background goes 1–3% darker in OKLab lightness. Text contrast is checked, never broken. Light. One fixed layer with a directional radial light, a vignette and a faint SVG grain. Two pseudo-elements, zero markup. Rhythm. Not every section. The hero, one-row strips and sections with their own artwork stay on the ground; the sections you read are mounted between them, about a third to a half of the page. Surfaces. Those sections sit 2–3% L above the ground with a lit top edge and a shadow below. Ground and surface are never more than 6% apart, so nothing turns into a card. Glow. One, under the section the visitor should act on. Alignment. The inset is compensated so the text stays on the header's grid. Fields. Two soft washes, one from your accent and one from its temperature opposite: a blue site gets a peach wash, an amber site gets a cool blue one. Motif. One faint shape from what the site is about. Rings for a camera page, a fading grid for a workshop, one arc for a journal, nothing for a bank. Cards. Your own cards get a fill and a 3px hover lift on the surfaces, for pointers that can hover only. Everything is derived from the site. Delete the block and the page is byte-for-byte what it was; a checker in the repo proves that for every example. Where the rules came from I didn't invent the numbers. I had one e-commerce prototype I'd tuned by hand until it looked built, and I measured it: eleven sections, four of them mounted, ground between every two, the store page a single sheet centered at 1440px. Those measurements became the first version of the skill. Then I ran the skill with a fresh agent on a demo page, looked at the result, fixed the skill, and repeated. Three things broke in ways that taught me something. It mounted every section. Side by side with the flat page, the first result read as the same page cut into boxes. That's where the rhythm rule came from: a mounted surface only reads as mounted if there is ground next to it. It was correct but thin on a light page. On a white, Apple-style product page the block did what it said and nothing more. I gave the same model, in a fresh session, the same page with a one-paragraph brief ("the sections look pasted on one tone; frame them, give them structure, add whatever looks best") and compared the two results. The differences (a larger radius on surfaces than on cards, two washes, one motif, cards as a second level) became the taste rules. Cards floated on mobile. The first person who ran it on a real project said the cards "get lifted a bit too much on mobile, like they're floating away from the rest of the page." Right: under 640px the cards stack full-width, the desktop shadow reads as hovering, and a hover lift fires on tap. v1.2.1 swaps in a tighter shadow below 640px and gates the lift behind (hover: hover). The repo keeps a dogfood log of all of this. Every "after" page in the examples was produced by the skill itself running on a mid-tier model, not by hand. The skeleton, if you want the idea without the skill This is the shape of the block on a dark theme, reduced to the parts that carry the effect. The real one has about forty lines and every value is derived from your tokens. /* unflat: start */ :root{ --unflat-ground:#0c0b0a; /* page background, 3% L darker */ --unflat-surface-top:#1c1b1a; /* 2% L lighter than the surface base */ --unflat-surface-bottom:#161514; /* page background, 2-3% L lighter */ --unflat-edge:rgba(236,231,223,.10); --unflat-shadow:0 40px 90px -50px rgba(0,0,0,.9),0 2px 0 rgba(0,0,0,.55); --unflat-inset:clamp(8px,1.6vw,24px); --unflat-gap:clamp(14px,2vw,28px); } html{background:var(--unflat-ground)} body{background:transparent} /* one light source: directional light top-left, vignette bottom-right, grain */ body::before{content:'';position:fixed;inset:0;z-index:-2;pointer-events:none; background: radial-gradient(130% 90% at 12% -15%,rgba(255,196,150,.075),transparent 58%), radial-gradient(100% 80% at 100% 110%,rgba(0,0,0,.45),transparent 60%)} /* the mounted sections only: never the hero, never every section */ main > :is(.features,.pricing,.faq){ position:relative;isolation:isolate;overflow:clip; margin-block:var(--unflat-gap);margin-inline:var(--unflat-inset); background:linear-gradient(180deg,var(--unflat-surface-top),var(--unflat-surface-bottom)); border:1px solid rgba(236,231,223,.10);border-top-color:var(--unflat-edge); box-shadow:var(--unflat-shadow)} /* a 1px light line along the top edge */ main > :is(.features,.pricing,.faq)::before{content:'';position:absolute;left:8%;right:8%;top:0;height:1px; background:linear-gradient(90deg,transparent,rgba(236,231,223,.22),transparent)} @media (max-width:640px){:root{--unflat-inset:0px}} /* unflat: end */ The part that took the work is not in this snippet: which sections go in that :is() list, and how the values come out of the site's own palette without breaking contrast. That is what the skill's reference files are for. Install npx skills add merturl4576/unflat Then, in Claude Code, Cursor or Codex: "This page looks flat. Use the unflat skill on app/globals.css. Keep the header as it is." It works with plain CSS and with Tailwind (the block is layer-less CSS appended after your imports). It does not do app dashboards, pages that already have depth, or anything that needs markup changes. What I'd like to know Two things. Whether the effect reads as "built" or as "boxes" to you. And what breaks when you run it on a real page: post the section that broke, here or as an issue, and I'll fix the skill. Repo: github.com/merturl4576/unflatshowdevcssaiwebdev

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More AI & Machine Learning News