Skip to content

File upload

Drop zone + file list.

Browsers
Chrome 123+Firefox 121+Safari 17.5+
Tier
1
Bundle compressed
2.27 kB
Forced layout budget
0 ms
Axe budget
0
SSR compatible
Yes
Published contract
Test harness

Playground

File upload

Attachments

Drop files here, or choose files above.

    Up to 3 files · 5 MB each

    "use client";
    import { FileUpload } from "@gunkin/uiify";
    
    export default function Example() {
      return (
        <FileUpload.Root multiple maxFiles={3} maxSize={5000000} className="">
          <FileUpload.HiddenInput name="attachments" aria-label="Attachments" />
          <FileUpload.Trigger as="button" className="">
            Choose files
          </FileUpload.Trigger>
          <FileUpload.Dropzone className="">Drop files here</FileUpload.Dropzone>
          <FileUpload.List className="">
            {(files) =>
              files.map((file, index) => (
                <FileUpload.Item key={file.name} index={index} className="">
                  <span>
                    {file.name} · {(file.size / 1024).toFixed(1)} KB
                  </span>
                  <FileUpload.ItemRemove index={index} aria-label={"Remove " + file.name}>
                    ×
                  </FileUpload.ItemRemove>
                </FileUpload.Item>
              ))
            }
          </FileUpload.List>
        </FileUpload.Root>
      );
    }
    /* File upload — trigger button with dashed dropzone. */
    
    @layer components {
      [data-uiify-file-upload] {
        display: grid;
        gap: var(--space-md);
        inline-size: 100%;
        max-inline-size: 24rem;
      }
    
      [data-uiify-file-upload] [data-part="dropzone"] {
        display: flex;
        align-items: center;
        justify-content: center;
        min-block-size: 8rem;
        padding: var(--space-lg);
        border: 2px dashed var(--input);
        border-radius: var(--radius-xl);
        corner-shape: squircle;
        background-color: var(--secondary);
        color: var(--muted-foreground);
        font-size: var(--text-sm);
        text-align: center;
        transition: var(--transition-colors);
      }
    
      [data-uiify-file-upload] [data-part="dropzone"]:hover,
      [data-uiify-file-upload] [data-part="dropzone"][data-dragging] {
        border-color: var(--primary);
        background-color: color-mix(in oklch, var(--primary) 8%, transparent);
        color: var(--foreground);
      }
    
      [data-uiify-file-upload] [data-part="dropzone"][data-disabled] {
        opacity: 0.5;
        cursor: not-allowed;
      }
    
      [data-uiify-file-upload] [data-part="list"] {
        display: grid;
        gap: var(--space-xs);
        margin: 0;
        padding: 0;
        list-style: none;
      }
    
      [data-uiify-file-upload] [data-part="item"] {
        display: flex;
        align-items: center;
        gap: var(--space-sm);
        padding: var(--space-sm) var(--space-md);
        border: var(--border-width) solid var(--border);
        border-radius: var(--radius-lg);
        corner-shape: squircle;
        background-color: var(--card);
        font-size: var(--text-sm);
      }
    }
    
    @layer components {
      [data-uiify-file-upload] [data-part="file-info"] {
        display: grid;
        gap: 0.25rem;
        min-inline-size: 0;
        flex: 1;
        overflow-wrap: anywhere;
      }
      [data-uiify-file-upload] small {
        color: var(--muted-foreground);
      }
      [data-uiify-file-upload] [data-part="remove"] {
        display: grid;
        place-items: center;
        flex: none;
        inline-size: 2rem;
        block-size: 2rem;
        margin-inline-start: auto;
        border: 0;
        border-radius: 50%;
        background: var(--secondary);
        color: var(--foreground);
        font-size: 1.25rem;
        line-height: 1;
        cursor: pointer;
      }
      [data-uiify-file-upload] [data-part="remove"]:hover {
        background: var(--accent);
      }
      [data-uiify-file-upload] [data-part="remove"]:focus-visible {
        outline: 2px solid var(--ring);
        outline-offset: 2px;
      }
    }
    "use client";
    import { FileUpload } from "@gunkin/uiify";
    
    export default function Example() {
      return (
        <FileUpload.Root
          multiple
          maxFiles={3}
          maxSize={5000000}
          className="
            grid gap-md [inline-size:100%] [max-inline-size:24rem] [&_[data-part=dropzone]]:flex
            [&_[data-part=dropzone]]:items-center [&_[data-part=dropzone]]:justify-center
            [&_[data-part=dropzone]]:[min-block-size:8rem] [&_[data-part=dropzone]]:p-lg
            [&_[data-part=dropzone]]:[border-width:2px] [&_[data-part=dropzone]]:[border-style:dashed]
            [&_[data-part=dropzone]]:border-input [&_[data-part=dropzone]]:rounded-xl
            [&_[data-part=dropzone]]:[corner-shape:squircle] [&_[data-part=dropzone]]:bg-secondary
            [&_[data-part=dropzone]]:text-muted-foreground [&_[data-part=dropzone]]:text-sm
            [&_[data-part=dropzone]]:text-center [&_[data-part=dropzone]]:[transition:var(--transition-colors)]
            [&_[data-part=dropzone]:hover]:border-primary
            [&_[data-part=dropzone]:hover]:[background-color:color-mix(in_oklch,_var(--primary)_8%,_transparent)]
            [&_[data-part=dropzone]:hover]:text-foreground [&_[data-part=dropzone][data-dragging]]:border-primary
            [&_[data-part=dropzone][data-dragging]]:[background-color:color-mix(in_oklch,_var(--primary)_8%,_transparent)]
            [&_[data-part=dropzone][data-dragging]]:text-foreground [&_[data-part=dropzone][data-disabled]]:opacity-50
            [&_[data-part=dropzone][data-disabled]]:cursor-not-allowed [&_[data-part=list]]:grid [&_[data-part=list]]:gap-xs
            [&_[data-part=list]]:m-0 [&_[data-part=list]]:p-0 [&_[data-part=list]]:list-none [&_[data-part=item]]:flex
            [&_[data-part=item]]:items-center [&_[data-part=item]]:gap-sm
            [&_[data-part=item]]:[padding:var(--space-sm)_var(--space-md)]
            [&_[data-part=item]]:border-[length:var(--border-width)] [&_[data-part=item]]:border-solid
            [&_[data-part=item]]:border-border [&_[data-part=item]]:rounded-lg [&_[data-part=item]]:[corner-shape:squircle]
            [&_[data-part=item]]:bg-card [&_[data-part=item]]:text-sm [&_[data-part=file-info]]:grid
            [&_[data-part=file-info]]:[gap:0.25rem] [&_[data-part=file-info]]:[min-inline-size:0]
            [&_[data-part=file-info]]:[flex:1] [&_[data-part=file-info]]:[overflow-wrap:anywhere]
            [&_small]:text-muted-foreground [&_[data-part=remove]]:grid [&_[data-part=remove]]:place-items-center
            [&_[data-part=remove]]:flex-none [&_[data-part=remove]]:[inline-size:2rem]
            [&_[data-part=remove]]:[block-size:2rem] [&_[data-part=remove]]:[margin-inline-start:auto]
            [&_[data-part=remove]]:border-0 [&_[data-part=remove]]:border-current [&_[data-part=remove]]:border-none
            [&_[data-part=remove]]:rounded-[50%] [&_[data-part=remove]]:bg-secondary [&_[data-part=remove]]:text-foreground
            [&_[data-part=remove]]:[font-size:1.25rem] [&_[data-part=remove]]:leading-none
            [&_[data-part=remove]]:cursor-pointer [&_[data-part=remove]:hover]:bg-accent
            [&_[data-part=remove]:focus-visible]:[outline:2px_solid_var(--ring)]
            [&_[data-part=remove]:focus-visible]:[outline-offset:2px]
          "
        >
          <FileUpload.HiddenInput name="attachments" aria-label="Attachments" />
          <FileUpload.Trigger
            as="button"
            className={
              "focus-visible:[outline:none] focus-visible:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--" +
              "ring)_16%,_transparent)] [--uiify-button-rim:rgb(255_255_255_/_20%)] [--uiify-button-wash:light-dark(rgb(" +
              "255_255_255_/_15%),_rgb(255_255_255_/_30%))] relative isolate inline-flex items-center justify-center [ga" +
              "p:0.375rem] [min-block-size:44px] [min-inline-size:44px] py-0 [padding-inline:1.25rem] border-[length:var" +
              "(--border-width)] border-solid border-transparent rounded-[var(--radius-full)] bg-foreground bg-clip-padd" +
              "ing text-background text-base font-[number:var(--weight-medium)] leading-6 no-underline cursor-pointer [t" +
              "ransition:var(--transition-colors)] before:content-[''] before:absolute before:inset-0 before:z-[-1] befo" +
              "re:[border-radius:inherit] before:pointer-events-none after:content-[''] after:absolute after:inset-0 aft" +
              "er:z-[-1] after:[border-radius:inherit] after:pointer-events-none before:[background-color:var(--uiify-bu" +
              "tton-wash)] before:[filter:blur(10px)] before:[translate:0_-50%] before:[clip-path:inset(50%_0_-50%_0_rou" +
              "nd_var(--radius-full))] after:[padding:0.5px] after:[background-image:linear-gradient(to_bottom,_var(--ui" +
              "ify-button-rim),_transparent_75%)] after:[mask:linear-gradient(#fff_0_0)_content-box_exclude,_linear-grad" +
              "ient(#fff_0_0)] [&:is([data-size=sm],[data-size=icon-sm])]:[min-block-size:32px] [&:is([data-size=sm],[da" +
              "ta-size=icon-sm])]:[min-inline-size:32px] [&:is([data-size=sm],[data-size=icon-sm])]:[padding-inline:0.75" +
              "rem] [&:is([data-size=sm],[data-size=icon-sm])]:[gap:0.25rem] [&:is([data-size=sm],[data-size=icon-sm])]:" +
              "text-sm [&:is([data-size=sm],[data-size=icon-sm])]:leading-5 [&:is([data-size=lg],[data-size=icon-lg])]:[" +
              "min-block-size:56px] [&:is([data-size=lg],[data-size=icon-lg])]:[min-inline-size:56px] [&:is([data-size=l" +
              "g],[data-size=icon-lg])]:[padding-inline:1.75rem] [&:is([data-size=lg],[data-size=icon-lg])]:text-lg [&:i" +
              "s([data-size=lg],[data-size=icon-lg])]:leading-7 [&[data-size^=icon]]:px-0 [@media_(any-hover:_hover)_and" +
              "_(any-pointer:_fine)]:hover:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(" +
              "--foreground)_90%,_var(--background))] active:[&:not(:disabled,[data-disabled])]:[background-color:color-" +
              "mix(in_oklch,_var(--foreground)_80%,_var(--background))] disabled:opacity-50 disabled:cursor-not-allowed " +
              "data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed data-[variant=secondary]:[&:where(:not(:che" +
              "cked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:[--uiify-button-rim:light-dark(rgb(0_0_" +
              "0_/_5%),_rgb(255_255_255_/_10%))] data-[variant=secondary]:[&:where(:not(:checked+*,[data-selected],[aria" +
              "-pressed=true],[aria-current=page]))]:[--uiify-button-wash:light-dark(rgb(255_255_255_/_20%),_rgb(255_255" +
              "_255_/_5%))] data-[variant=secondary]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-" +
              "current=page]))]:[background-color:color-mix(in_oklch,_var(--foreground)_10%,_transparent)] data-[variant" +
              "=secondary]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:text-fore" +
              "ground data-[variant=outline]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=" +
              "page]))]:[--uiify-button-rim:color-mix(in_oklch,_var(--foreground)_25%,_transparent)] data-[variant=outli" +
              "ne]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:[border-color:col" +
              "or-mix(in_oklch,_var(--foreground)_20%,_transparent)] data-[variant=outline]:[&:where(:not(:checked+*,[da" +
              "ta-selected],[aria-pressed=true],[aria-current=page]))]:bg-transparent data-[variant=outline]:[&:where(:n" +
              "ot(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:text-foreground data-[variant=ou" +
              "tline]:before:content-none data-[variant=outline]:after:[inset:calc(-1_*_var(--border-width))] data-[vari" +
              "ant=outline]:after:[padding:var(--border-width)] data-[variant=outline]:[&:where(:not(:checked+*,[data-se" +
              "lected],[aria-pressed=true],[aria-current=page]))]:active:[&:not(:disabled,[data-disabled])]:[background-" +
              "color:color-mix(in_oklch,_var(--foreground)_16%,_transparent)] data-[variant=ghost]:[&:where(:not(:checke" +
              "d+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:bg-transparent data-[variant=ghost]:[&:whe" +
              "re(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:text-foreground data-[varia" +
              "nt=ghost]:before:content-none data-[variant=ghost]:after:content-none data-[variant=ghost]:[&:where(:not(" +
              ":checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:active:[&:not(:disabled,[data-disab" +
              "led])]:[background-color:color-mix(in_oklch,_var(--foreground)_16%,_transparent)] data-[variant=secondary" +
              "]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:active:[&:not(:disa" +
              "bled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--foreground)_26%,_transparent)] data-[v" +
              "ariant=destructive]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:[" +
              "--uiify-button-wash:rgb(255_255_255_/_15%)] data-[variant=destructive]:[&:where(:not(:checked+*,[data-sel" +
              "ected],[aria-pressed=true],[aria-current=page]))]:bg-destructive data-[variant=destructive]:text-destruct" +
              "ive-foreground data-[variant=destructive]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[a" +
              "ria-current=page]))]:active:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(" +
              "--destructive),_black_20%)] [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=outline]:[&" +
              ":where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:hover:[&:not(:disabled," +
              "[data-disabled])]:[background-color:color-mix(in_oklch,_var(--foreground)_10%,_transparent)] [@media_(any" +
              "-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=ghost]:[&:where(:not(:checked+*,[data-selected],[ar" +
              "ia-pressed=true],[aria-current=page]))]:hover:[&:not(:disabled,[data-disabled])]:[background-color:color-" +
              "mix(in_oklch,_var(--foreground)_10%,_transparent)] [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:da" +
              "ta-[variant=secondary]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))" +
              "]:hover:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--foreground)_20%,_t" +
              "ransparent)] [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=destructive]:[&:where(:not" +
              "(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:hover:[&:not(:disabled,[data-disab" +
              "led])]:[background-color:color-mix(in_oklch,_var(--destructive),_black_12%)] [&:is([data-selected],[aria-" +
              "pressed=true],[aria-current=page])]:[--uiify-button-rim:rgb(255_255_255_/_20%)] [&:is([data-selected],[ar" +
              "ia-pressed=true],[aria-current=page])]:[--uiify-button-wash:rgb(255_255_255_/_15%)] [&:is([data-selected]" +
              ",[aria-pressed=true],[aria-current=page])]:border-transparent [&:is([data-selected],[aria-pressed=true],[" +
              "aria-current=page])]:bg-primary [&:is([data-selected],[aria-pressed=true],[aria-current=page])]:text-prim" +
              "ary-foreground [:checked+&]:[--uiify-button-rim:rgb(255_255_255_/_20%)] [:checked+&]:[--uiify-button-wash" +
              ":rgb(255_255_255_/_15%)] [:checked+&]:border-transparent [:checked+&]:bg-primary [:checked+&]:text-primar" +
              "y-foreground [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:[&:is([data-selected],[aria-pressed=true" +
              "],[aria-current=page]):hover:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--" +
              "primary)_88%,_var(--primary-foreground))] [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:[:checked:n" +
              "ot(:disabled)+&:hover]:[background-color:color-mix(in_oklch,_var(--primary)_88%,_var(--primary-foreground" +
              "))] [:disabled+&]:opacity-50 [:disabled+&]:cursor-not-allowed [&[hidden]]:hidden data-[variant=link]:px-0" +
              " data-[variant=link]:border-0 data-[variant=link]:border-current data-[variant=link]:border-none data-[va" +
              "riant=link]:rounded-none data-[variant=link]:bg-transparent data-[variant=link]:text-primary data-[varian" +
              "t=link]:underline data-[variant=link]:[text-underline-offset:0.2em] data-[variant=link]:active:px-0 data-" +
              "[variant=link]:active:border-0 data-[variant=link]:active:border-current data-[variant=link]:active:borde" +
              "r-none data-[variant=link]:active:rounded-none data-[variant=link]:active:bg-transparent data-[variant=li" +
              "nk]:active:text-primary data-[variant=link]:active:underline data-[variant=link]:active:[text-underline-o" +
              "ffset:0.2em] data-[variant=link]:[min-inline-size:0] [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:" +
              "data-[variant=link]:hover:px-0 [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:ho" +
              "ver:border-0 [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:hover:border-current" +
              " [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:hover:border-none [@media_(any-h" +
              "over:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:hover:rounded-none [@media_(any-hover:_hover)_a" +
              "nd_(any-pointer:_fine)]:data-[variant=link]:hover:bg-transparent [@media_(any-hover:_hover)_and_(any-poin" +
              "ter:_fine)]:data-[variant=link]:hover:text-primary [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:da" +
              "ta-[variant=link]:hover:underline [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]" +
              ":hover:[text-underline-offset:0.2em] data-[variant=link]:before:content-none data-[variant=link]:after:co" +
              "ntent-none data-[variant=link]:focus-visible:[outline:2px_solid_var(--ring)] data-[variant=link]:focus-vi" +
              "sible:[outline-offset:3px] [[data-uiify-toggle]>input:focus-visible+&]:[box-shadow:0_0_0_var(--ring-width" +
              ")_color-mix(in_oklch,_var(--ring)_16%,_transparent)] [[data-uiify-toggle-group]_[data-part=item]>input:fo" +
              "cus-visible+&]:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%,_transparent)] [[d" +
              "ata-uiify-modal][data-part=close]&]:border-transparent [[data-uiify-modal][data-part=close]&]:[background" +
              "-color:color-mix(in_oklch,_var(--foreground)_8%,_transparent)] [[data-uiify-modal][data-part=close]&]:tex" +
              "t-foreground [[data-uiify-modal][data-part=close]&:hover]:[background-color:color-mix(in_oklch,_var(--for" +
              "eground)_14%,_transparent)] [[data-uiify-modal][data-part=close]&:active]:[background-color:color-mix(in_" +
              "oklch,_var(--foreground)_20%,_transparent)] [[data-uiify-pagination]_span&:not([data-disabled])]:cursor-d" +
              "efault [[data-uiify-input-group]>[data-part=addon]>&]:[min-inline-size:0] [[data-uiify-input-group]>[data" +
              "-part=addon]>&]:[max-inline-size:100%] [[data-uiify-input-group]>[data-part=addon]>&]:whitespace-normal [" +
              "[data-uiify-input-group]>[data-part=addon]>&]:[overflow-wrap:anywhere] [[data-uiify-item-content]>&]:[jus" +
              "tify-self:start]"
            }
          >
            Choose files
          </FileUpload.Trigger>
          <FileUpload.Dropzone className="">Drop files here</FileUpload.Dropzone>
          <FileUpload.List className="">
            {(files) =>
              files.map((file, index) => (
                <FileUpload.Item key={file.name} index={index} className="">
                  <span>
                    {file.name} · {(file.size / 1024).toFixed(1)} KB
                  </span>
                  <FileUpload.ItemRemove index={index} aria-label={"Remove " + file.name}>
                    ×
                  </FileUpload.ItemRemove>
                </FileUpload.Item>
              ))
            }
          </FileUpload.List>
        </FileUpload.Root>
      );
    }
    /* app.css */
    
    @layer reset, ui, app, tokens, base, demo, components, utilities, theme;
    
    @import "@gunkin/uiify/styles/reset";
    
    @import "@gunkin/uiify/components/behavior.css";
    
    @import "@gunkin/uiify/styles/tokens";
    
    @import "@gunkin/uiify/styles/tailwind";
    
    @import "tailwindcss/utilities.css";
    
    @theme { --spacing: 0.25rem; --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; }
    
    @source inline('grid gap-md [inline-size:100%] [max-inline-size:24rem] [&_[data-part=dropzone]]:flex [&_[data-part=dropzone]]:items-center [&_[data-part=dropzone]]:justify-center [&_[data-part=dropzone]]:[min-block-size:8rem] [&_[data-part=dropzone]]:p-lg [&_[data-part=dropzone]]:[border-width:2px] [&_[data-part=dropzone]]:[border-style:dashed] [&_[data-part=dropzone]]:border-input [&_[data-part=dropzone]]:rounded-xl [&_[data-part=dropzone]]:[corner-shape:squircle] [&_[data-part=dropzone]]:bg-secondary [&_[data-part=dropzone]]:text-muted-foreground [&_[data-part=dropzone]]:text-sm [&_[data-part=dropzone]]:text-center [&_[data-part=dropzone]]:[transition:var(--transition-colors)] [&_[data-part=dropzone]:hover]:border-primary [&_[data-part=dropzone]:hover]:[background-color:color-mix(in_oklch,_var(--primary)_8%,_transparent)] [&_[data-part=dropzone]:hover]:text-foreground [&_[data-part=dropzone][data-dragging]]:border-primary [&_[data-part=dropzone][data-dragging]]:[background-color:color-mix(in_oklch,_var(--primary)_8%,_transparent)] [&_[data-part=dropzone][data-dragging]]:text-foreground [&_[data-part=dropzone][data-disabled]]:opacity-50 [&_[data-part=dropzone][data-disabled]]:cursor-not-allowed [&_[data-part=list]]:grid [&_[data-part=list]]:gap-xs [&_[data-part=list]]:m-0 [&_[data-part=list]]:p-0 [&_[data-part=list]]:list-none [&_[data-part=item]]:flex [&_[data-part=item]]:items-center [&_[data-part=item]]:gap-sm [&_[data-part=item]]:[padding:var(--space-sm)_var(--space-md)] [&_[data-part=item]]:border-[length:var(--border-width)] [&_[data-part=item]]:border-solid [&_[data-part=item]]:border-border [&_[data-part=item]]:rounded-lg [&_[data-part=item]]:[corner-shape:squircle] [&_[data-part=item]]:bg-card [&_[data-part=item]]:text-sm [&_[data-part=file-info]]:grid [&_[data-part=file-info]]:[gap:0.25rem] [&_[data-part=file-info]]:[min-inline-size:0] [&_[data-part=file-info]]:[flex:1] [&_[data-part=file-info]]:[overflow-wrap:anywhere] [&_small]:text-muted-foreground [&_[data-part=remove]]:grid [&_[data-part=remove]]:place-items-center [&_[data-part=remove]]:flex-none [&_[data-part=remove]]:[inline-size:2rem] [&_[data-part=remove]]:[block-size:2rem] [&_[data-part=remove]]:[margin-inline-start:auto] [&_[data-part=remove]]:border-0 [&_[data-part=remove]]:border-current [&_[data-part=remove]]:border-none [&_[data-part=remove]]:rounded-[50%] [&_[data-part=remove]]:bg-secondary [&_[data-part=remove]]:text-foreground [&_[data-part=remove]]:[font-size:1.25rem] [&_[data-part=remove]]:leading-none [&_[data-part=remove]]:cursor-pointer [&_[data-part=remove]:hover]:bg-accent [&_[data-part=remove]:focus-visible]:[outline:2px_solid_var(--ring)] [&_[data-part=remove]:focus-visible]:[outline-offset:2px] focus-visible:[outline:none] focus-visible:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%,_transparent)] [--uiify-button-rim:rgb(255_255_255_/_20%)] [--uiify-button-wash:light-dark(rgb(255_255_255_/_15%),_rgb(255_255_255_/_30%))] relative isolate inline-flex items-center justify-center [gap:0.375rem] [min-block-size:44px] [min-inline-size:44px] py-0 [padding-inline:1.25rem] border-[length:var(--border-width)] border-solid border-transparent rounded-[var(--radius-full)] bg-foreground bg-clip-padding text-background text-base font-[number:var(--weight-medium)] leading-6 no-underline cursor-pointer [transition:var(--transition-colors)] before:absolute before:inset-0 before:z-[-1] before:[border-radius:inherit] before:pointer-events-none after:absolute after:inset-0 after:z-[-1] after:[border-radius:inherit] after:pointer-events-none before:[background-color:var(--uiify-button-wash)] before:[filter:blur(10px)] before:[translate:0_-50%] before:[clip-path:inset(50%_0_-50%_0_round_var(--radius-full))] after:[padding:0.5px] after:[background-image:linear-gradient(to_bottom,_var(--uiify-button-rim),_transparent_75%)] after:[mask:linear-gradient(#fff_0_0)_content-box_exclude,_linear-gradient(#fff_0_0)] [&:is([data-size=sm],[data-size=icon-sm])]:[min-block-size:32px] [&:is([data-size=sm],[data-size=icon-sm])]:[min-inline-size:32px] [&:is([data-size=sm],[data-size=icon-sm])]:[padding-inline:0.75rem] [&:is([data-size=sm],[data-size=icon-sm])]:[gap:0.25rem] [&:is([data-size=sm],[data-size=icon-sm])]:text-sm [&:is([data-size=sm],[data-size=icon-sm])]:leading-5 [&:is([data-size=lg],[data-size=icon-lg])]:[min-block-size:56px] [&:is([data-size=lg],[data-size=icon-lg])]:[min-inline-size:56px] [&:is([data-size=lg],[data-size=icon-lg])]:[padding-inline:1.75rem] [&:is([data-size=lg],[data-size=icon-lg])]:text-lg [&:is([data-size=lg],[data-size=icon-lg])]:leading-7 [&[data-size^=icon]]:px-0 [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:hover:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--foreground)_90%,_var(--background))] active:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--foreground)_80%,_var(--background))] disabled:opacity-50 disabled:cursor-not-allowed data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed data-[variant=secondary]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:[--uiify-button-rim:light-dark(rgb(0_0_0_/_5%),_rgb(255_255_255_/_10%))] data-[variant=secondary]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:[--uiify-button-wash:light-dark(rgb(255_255_255_/_20%),_rgb(255_255_255_/_5%))] data-[variant=secondary]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:[background-color:color-mix(in_oklch,_var(--foreground)_10%,_transparent)] data-[variant=secondary]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:text-foreground data-[variant=outline]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:[--uiify-button-rim:color-mix(in_oklch,_var(--foreground)_25%,_transparent)] data-[variant=outline]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:[border-color:color-mix(in_oklch,_var(--foreground)_20%,_transparent)] data-[variant=outline]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:bg-transparent data-[variant=outline]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:text-foreground data-[variant=outline]:before:content-none data-[variant=outline]:after:[inset:calc(-1_*_var(--border-width))] data-[variant=outline]:after:[padding:var(--border-width)] data-[variant=outline]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:active:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--foreground)_16%,_transparent)] data-[variant=ghost]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:bg-transparent data-[variant=ghost]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:text-foreground data-[variant=ghost]:before:content-none data-[variant=ghost]:after:content-none data-[variant=ghost]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:active:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--foreground)_16%,_transparent)] data-[variant=secondary]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:active:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--foreground)_26%,_transparent)] data-[variant=destructive]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:[--uiify-button-wash:rgb(255_255_255_/_15%)] data-[variant=destructive]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:bg-destructive data-[variant=destructive]:text-destructive-foreground data-[variant=destructive]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:active:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--destructive),_black_20%)] [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=outline]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:hover:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--foreground)_10%,_transparent)] [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=ghost]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:hover:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--foreground)_10%,_transparent)] [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=secondary]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:hover:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--foreground)_20%,_transparent)] [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=destructive]:[&:where(:not(:checked+*,[data-selected],[aria-pressed=true],[aria-current=page]))]:hover:[&:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--destructive),_black_12%)] [&:is([data-selected],[aria-pressed=true],[aria-current=page])]:[--uiify-button-rim:rgb(255_255_255_/_20%)] [&:is([data-selected],[aria-pressed=true],[aria-current=page])]:[--uiify-button-wash:rgb(255_255_255_/_15%)] [&:is([data-selected],[aria-pressed=true],[aria-current=page])]:border-transparent [&:is([data-selected],[aria-pressed=true],[aria-current=page])]:bg-primary [&:is([data-selected],[aria-pressed=true],[aria-current=page])]:text-primary-foreground [:checked+&]:[--uiify-button-rim:rgb(255_255_255_/_20%)] [:checked+&]:[--uiify-button-wash:rgb(255_255_255_/_15%)] [:checked+&]:border-transparent [:checked+&]:bg-primary [:checked+&]:text-primary-foreground [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:[&:is([data-selected],[aria-pressed=true],[aria-current=page]):hover:not(:disabled,[data-disabled])]:[background-color:color-mix(in_oklch,_var(--primary)_88%,_var(--primary-foreground))] [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:[:checked:not(:disabled)+&:hover]:[background-color:color-mix(in_oklch,_var(--primary)_88%,_var(--primary-foreground))] [:disabled+&]:opacity-50 [:disabled+&]:cursor-not-allowed [&[hidden]]:hidden data-[variant=link]:px-0 data-[variant=link]:border-0 data-[variant=link]:border-current data-[variant=link]:border-none data-[variant=link]:rounded-none data-[variant=link]:bg-transparent data-[variant=link]:text-primary data-[variant=link]:underline data-[variant=link]:[text-underline-offset:0.2em] data-[variant=link]:active:px-0 data-[variant=link]:active:border-0 data-[variant=link]:active:border-current data-[variant=link]:active:border-none data-[variant=link]:active:rounded-none data-[variant=link]:active:bg-transparent data-[variant=link]:active:text-primary data-[variant=link]:active:underline data-[variant=link]:active:[text-underline-offset:0.2em] data-[variant=link]:[min-inline-size:0] [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:hover:px-0 [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:hover:border-0 [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:hover:border-current [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:hover:border-none [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:hover:rounded-none [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:hover:bg-transparent [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:hover:text-primary [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:hover:underline [@media_(any-hover:_hover)_and_(any-pointer:_fine)]:data-[variant=link]:hover:[text-underline-offset:0.2em] data-[variant=link]:before:content-none data-[variant=link]:after:content-none data-[variant=link]:focus-visible:[outline:2px_solid_var(--ring)] data-[variant=link]:focus-visible:[outline-offset:3px] [[data-uiify-toggle]>input:focus-visible+&]:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%,_transparent)] [[data-uiify-toggle-group]_[data-part=item]>input:focus-visible+&]:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%,_transparent)] [[data-uiify-modal][data-part=close]&]:border-transparent [[data-uiify-modal][data-part=close]&]:[background-color:color-mix(in_oklch,_var(--foreground)_8%,_transparent)] [[data-uiify-modal][data-part=close]&]:text-foreground [[data-uiify-modal][data-part=close]&:hover]:[background-color:color-mix(in_oklch,_var(--foreground)_14%,_transparent)] [[data-uiify-modal][data-part=close]&:active]:[background-color:color-mix(in_oklch,_var(--foreground)_20%,_transparent)] [[data-uiify-pagination]_span&:not([data-disabled])]:cursor-default [[data-uiify-input-group]>[data-part=addon]>&]:[min-inline-size:0] [[data-uiify-input-group]>[data-part=addon]>&]:[max-inline-size:100%] [[data-uiify-input-group]>[data-part=addon]>&]:whitespace-normal [[data-uiify-input-group]>[data-part=addon]>&]:[overflow-wrap:anywhere] [[data-uiify-item-content]>&]:[justify-self:start]');
    
    @source inline("before:content-[''] after:content-['']");
    
    @layer base { :root { --uiify-control-text: var(--text-sm); } @media (pointer: coarse) { :root { --uiify-control-text: var(--text-base); } } }
    
    /* Authored animation and registered properties used by the Tailwind skin.
     * Visual rules belong in utility recipes; only at-rule definitions live here. */
    @layer components {
      @keyframes uiify-presence-in {
        from {
          opacity: 0;
          scale: var(--presence-scale);
        }
      }
    }
    
    @layer components {
      @keyframes uiify-presence-out {
        to {
          opacity: 0;
          scale: var(--presence-scale);
        }
      }
    }
    
    @property --uiify-spinner-tail {
      syntax: "<angle>";
      inherits: false;
      initial-value: 0deg;
    }
    
    @property --uiify-spinner-length {
      syntax: "<angle>";
      inherits: false;
      initial-value: 36deg;
    }
    
    @layer components {
      @keyframes uiify-spinner-rotate {
        to {
          transform: rotate(360deg);
        }
      }
    }
    
    @layer components {
      @keyframes uiify-spinner-orbit {
        0% {
          --uiify-spinner-tail: 0deg;
          --uiify-spinner-length: 36deg;
        }
    
        50% {
          --uiify-spinner-tail: 90deg;
          --uiify-spinner-length: 324deg;
        }
    
        100% {
          --uiify-spinner-tail: 360deg;
          --uiify-spinner-length: 36deg;
        }
      }
    }
    
    @layer components {
      @keyframes uiify-skeleton-pulse {
        50% {
          opacity: 0.45;
        }
      }
    }
    
    @layer components {
      @keyframes uiify-skeleton-shimmer {
        to {
          background-position: -200% 0;
        }
      }
    }
    
    @layer components {
      @keyframes uiify-skeleton-reveal {
        from {
          opacity: 0;
        }
        to {
          opacity: 1;
        }
      }
    }
    
    @property --candle-body-height {
      syntax: "<percentage>";
      inherits: true;
      initial-value: 0%;
    }
    
    @property --candle-body-top {
      syntax: "<percentage>";
      inherits: true;
      initial-value: 50%;
    }
    
    @property --candle-slot {
      syntax: "<percentage>";
      inherits: true;
      initial-value: 100%;
    }
    
    @property --candle-wick-height {
      syntax: "<percentage>";
      inherits: true;
      initial-value: 0%;
    }
    
    @property --candle-wick-top {
      syntax: "<percentage>";
      inherits: true;
      initial-value: 50%;
    }
    
    @property --candle-x {
      syntax: "<percentage>";
      inherits: true;
      initial-value: 0%;
    }
    
    @property --candle-scale {
      syntax: "<number>";
      inherits: true;
      initial-value: 1;
    }
    
    @property --line-y {
      syntax: "<percentage>";
      inherits: true;
      initial-value: 50%;
    }
    
    @property --line-prev-y {
      syntax: "<percentage>";
      inherits: true;
      initial-value: 50%;
    }
    
    @property --line-count {
      syntax: "<integer>";
      inherits: false;
      initial-value: 1;
    }
    
    @property --line-scale {
      syntax: "<number>";
      inherits: true;
      initial-value: 1;
    }
    
    @layer components {
      @keyframes uiify-otp-input-caret-blink {
        50% {
          opacity: 0;
        }
      }
    }

    API

    FileUpload.Root

    PropTypeDefaultDescription
    childrenReactNode—File upload parts: hidden input, trigger, dropzone and list.
    valuereadonly File[]—Current files, making the component controlled.
    defaultValuereadonly File[][]Starting files for the uncontrolled component.
    onValueChange(files: File[]) => void—Called with the accepted files whenever the selection changes.
    onReject(rejections: FileRejection[]) => void—Called with the files that failed validation and why.
    acceptstring[][]Accepted file types, as extensions or MIME types.
    maxFilesnumber—Largest number of files that can be held at once.
    maxSizenumber—Largest accepted file size in bytes.
    minSizenumber—Smallest accepted file size in bytes.
    multiplebooleanfalseLets the picker select more than one file.
    disabledbooleanfalseDisables the picker, the dropzone and the remove buttons.

    FileUpload.HiddenInput

    PropTypeDefaultDescription
    classNamestring—Class applied to the native file input.

    FileUpload.Trigger

    PropTypeDefaultDescription
    childrenReactNode—Content of the control that opens the file picker.
    classNamestring—Class applied to the trigger element.
    renderReactElement | ((props, state) => ReactElement)—Replace the rendered element while keeping behavior and accessibility wiring.

    FileUpload.Dropzone

    PropTypeDefaultDescription
    childrenReactNode—Content of the drop target.
    classNamestring—Class applied to the dropzone element.
    renderReactElement | ((props, state) => ReactElement)—Replace the rendered element while keeping behavior and accessibility wiring.

    FileUpload.List

    PropTypeDefaultDescription
    childrenReactNode | ((files: readonly File[]) => ReactNode)—Render accepted files from the live list. Controlled value changes and external resets are reflected immediately.
    classNamestring—Class applied to the list element.

    FileUpload.Item

    PropTypeDefaultDescription
    childrenReactNode—Content describing the file, such as its name and size.
    classNamestring—Class applied to the item element.
    index*number—Position of the file this item represents.
    renderReactElement | ((props, state) => ReactElement)—Replace the rendered element while keeping behavior and accessibility wiring.

    FileUpload.ItemRemove

    PropTypeDefaultDescription
    classNamestring—Class applied to the remove button.
    index*number—Position of the file this button removes.

    FileRejection

    PropTypeDefaultDescription
    file*File—The file that was not accepted.
    reason*"accept" | "max-size" | "max-files"—Which rule the file failed.

    Keyboard

    KeyAction
    Enter / SpaceOpen the file picker from Trigger or remove a focused file
    TabMove between the picker trigger and file removal controls

    Accessibility

    Give HiddenInput an accessible label. List receives the accepted files and renders their names, sizes and labeled removal buttons. Root supports controlled value, cumulative maxFiles, maxSize and accept validation; onReject reports rejected files. Disabled roots block picker, drop and removal actions. File handling requires hydration.

    Styling hooks

    data-uiify-file-uploaddata-partdata-disableddata-state