Stop Pasting “Make a Dashboard” into ChatGPT — A Web Admin’s Tailwind Playbook
I have opened alot of “AI generated admin dashboards.” They look fine in the screenshot. Then you resize the browser and the sidebar sits on the table like a relative who will not leave. The chart is a purple rectangle. The table cannot sort. Dark mode turns the labels the same colour as the background. Someone still sends it to production becuase the demo GIF was pretty.
If you are the web admin — the person who actualy has to ship an internal tool for finance in Jakarta, or a seller portal that looks less embarassing than the 2014 PHP one — you do not need a miracle UI. You need a repeatable way to get Tailwind output that is boring, accessible, and not a 400-class div.
This is that way. Tailwind CSS v4 flavoured, becuase that is what new projects should be on, and honest about what models still mess up in 2026.
What you’re generating (please name the job)
“Dashboard” is not a spec. An operations home for a logistics team at 7am is not the same as a SaaS analytics screen with four vanity metrics. Before any prompt, write a one-pager even if you are the only reader:
- Who is on this page at 8:15, on what device? (Warehouse Android. Founder iPad. Finance on a laptop with 80 tabs.)
- The three numbers they must see withotu scrolling on a 13-inch screen.
- The one table they live in, and the filters they actualy use (status, cabang, date range, not 12 dropdowns).
- What “empty” looks like. Empty states are where AI UIs look drunk.
I keep a real exmaple in my notes: a snack brand in Tangerang with 12 stores. Morning dashboard is not MRR. It is yesterday’s sales vs target, stockouts, and wich store has not opened the cashier app. If you let the model invent widgets, you get “User Engagement” and a world map. Nobody in that company needs a world map.
Give the model a design system, or it will invent one
Tailwind v4 wants tokens in CSS via @theme, not a second brain in random hex codes. If you let the AI pick bg-[#1e293b] and rounded-[19px] everywhere, you cannot rebrand, you cannot do dark mode cleanly, and you cannot explain the UI to the next intern.
@import "tailwindcss";
@theme {
--color-brand-500: oklch(0.62 0.18 252);
--color-ink: oklch(0.25 0.02 70);
--color-paper: oklch(0.99 0.01 90);
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--shadow-soft: 0 12px 40px rgb(15 23 42 / 0.14);
}
Paste that into the prompt and say: only these tokens plus Tailwind’s scale. No arbitrary hex unless I ask. That one constraint deletes half the mess.
Also say wich version. v4 is CSS-first. If the model writes tailwind.config.js content arrays and @tailwind base, you will spend an afternoon mixing two configs and blaming the compiler. Tell it: v4, @import "tailwindcss", no JS config unless we are migrating.
Class order and static class names
Two rules models violate constantly.
One: order classes in a human way — layout, spacing, size, type, colour, effects, interactive — so diffs are readable. Prettier plugins exist. Still tell the model, becuase the first draft is what you will be stuck reviewing.
Two: never build class names with string concat like text-${color}-500. Tailwind’s scanner will not see them, they will not exist in the CSS, and you will think dark mode is “broken.” Ask for complete class strings, or a map of variants.
The prompt that doesnt produce a Dribbble poster
I use a scaffold prompt, then component prompts. Never “build the whole prodcut.”
Scaffold: You are a front-end engineer building an internal admin in Tailwind CSS v4. Output HTML + a small snippet of CSS for @theme only. Mobile first. Sidebar becomes a drawer under lg. No chart library yet — use labelled empty boxes with min-height. Use semantic elements and visible focus-visible rings. Indonesian UI copy. Rupiah formatting with a simple helper class, left-aligned numbers in tables. Do not use random images. Do not invent routes we did not list: /orders, /stock, /stores, /settings.
Notice I banned charts on pass one. Charts are how the model wastes tokens on a canvas that you will replace with your actual library (Chart.js, Apache ECharts, whatever your stack alredy loaded). A grey box with “sales 7d” is a better first merge.
Pass two is one component at a time: the data table, then the filters, then the header, then dark mode. If you ask for all of it, you get a 1,200 line file with three slightly diffrent button styles.
Table prompts need real constraints
Internal tools are tables with feelings. Ask for:
- sticky header, horizontal scroll on small screens, not a squashed 9-colum miracle
- row hover, selected state, and a bulk bar that only apears when somehting is checked (
has-[:checked]is your friend in v4) - numeric colums
tabular-nums text-right - status pills with tokens, not inline hex
- an empty row that is not “No data found.” Try “Belum ada order hari ini. Coba cabang lain atau ganti tanggal.”
If you know you will load 500+ rows, say so, and ask for pagination UI even if the logic is fake. A pretty table that chokes is how ops peopel go back to Excel and never mention your dashboard again.
Layout patterns that survive a real phone
The layout that keeps working:
- App shell:
flexwith a sidebar that ishidden lg:flexand a main that ismin-w-0 flex-1. Thatmin-w-0is the difference betwen a table that scrolls and a table that blows the page wide. - On small screens, a top bar with a menu button. The drawer is a fixed panel, not the sidebar squeezed to 64px of mystery icons.
- Stats:
grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4. Four KPIs. If you need eight, you do not have KPIs, you have a museum. - Main colum: filters first, then the table, then secondary cards. Do not put a 400px chart above the table if the table is the job.
Container queries are nice in v4 when a widget lives in a variable-width main colum. Named containers keep nested queries from fighting. I only ask the model for container queries after the simple breakpoints work. Otherwise it sprinkles @min-[480px] like confetti.
Dark mode, a11y, and the stuff demos skip
Dark mode should be tokens, not “add dark: to some of the divs.” Define paper/ink/brand for both, switch with a class or data-theme. Then audit every custom background the model snuck in.
Focus rings: focus-visible:ring-2, not outline-none as a lifestyle. Keyboard users in finance teams are not a edge case. They live in forms.
sr-only labels on icon buttons. The hamburger is not self-explanatory just becuase you work in Figma. Contrast: grey-on-grey is the default AI aesthetic. I literaly add “text must pass a rough WCAG AA against its background; no gray-400 on gray-100” to the prompt and it still cheats, so I grep for text-gray-400 after.
Indonesian copy has longer words. “Berlangsung” vs “Live.” “Selengkapnya.” Buttons need more padding than the English mock. Tell the model the UI langauge up front or your 88px sidebar labels wrap into tragedy.
What I still never trust the model with
Auth screens copied from a random template with “Sign in with GitHub” on an app for store supervisors. Ask for email + OTP if that’s what you use. Local realism matters or the stakeholder thinks the whole prodcut is a toy.
Chart colours that ignore colour-blindness and print. Operations peopel screenshot into WhatsApp groups. Neon-on-navy becomes mud. Use brand + one alert red + one ok green, and patterns, not 8 pastels.
Dynamic Tailwind classes. Mentioned alredy. I will mention it untill I die.
Fake accessibility. div with onClick and no role, no keyboard. If it is a button, it is a <button>. This is not a style opinion.
Installing a whole kit when you needed a page. shadcn/ui and similiar are great if you alredy live there. If the model starts scaffolding 40 components for a three-page internal tool, stop it. You will own those files at 11pm when a colum is wrong.
A realistic build order for an admin who is not a designer
- Tokens in
@theme. Brand colour from the existing site, not a new personality. - App shell with drawer. Test at 375px. If the sidebar fails, nothign else matters.
- KPI row with four cards, real label copy, skeleton pulse optional.
- Filters as a wrapping flex, not a single crowded row.
- Table with 12 realistic dummy rows (Indonesian names, Rp, WIB timestamps). Dummy data that looks like yours exposes width problems.
- Empty and error states.
- Dark mode token switch.
- Then charts, if you still want them.
At each step, paste the current file back into the model and say “only modify X.” Context windows are big now. Discipline is still the scarce resource. If you say “improve the UI,” it will restyle everythign and you cannot review the diff.
Dummy data that looks like Indonesia
Ask for it explicitly. Rp 1.250.000 with the right thousand seperator, phone numbers 08…, dates in dd/mm/yyyy HH:mm or relative “3 jam lalu.” Store names like “Cabang BSD” and “Kuta 2,” not “New York Flagship.” This is not nationalism. This is how you catch that the amount colum is too narrow before the CFO does.
When a template is smarter than a prompt
If you need auth, billing, and 15 pages by next week, starting from a known Tailwind dashboard kit can be saner than generating from zero. Use AI to restyle tokens and replace English, not to invent a design sytem on Tuesday. I only generate from scratch when the internal tool has a wierd workflow that templates fight — like a packing station UI that is mostly one fat table and huge buttons for gloves-on use.
There is no prize for a unique admin aesthetic. Clarity is the aesthetic. Your users alredy have taste fatigue from consumer apps. Give them alignment, type they can read at 7am, and a filter that remembers cabang.
A short war story, then you can go
We asked a model to “make a Tailwind dashboard for a clinic in Depok.” First output: a fitness-app gradient, English, a graph of “wellness score,” no appointment table. Second prompt, with tokens, routes, and Bahasa: still a graph on top. Third: I forbade charts and demanded the appointment table with doctor name, poli, and status. That third file, after I fixed min-w-0 and the drawer, is still in production with real data wired in. The wellness score is not.
You will have that third prompt too. Save it. Name it admin-shell.md. The playbook is the prompt plus the veto list, not the pretty first render.
Do this on the next internal tool
Write the one-pager. Paste tokens. Ban charts on pass one. Generate the shell, then the table, then the rest. Test on your own phone on the office Wi-Fi, wich is worse than you think. Grep for arbitrary hex and outline-none. Put real rupiah in the dummy rows.
If the sidebar behaves and the table scrolls, you alredy beat 80% of generated dashboards on the internet. The rest is wiring APIs, wich is a diffrent headache and honestlly more honest work.
Reviewing AI markup without losing an evening
The hidden cost is not generation. It is review. A 400-line first draft looks “done” and then you find three paddings that almost match, a header that is a div, and a filter that doesnt reset. Build a 10-minute review, same every time, or you will either rubber-stamp junk or rewrite the file by hand and swear off AI for a month.
I walk the page in this order, on a real phone and a laptop, before I even look at the class soup:
- Can I open the drawer, close it, and not trap focus? If the overlay does not click-away, fix that before colours.
- Does the table’s first colum stay readable when I sort by the last colum? Horizontal scroll is allowed. Mystery overflow on the body is not.
- Tab through every control. If I lose the ring, that control is not done.
- Toggle dark mode on a KPI card and on a status pill. If one of them stays light, the tokens were bypassed.
- Paste a long Indonesian label — “Menunggu konfirmasi pembayaran” — into a pill and a button. If the layout cracks, the English mock lied to you.
Only after that do I grep. Search for style=, #[0-9a-f], outline-none, loading="lazy" on icons that are 16px anyway, and any h-screen nested inside another h-screen (classic double scroll). Then I ask the model to patch those grep hits only. Narrow patches are how you keep the good table while killing the neon chart it snuck back in.
Save a “known good” shell in the repo — empty layout, tokens, drawer, dummy table — and start every new admin from that file, not from a blank chat. The chat is for filling the table colums, not for reinventing the app chrome becase you liked a screenshot on Twitter at midnight.
If the sidebar behaves and the table scrolls, you alredy beat most generated dashboards on the internet. Wiring APIs is a diffrent headache and, honestlly, more honest work.
And please, if the model offers a world map, smile and delete it. Unless you actualy ship globally. You probaly ship to Bekasi first.

Post a Comment for "Stop Pasting “Make a Dashboard” into ChatGPT — A Web Admin’s Tailwind Playbook"
Post a Comment