Pure TailwindMIT licensed

Tailwind Portfolio Card with CSS-Drawn Dashboard Mock

A case-study tile whose thumbnail is a miniature dashboard drawn entirely in grid and flex — side rail, KPI row, eight-bar chart — with no image at all. The port's win is that the mock's eight bars become a flex row of flex-1 spans whose heights are the only thing that varies.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="twc-14 w-full min-h-svh flex items-center justify-center px-5 py-9 bg-twc14-bg font-twc14-sans">
  <article class="group/card w-full max-w-[340px] overflow-hidden rounded-2xl bg-white border border-black/6 shadow-[0_2px_12px_oklch(0_0_0/0.04)] transition-[transform,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_32px_oklch(0.55_0.12_45/0.16)] motion-reduce:transition-none motion-reduce:hover:translate-y-0">

    <div class="relative aspect-video overflow-hidden [background:linear-gradient(135deg,#f7edd6,#e8c9a0)]">
      <div aria-hidden="true" class="absolute inset-3.5 overflow-hidden rounded-md bg-white shadow-[0_4px_12px_oklch(0.4_0.08_50/0.16)] transition-transform duration-350 ease-out group-hover/card:scale-104 motion-reduce:transition-none motion-reduce:group-hover/card:scale-100">
        <div class="h-3 bg-twc14-mockbar border-b border-black/6"></div>
        <div class="grid grid-cols-[40px_1fr] h-[calc(100%-12px)]">
          <div class="flex flex-col gap-[5px] px-1.5 py-2 bg-twc14-mockside">
            <span class="block h-[5px] rounded-sm bg-twc14-acc"></span>
            <span class="block h-[5px] rounded-sm bg-twc14-mockline"></span>
            <span class="block h-[5px] rounded-sm bg-twc14-mockline"></span>
            <span class="block h-[5px] rounded-sm bg-twc14-mockline"></span>
          </div>
          <div class="flex flex-col gap-1.5 p-2">
            <div class="grid grid-cols-3 gap-1">
              <span class="h-5 rounded-[3px] bg-twc14-mockbar"></span>
              <span class="h-5 rounded-[3px] bg-twc14-mockbar"></span>
              <span class="h-5 rounded-[3px] bg-twc14-mockbar"></span>
            </div>
            <div class="flex-1 flex items-end gap-[3px] pt-1">
              <span style="height:35%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:55%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:40%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:70%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:60%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:85%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:50%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:75%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
            </div>
          </div>
        </div>
      </div>
      <div aria-hidden="true" class="absolute inset-0 pointer-events-none transition-opacity duration-300 group-hover/card:opacity-0 [background:linear-gradient(180deg,transparent_60%,oklch(0_0_0/0.05))] motion-reduce:transition-none"></div>
    </div>

    <div class="px-4.5 pt-4 pb-4.5">
      <span class="inline-block mb-2.5 px-2.5 py-1 rounded-full font-twc14-mono text-[10px] font-bold tracking-[0.14em] text-twc14-accink bg-twc14-acc/10">SAAS &middot; DASHBOARD</span>
      <h3 class="m-0 mb-1.5 text-base font-bold tracking-[-0.01em] text-twc14-ink">Meridian Analytics Rebrand</h3>
      <p class="m-0 mb-3 text-[12.5px]/[1.55] text-twc14-muted">Redesigned the analytics dashboard for 12k+ enterprise users &mdash; 41% faster time-to-insight.</p>
      <ul class="flex flex-wrap gap-1.5 list-none m-0 mb-3.5 p-0">
        <li class="px-2 py-[3px] rounded bg-twc14-tag text-[11px] font-semibold text-twc14-muted">Figma</li>
        <li class="px-2 py-[3px] rounded bg-twc14-tag text-[11px] font-semibold text-twc14-muted">Design System</li>
        <li class="px-2 py-[3px] rounded bg-twc14-tag text-[11px] font-semibold text-twc14-muted">Data Viz</li>
      </ul>
      <div class="flex items-center justify-between gap-2 pt-3 border-t border-black/6">
        <span class="text-xs font-semibold text-twc14-muted">Meridian Labs</span>
        <a href="#project" class="inline-flex items-center gap-1.5 min-h-11 rounded-md no-underline text-[12.5px] font-semibold text-twc14-accink transition-colors duration-200 hover:text-twc14-accdark hover:underline hover:underline-offset-[3px] focus-visible:outline-2 focus-visible:outline-twc14-acc focus-visible:outline-offset-[3px]">
          View project
          <span aria-hidden="true" class="inline-flex items-center transition-transform duration-200 ease-out group-hover/card:translate-x-[3px] motion-reduce:transition-none motion-reduce:group-hover/card:translate-x-0">
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
          </span>
        </a>
      </div>
    </div>
  </article>
