Feedback

Empty Folder

An empty folder that is itself the drop zone: the dashed border and the tray arrow promise dragging before any text does.

  • empty
  • upload
  • dropzone
  • files

Preview

1440pxHost theme

Use it with AI

  1. 1. Copy the link.
  2. 2. Write to your agent in your own words and drop the link into the sentence.
  3. 3. The agent opens the link and installs the component from the registry.

put this in the header: https://vibeui.ru/c/empty-004?lang=en

This folder is empty

Перетащите файлы прямо сюда или выберите их на диске — они появятся в этой папке.

PDF, PNG, JPG, ZIP — до 50 МБ

Show the full instructions

What the link says, spelled out. Use it when your agent cannot open links — paste this text instead.

# Install and place "empty-004" (Empty Folder) from VibeUI

## 1. Install first — do not skip, do not recreate
Run this exact command before writing any code:
npx shadcn@latest add https://vibeui.ru/r/empty-004.json

Registry item: https://vibeui.ru/r/empty-004.json
Installs to: components/vibeui/empty-004.tsx (the exact path follows this project's components.json aliases).
npm dependencies: none.
Registry dependencies: none.

Install it from the registry. Do not recreate it from the description,
do not substitute a similar component from another library, and do not
rewrite it to match the project's existing style.

## 2. What it is
An empty folder that is itself the drop zone: the dashed border and the tray arrow promise dragging before any text does.

An empty folder as an upload area: a dashed border, an arrow into a tray, an explanation, one file-picking button and a line of accepted formats. Zero dependencies, one file, its own palette.

## 3. How to use it
import { Empty004 } from "@/components/vibeui/empty-004"

<Empty004 title="This folder is empty" actionLabel="Choose files" onAction={pickFiles} />

Read the installed file for the full prop list.

## 4. Where to place it
This is a small inline component. Put it exactly where the user asked,
inside the existing markup. Do not create a new page, section or
wrapper for it. If a similar control already sits in that spot,
replace it instead of adding a second one.

Placement: ___
(The user fills this line in. If it is still blank, ask where to put it
instead of guessing.)

## 5. Keep exactly as installed
- the local --vibeui-empty-004-* palette — do not swap it for your theme tokens
- the dashed border: it is the promise that a file can be dropped here
- the CSS arrow with aria-hidden — it is decorative and must not be announced
- the single button: dragging and picking from disk are two doors into one action, not two actions
- the formats and size limit line: a rejection after uploading annoys more than a rule before it
- the light card background — on a dark surface the card has to carry its own

## 6. You may change
- title and text — the heading and the explanation
- actionLabel — the wording of the file-picking button
- formats — the accepted formats and size line
- the onAction handler — opening the system file dialog
- the accent through the accent prop — it colours the arrow, the glow and the button

## 7. Rules
- Dragging is not implemented: onDragOver and onDrop belong to the calling code.
- Do not show this state while the list is loading: an empty folder and "not loaded yet" are different things.
- When uploading is forbidden by permissions, show a refusal screen with an explanation instead.
- Do not lift the CSS variables into globals.css: the component has to stay a single file.

## 8. Verify
- it renders with no console errors;
- it looks like the preview on the VibeUI page you copied this from;
- if it does not, you changed something listed in section 5 — put it back.

For developers

npx shadcn@latest add https://vibeui.ru/r/empty-004.json
https://vibeui.ru/r/empty-004.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-empty-004-*. Серверный: хуков нет. Пунктирная рамка лежит на самом блоке, стрелка нарисована псевдоэлементами и вложенным i, подсветка сверху — радиальный градиент от акцента. Обработчик перетаскивания не входит в компонент: он остаётся за вызывающим кодом, а кнопка даёт тот же путь для тех, кто не тащит файлы мышью.

Component source

The same file your agent installs. Here in case you would rather copy it by hand.

import type { ComponentPropsWithoutRef, CSSProperties } from "react"

export type Empty004Props = Omit<
  ComponentPropsWithoutRef<"div">,
  "children" | "title"
> & {
  title?: string
  text?: string
  actionLabel?: string
  formats?: string
  onAction?: () => void
  accent?: string
}

// Идея компонента: пустая папка, которая сама и есть область загрузки.
// Пунктирная рамка и стрелка в лоток говорят «сюда можно бросить файл»
// раньше любого текста, а кнопка остаётся для тех, кто перетаскивать не
// станет: у одного действия должно быть два входа, но одна цель.
const STYLES = `
:where([data-vibeui-block="empty-004"]){
--vibeui-empty-004-bg:oklch(1 0 0);
--vibeui-empty-004-fg:oklch(0.21 0.014 265);
--vibeui-empty-004-muted:oklch(0.55 0.014 265);
--vibeui-empty-004-border:oklch(0.88 0.008 265);
--vibeui-empty-004-accent:oklch(0.55 0.17 265);
--vibeui-empty-004-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
/* Пунктирная рамка — обещание перетаскивания, а не украшение карточки. */
[data-vibeui-block="empty-004"]{
display:flex;flex-direction:column;align-items:center;gap:0.5rem;
width:100%;max-width:24rem;box-sizing:border-box;padding:1.75rem 1.25rem;
text-align:center;
background:
radial-gradient(90% 70% at 50% 0%,color-mix(in oklab,var(--vibeui-empty-004-accent) 7%,transparent),transparent 70%),
var(--vibeui-empty-004-bg);
border:1.5px dashed var(--vibeui-empty-004-border);border-radius:1rem;
font-family:var(--vibeui-empty-004-font);color:var(--vibeui-empty-004-fg);
}
/* Стрелка в лоток: линия, наконечник и дно нарисованы бордюрами. */
[data-vibeui-block="empty-004"] [data-part="mark"]{
position:relative;width:2.75rem;height:2.5rem;margin-bottom:0.25rem;
color:var(--vibeui-empty-004-accent);
}
[data-vibeui-block="empty-004"] [data-part="mark"]::before{
content:"";position:absolute;left:50%;top:0.125rem;width:2px;height:1.25rem;
margin-left:-1px;border-radius:9999px;background:currentColor;
}
[data-vibeui-block="empty-004"] [data-part="mark"] i{
position:absolute;left:50%;top:0.25rem;width:0.6875rem;height:0.6875rem;
margin-left:-0.34375rem;
border:solid currentColor;border-width:2px 0 0 2px;transform:rotate(45deg);border-radius:1px 0 0 0;
}
[data-vibeui-block="empty-004"] [data-part="mark"]::after{
content:"";position:absolute;left:0;bottom:0.125rem;width:100%;height:0.9375rem;
border:2px solid currentColor;border-top:0;border-radius:0 0 0.5rem 0.5rem;opacity:.45;
}
[data-vibeui-block="empty-004"] [data-part="title"]{margin:0;font-size:1rem;font-weight:680;line-height:1.3}
[data-vibeui-block="empty-004"] [data-part="text"]{
margin:0;max-width:32ch;font-size:0.8125rem;line-height:1.5;color:var(--vibeui-empty-004-muted);
}
[data-vibeui-block="empty-004"] [data-part="action"]{
appearance:none;border:0;cursor:pointer;margin-top:0.5rem;
height:2.5rem;padding:0 1.125rem;border-radius:0.75rem;
background:var(--vibeui-empty-004-accent);color:oklch(0.99 0.01 265);
font:inherit;font-size:0.875rem;font-weight:650;
}
[data-vibeui-block="empty-004"] [data-part="action"]:focus-visible{outline:2px solid var(--vibeui-empty-004-accent);outline-offset:2px}
[data-vibeui-block="empty-004"] [data-part="formats"]{
margin:0.375rem 0 0;font-size:0.6875rem;color:var(--vibeui-empty-004-muted);
}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="empty-004"] *{animation:none!important;transition:none!important}}
`

/**
 * Пустая папка как область загрузки: рамка, стрелка и одно действие.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Empty004({
  title = "В папке пока пусто",
  text = "Перетащите файлы прямо сюда или выберите их на диске — они появятся в этой папке.",
  actionLabel = "Выбрать файлы",
  formats = "PDF, PNG, JPG, ZIP — до 50 МБ",
  onAction,
  accent,
  className,
  style,
  ...props
}: Empty004Props) {
  const palette = {
    ...(accent ? { "--vibeui-empty-004-accent": accent } : null),
    ...style,
  } as CSSProperties

  return (
    <>
      <style href="vibeui-empty-004" precedence="medium">
        {STYLES}
      </style>
      <div
        {...props}
        data-vibeui-block="empty-004"
        className={className}
        style={palette}
      >
        <span data-part="mark" aria-hidden="true">
          <i />
        </span>
        <h3 data-part="title">{title}</h3>
        <p data-part="text">{text}</p>
        <button type="button" data-part="action" onClick={onAction}>
          {actionLabel}
        </button>
        <p data-part="formats">{formats}</p>
      </div>
    </>
  )
}