Pure TailwindMIT licensed

Tailwind File Drop Zone

Drag-and-drop upload with a dashed boundary and zero JavaScript: a real file input stretched invisibly over the whole zone, so the browser handles the drop, the picker and keyboard focus. A 0 KB replacement for Dropzone.js in the common single-file case.

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

<div class="tif-15 grid min-h-svh w-full place-items-center bg-tif15-bg px-6 py-14 font-tif15-sans" aria-label="File drop zone demo">
  <label class="relative block w-full max-w-80 cursor-pointer rounded-xl border-[1.5px] border-dashed border-tif15-acc/50 bg-tif15-acc/5 px-4.5 py-5.5 text-center transition-[border-color,background-color] duration-200 hover:border-tif15-acc2 hover:bg-tif15-acc/10 focus-within:border-tif15-acc2 focus-within:bg-tif15-acc/12 focus-within:ring-3 focus-within:ring-tif15-acc2/18">
    <input type="file" name="tif15-upload" accept="image/*,.pdf" class="absolute inset-0 size-full cursor-pointer opacity-0">
    <span class="mx-auto mb-2 grid size-10 place-items-center rounded-[10px] bg-tif15-acc/18 text-tif15-acc3" aria-hidden="true">
      <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
    </span>
    <span class="block text-[13px] text-tif15-ink"><strong class="font-semibold text-tif15-acc3">Click to upload</strong> or drag &amp; drop
      <span class="mt-1 block text-[11px] text-tif15-mut">PNG, JPG, or PDF · max 10 MB</span>
    </span>
  </label>
</div>
@import "tailwindcss";

@theme {
  --color-tif15-bg: #12121a;
  --color-tif15-bg: oklch(0.19 0.015 285);
  --color-tif15-ink: #c8c0ff;
  --color-tif15-ink: oklch(0.83 0.07 295);
  --color-tif15-mut: #b8b6d4;
  --color-tif15-mut: oklch(0.77 0.03 290);
  --color-tif15-acc: #6366f1;
  --color-tif15-acc: oklch(0.58 0.21 277);
  --color-tif15-acc2: #818cf8;
  --color-tif15-acc2: oklch(0.68 0.16 277);
  --color-tif15-acc3: #a5b4fc;
  --color-tif15-acc3: oklch(0.78 0.11 277);
  --font-tif15-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

.tif-15 {
  width: 100%;
}

.tif-15 ::selection {
  background: var(--color-tif15-acc2);
  color: #12121a;
}

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-tif15-bg: #12121a;
  --color-tif15-bg: oklch(0.19 0.015 285);
  --color-tif15-ink: #c8c0ff;
  --color-tif15-ink: oklch(0.83 0.07 295);
  --color-tif15-mut: #b8b6d4;
  --color-tif15-mut: oklch(0.77 0.03 290);
  --color-tif15-acc: #6366f1;
  --color-tif15-acc: oklch(0.58 0.21 277);
  --color-tif15-acc2: #818cf8;
  --color-tif15-acc2: oklch(0.68 0.16 277);
  --color-tif15-acc3: #a5b4fc;
  --color-tif15-acc3: oklch(0.78 0.11 277);
  --font-tif15-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

.tif-15 {
  width: 100%;
}

.tif-15 ::selection {
  background: var(--color-tif15-acc2);
  color: #12121a;
}
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=Plus+Jakarta+Sans:wght@400;500;600&display=swap">

What changed in the port

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

  • The hover and focus-within blocks become two variant groups on the label, so the keyboard path is visibly equal to the mouse path rather than an afterthought at the bottom of a stylesheet.
  • A focus-within:ring-3 was added: the original relied on a border tint alone, which is a weak focus indicator on a large target.
  • 1.5px is an arbitrary border width — a legitimate arbitrary value, since Tailwind's border scale is integers.
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 File Drop Zone
Source: https://codefronts.com/components/tailwind-css-input-fields/tailwind-file-drop-zone/

Drag-and-drop upload with a dashed boundary and zero JavaScript: a real file input stretched invisibly over the whole zone, so the browser handles the drop, the picker and keyboard focus. A 0 KB replacement for Dropzone.js in the common single-file case.
## HTML
```html
<div class="tif-15 grid min-h-svh w-full place-items-center bg-tif15-bg px-6 py-14 font-tif15-sans" aria-label="File drop zone demo">
  <label class="relative block w-full max-w-80 cursor-pointer rounded-xl border-[1.5px] border-dashed border-tif15-acc/50 bg-tif15-acc/5 px-4.5 py-5.5 text-center transition-[border-color,background-color] duration-200 hover:border-tif15-acc2 hover:bg-tif15-acc/10 focus-within:border-tif15-acc2 focus-within:bg-tif15-acc/12 focus-within:ring-3 focus-within:ring-tif15-acc2/18">
    <input type="file" name="tif15-upload" accept="image/*,.pdf" class="absolute inset-0 size-full cursor-pointer opacity-0">
    <span class="mx-auto mb-2 grid size-10 place-items-center rounded-[10px] bg-tif15-acc/18 text-tif15-acc3" aria-hidden="true">
      <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
    </span>
    <span class="block text-[13px] text-tif15-ink"><strong class="font-semibold text-tif15-acc3">Click to upload</strong> or drag &amp; drop
      <span class="mt-1 block text-[11px] text-tif15-mut">PNG, JPG, or PDF · max 10 MB</span>
    </span>
  </label>
</div>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tif15-bg: #12121a;
  --color-tif15-bg: oklch(0.19 0.015 285);
  --color-tif15-ink: #c8c0ff;
  --color-tif15-ink: oklch(0.83 0.07 295);
  --color-tif15-mut: #b8b6d4;
  --color-tif15-mut: oklch(0.77 0.03 290);
  --color-tif15-acc: #6366f1;
  --color-tif15-acc: oklch(0.58 0.21 277);
  --color-tif15-acc2: #818cf8;
  --color-tif15-acc2: oklch(0.68 0.16 277);
  --color-tif15-acc3: #a5b4fc;
  --color-tif15-acc3: oklch(0.78 0.11 277);
  --font-tif15-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

.tif-15 {
  width: 100%;
}

.tif-15 ::selection {
  background: var(--color-tif15-acc2);
  color: #12121a;
}
```

How this works

The input is the drop zone. Stretched to the label's full box at zero opacity, it receives every drag, drop and click the user aims at the pretty thing underneath:

class="absolute inset-0 size-full cursor-pointer opacity-0"

Invisible, not hidden. display:none or hidden would remove it from the hit-testing and accessibility trees, taking drag events and focus with it. opacity-0 keeps both.

Make it yours

  • Types: accept=&quot;.csv,.xlsx&quot; or whatever your endpoint takes.
  • Multiple files: add multiple; nothing in the styling changes.
  • Boundary: border-dashed, rounded-xl, p-5 and the accent token.
  • Copy: the strong line and the type hint are ordinary markup — rewrite freely.

Gotchas — read before shipping

  • Never swap opacity-0 for hidden. Drag events and the keyboard both stop working and nothing tells you why.
  • Pure CSS cannot echo the chosen filename. Three lines on change can, and most products need it.
  • The &quot;max 10 MB&quot; line is copy. Enforce the limit on change and again on the server.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Native file input drag-and-drop works everywhere; no library, no polyfill.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Theme tokensset in @theme, used by a utility

Search CodeFronts

Loading…