</section>
@import "tailwindcss";

@theme {
  --color-twc14-bg: #f8f7f4;
  --color-twc14-bg: oklch(0.97 0.005 90);
  --color-twc14-ink: #1a1a1a;
  --color-twc14-ink: oklch(0.21 0.005 270);
  --color-twc14-muted: #6b6459;
  --color-twc14-muted: oklch(0.49 0.015 75);
  --color-twc14-acc: #b8613b;
  --color-twc14-acc: oklch(0.55 0.12 45);
  --color-twc14-accink: #9c4e2d;
  --color-twc14-accink: oklch(0.47 0.13 42);
  --color-twc14-accdark: #7c3a1e;
  --color-twc14-accdark: oklch(0.38 0.12 42);
  --color-twc14-tag: #f5f2ec;
  --color-twc14-tag: oklch(0.96 0.008 85);
  --color-twc14-mockbar: #f0eae1;
  --color-twc14-mockbar: oklch(0.93 0.012 80);
  --color-twc14-mockside: #faf7f2;
  --color-twc14-mockside: oklch(0.97 0.008 80);
  --color-twc14-mockline: #e0d5c4;
  --color-twc14-mockline: oklch(0.87 0.02 80);
  --font-twc14-sans: 'Space Grotesk', system-ui, sans-serif;
  --font-twc14-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
/* The mock dashboard is entirely utilities; the stylesheet is tokens plus
   the root contract. */

.twc-14 {
  width: 100%;
  min-height: 100svh;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-twc14-bg: #f8f7f4;
  --color-twc14-bg: oklch(0.97 0.005 90);
  --color-twc14-ink: #1a1a1a;
  --color-twc14-ink: oklch(0.21 0.005 270);
  --color-twc14-muted: #6b6459;
  --color-twc14-muted: oklch(0.49 0.015 75);
  --color-twc14-acc: #b8613b;
  --color-twc14-acc: oklch(0.55 0.12 45);
  --color-twc14-accink: #9c4e2d;
  --color-twc14-accink: oklch(0.47 0.13 42);
  --color-twc14-accdark: #7c3a1e;
  --color-twc14-accdark: oklch(0.38 0.12 42);
  --color-twc14-tag: #f5f2ec;
  --color-twc14-tag: oklch(0.96 0.008 85);
  --color-twc14-mockbar: #f0eae1;
  --color-twc14-mockbar: oklch(0.93 0.012 80);
  --color-twc14-mockside: #faf7f2;
  --color-twc14-mockside: oklch(0.97 0.008 80);
  --color-twc14-mockline: #e0d5c4;
  --color-twc14-mockline: oklch(0.87 0.02 80);
  --font-twc14-sans: 'Space Grotesk', system-ui, sans-serif;
  --font-twc14-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
/* The mock dashboard is entirely utilities; the stylesheet is tokens plus
   the root contract. */

.twc-14 {
  width: 100%;
  min-height: 100svh;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;800&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • A twelve-element CSS-drawn dashboard needed sixteen rules. As utilities it is two nested grids and a flex row, and the eight chart bars differ by exactly one inline height each.
  • The mock's greys became @theme tokens (--color-twc14-mockbar and friends) rather than hex literals, because they repeat across the rail, the KPI row and the top bar — that is a scale, so it earns tokens.
  • Two hover reveals from one group, each with its own motion-reduce twin: the scale becomes a still frame and the overlay simply stays.
  • UI change from the CSS original: the View-project link gained a hover underline so the affordance is not motion-and-colour only, and the → text character became an SVG so it renders identically across font stacks.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Portfolio Card with CSS-Drawn Dashboard Mock
Source: https://codefronts.com/components/tailwind-css-cards/tailwind-portfolio-card-css-dashboard-mock/

A case-study tile whose thumbnail is a miniature dashboard drawn entirely in grid and flex &mdash; side rail, KPI row, eight-bar chart &mdash; with no image at all. The port's win is that the mock's eight bars become a flex row of flex-1 spans whose heights are the only thing that varies.
## HTML
```html
<section class="twc-14 w-full min-h-svh flex items-center justify-center px-5 py-9 bg-twc14-bg font-twc14-sans">
  <article class="group/card w-full max-w-[340px] overflow-hidden rounded-2xl bg-white border border-black/6 shadow-[0_2px_12px_oklch(0_0_0/0.04)] transition-[transform,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_32px_oklch(0.55_0.12_45/0.16)] motion-reduce:transition-none motion-reduce:hover:translate-y-0">

    <div class="relative aspect-video overflow-hidden [background:linear-gradient(135deg,#f7edd6,#e8c9a0)]">
      <div aria-hidden="true" class="absolute inset-3.5 overflow-hidden rounded-md bg-white shadow-[0_4px_12px_oklch(0.4_0.08_50/0.16)] transition-transform duration-350 ease-out group-hover/card:scale-104 motion-reduce:transition-none motion-reduce:group-hover/card:scale-100">
        <div class="h-3 bg-twc14-mockbar border-b border-black/6"></div>
        <div class="grid grid-cols-[40px_1fr] h-[calc(100%-12px)]">
          <div class="flex flex-col gap-[5px] px-1.5 py-2 bg-twc14-mockside">
            <span class="block h-[5px] rounded-sm bg-twc14-acc"></span>
            <span class="block h-[5px] rounded-sm bg-twc14-mockline"></span>
            <span class="block h-[5px] rounded-sm bg-twc14-mockline"></span>
            <span class="block h-[5px] rounded-sm bg-twc14-mockline"></span>
          </div>
          <div class="flex flex-col gap-1.5 p-2">
            <div class="grid grid-cols-3 gap-1">
              <span class="h-5 rounded-[3px] bg-twc14-mockbar"></span>
              <span class="h-5 rounded-[3px] bg-twc14-mockbar"></span>
              <span class="h-5 rounded-[3px] bg-twc14-mockbar"></span>
            </div>
            <div class="flex-1 flex items-end gap-[3px] pt-1">
              <span style="height:35%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:55%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:40%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:70%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:60%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:85%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:50%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
              <span style="height:75%" class="flex-1 rounded-t-sm [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>
            </div>
          </div>
        </div>
      </div>
      <div aria-hidden="true" class="absolute inset-0 pointer-events-none transition-opacity duration-300 group-hover/card:opacity-0 [background:linear-gradient(180deg,transparent_60%,oklch(0_0_0/0.05))] motion-reduce:transition-none"></div>
    </div>

    <div class="px-4.5 pt-4 pb-4.5">
      <span class="inline-block mb-2.5 px-2.5 py-1 rounded-full font-twc14-mono text-[10px] font-bold tracking-[0.14em] text-twc14-accink bg-twc14-acc/10">SAAS &middot; DASHBOARD</span>
      <h3 class="m-0 mb-1.5 text-base font-bold tracking-[-0.01em] text-twc14-ink">Meridian Analytics Rebrand</h3>
      <p class="m-0 mb-3 text-[12.5px]/[1.55] text-twc14-muted">Redesigned the analytics dashboard for 12k+ enterprise users &mdash; 41% faster time-to-insight.</p>
      <ul class="flex flex-wrap gap-1.5 list-none m-0 mb-3.5 p-0">
        <li class="px-2 py-[3px] rounded bg-twc14-tag text-[11px] font-semibold text-twc14-muted">Figma</li>
        <li class="px-2 py-[3px] rounded bg-twc14-tag text-[11px] font-semibold text-twc14-muted">Design System</li>
        <li class="px-2 py-[3px] rounded bg-twc14-tag text-[11px] font-semibold text-twc14-muted">Data Viz</li>
      </ul>
      <div class="flex items-center justify-between gap-2 pt-3 border-t border-black/6">
        <span class="text-xs font-semibold text-twc14-muted">Meridian Labs</span>
        <a href="#project" class="inline-flex items-center gap-1.5 min-h-11 rounded-md no-underline text-[12.5px] font-semibold text-twc14-accink transition-colors duration-200 hover:text-twc14-accdark hover:underline hover:underline-offset-[3px] focus-visible:outline-2 focus-visible:outline-twc14-acc focus-visible:outline-offset-[3px]">
          View project
          <span aria-hidden="true" class="inline-flex items-center transition-transform duration-200 ease-out group-hover/card:translate-x-[3px] motion-reduce:transition-none motion-reduce:group-hover/card:translate-x-0">
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
          </span>
        </a>
      </div>
    </div>
  </article>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-twc14-bg: #f8f7f4;
  --color-twc14-bg: oklch(0.97 0.005 90);
  --color-twc14-ink: #1a1a1a;
  --color-twc14-ink: oklch(0.21 0.005 270);
  --color-twc14-muted: #6b6459;
  --color-twc14-muted: oklch(0.49 0.015 75);
  --color-twc14-acc: #b8613b;
  --color-twc14-acc: oklch(0.55 0.12 45);
  --color-twc14-accink: #9c4e2d;
  --color-twc14-accink: oklch(0.47 0.13 42);
  --color-twc14-accdark: #7c3a1e;
  --color-twc14-accdark: oklch(0.38 0.12 42);
  --color-twc14-tag: #f5f2ec;
  --color-twc14-tag: oklch(0.96 0.008 85);
  --color-twc14-mockbar: #f0eae1;
  --color-twc14-mockbar: oklch(0.93 0.012 80);
  --color-twc14-mockside: #faf7f2;
  --color-twc14-mockside: oklch(0.97 0.008 80);
  --color-twc14-mockline: #e0d5c4;
  --color-twc14-mockline: oklch(0.87 0.02 80);
  --font-twc14-sans: 'Space Grotesk', system-ui, sans-serif;
  --font-twc14-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
/* The mock dashboard is entirely utilities; the stylesheet is tokens plus
   the root contract. */

.twc-14 {
  width: 100%;
  min-height: 100svh;
}
```

How this works

The chart is a flex row. Eight spans, each flex-1 with a gradient fill and a top-only radius; the only per-bar value is the height:

<div class="flex-1 flex items-end gap-[3px] pt-1">
  <span style="height:35%" class="flex-1 rounded-t-sm
    [background:linear-gradient(180deg,#d97e4a,#b8451f)]"></span>

In JSX these become style={{height:'35%'}} or a mapped array — the note is in the gotchas because a string style attribute is the one thing that does not survive the paste.

Two reveals, one group. The mock scales up while a subtle bottom overlay fades out. Both read group-hover/card:, and both carry a motion-reduce twin, so the reduced-motion state is a still frame rather than a half-animation.

The footer link is an arrow plus an underline. Same reasoning as the SaaS card: with motion reduced, a colour change alone is not a sufficient affordance.

Make it yours

  • Swap the mock for a real screenshot: drop an <img class=&quot;absolute inset-0 size-full object-cover&quot;> into the thumb and delete the mock block — the 16:9 ratio and hover scale still apply.
  • --color-twc14-acc retints the chip, the hover shadow and the link; the chart gradient is separate so the thumbnail keeps its own warmth.
  • Tags are flex flex-wrap gap-1.5 — five tags wrap without a container query.
  • For a grid: grid grid-cols-[repeat(auto-fill,minmax(280px,1fr))] gap-6 on the parent and drop the card's max-w.

Gotchas — read before shipping

  • String style=&quot;height:35%&quot; attributes do not survive a paste into JSX. Convert to style={{height:'35%'}} or map an array of eight values.
  • scale-104 on an element inside a rounded-md overflow-hidden box can leak a pixel at the corner in Safari. Drop to scale-102 or add will-change-transform if you see a seam.
  • The chip's accent on a 10% tint is ~4.6:1 against white — measure it against the actual page background, not white in isolation, before shipping a tinted grid.
  • Three simultaneous property transitions per card times thirty cards saturates the compositor on 60Hz Android. content-visibility:auto is the mitigation.
  • Keep the link's min-h-11 even when the visible text is 20px tall — 2.5.5 is about the hit area, not the glyph.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

aspect-ratio and grid are Baseline; content-visibility (optional) is Chrome 85+, Safari 18+, Firefox 125+. The floor is Tailwind v4's generated sheet.

Techniques used in this demo

HTML & JSno Tailwind involved

Search CodeFronts

Loading…