File Upload / Dropzone
رفع الملفات<input type="file">
also calleddropzone، file picker، file input، upload field، drag and drop upload، attachment field، منطقة الإفلات، حقل رفع الملف، دروب زون، أبلود، سحب وإفلات الملفات، حقل المرفقات
File upload is how a user hands a file from their own machine to the application, and on the web it rests on one element: <input type="file">. Pressing it opens the operating system picker, a window your page has no authority over: it will not open without a user gesture, it never reveals the path on disk, and all that comes back is a File object. Three things get mistaken for it. A dropzone is a drag target and not a field, a rectangle listening for dragover and drop, and it is a second route to the same files that never replaces the input, because dropping does not exist on a touch screen or from a keyboard. The "Choose a file" button is rarely its own element; it is usually a <label> bound to the input, which forwards the pointer press while the input keeps the focus and the keyboard activation, since a <label> is not in the tab order. A full uploader sits on top of all of this: a queue, per-file progress, cancel, retry. The field itself uploads nothing, it only selects, and sending the bytes belongs to fetch or XMLHttpRequest, unless the input sits inside a <form> with enctype="multipart/form-data", where the browser sends them on submit.
If you called it…
Live specimen
Interact with the demo. Every part is real and numbered.
Anatomy: every part, named
Hover a row to locate it in the demo above.
Build prompt
How it behaves right-to-left
This section is ours alone.
The file row
mirrorsThe row is ordered logically: icon and name at the start, size and the remove button at the end, so the whole row swaps in Arabic and the remove button lands on the left. Build it as a flex row with margin-inline-start: auto on the button, never margin-left: auto and never float: right. If a coloured edge marks the file status, draw it with border-inline-start rather than border-left, or after the flip the edge sits at the opposite end from the name.
The per-file bar
mirrorsThe bar fills from the logical start, which in Arabic is the right. Build the fill as an inner element pinned with inset-inline-start: 0 and animate its inline-size from 0 to 100%: it mirrors on its own, because the edge it is anchored to is the one that moved. Stay off transform: scaleX(), since transform-origin accepts no logical keyword at all, and off linear-gradient(to right, …), since gradients have none either. A native <progress> takes its direction from the inherited direction value, so a dir="ltr" you add to the row to fix the file name reverses the fill along with it.
A Latin file name in an Arabic row
never mirrorsA file name is user text whose direction you cannot know in advance, so isolate it instead of forcing one: <bdi>, or unicode-bidi: plaintext on the element holding it. CSS has no direction: auto, since the property takes only ltr and rtl; the automatic behaviour lives in the dir="auto" attribute and in the plaintext keyword, which isolates the run and reads its direction from the first strong character. Without isolation the neutral characters next to digits (hyphen, underscore, dot) take the direction of the Arabic paragraph, the name breaks into islands that are reordered right to left, and invoice_2026.pdf renders on screen as pdf.2026_invoice. Parentheses make it worse, since their Bidi_Mirrored property is Yes and the glyphs themselves swap. Do not patch this with dir="ltr" on the row: that flips the layout and the bar with it, and it mangles Arabic names carrying digits such as "تقرير 2026.pdf".
Size and the numeral system
never mirrorsA file size is a number and a unit, and neither mirrors: numbers read left to right in Arabic too. The failure is mixing systems: a page that prints ٢٫٤ in Arabic-Indic digits and then MB in Latin letters, or one that builds the string with toFixed(1) and glues an Arabic unit onto it, so "2.4 ميجابايت" sits in the list next to "٨٦٠ كيلوبايت". Pick one numbering system for the whole product and format through it: Intl.NumberFormat("ar-EG", { style: "unit", unit: "megabyte", unitDisplay: "short" }) returns the number, the separator and the unit from one source, and the Arabic decimal separator is U+066B, not the dot. If you want Latin digits inside an Arabic page, ask for them explicitly with ar-EG-u-nu-latn.
The system dialog
never mirrorsThe picker window is not part of your page: the operating system draws it, and its direction and language follow the device settings, not your dir attribute and not your interface language. Do not promise an Arabic dialog and do not try to style it. The default text inside the input itself (Choose File, No file chosen) comes from the browser locale and cannot be translated or replaced, and ::file-selector-button styles the button without touching its words. That alone is reason enough to hide the input behind a <label> whose text you write yourself.
In code
Each row is one framework's word for the same thing. Take the row your project speaks.
See also
Updated · 2026-08-19