File input
Category: Forms · Tags: file, upload, attachment, multiple, accept
Receipts
PDF, PNG or JPG, up to 10 MB each. You can choose several files.
No files chosen.
How to use it
Use to attach one or more files to a form, such as receipts on an expense claim. State the accepted types and size limit up front, and check both again on the server -- accept only filters the picker. For large drag-and-drop uploads with progress, build on this with a drop zone rather than replacing the native input. Change the ids, the accept list and hint text, and remove multiple if only one file is allowed.
Accessibility
The real control is a native type="file" input, visually hidden but still focusable, labelled by the visible "Choose files" <label for>; when the input has keyboard focus the label shows a 2px outline, and Enter or Space opens the system file picker. A hint with the accepted types (also enforced with accept) is attached through aria-describedby. After choosing, each file's name and size is listed and a role="status" line announces "2 files chosen". Text is #0f172a on #ffffff (17.85:1), the hint #475569 on #ffffff (7.58:1), the button label #1d4ed8 on #ffffff (6.70:1) and #0f172a on the #e2e8f0 hover (14.48:1), and file names #0f172a on #f8fafc (17.06:1).
Source (copy-ready)
<div class="dcl-fileinput">
<p class="dcl-fileinput__title" id="dcl-fileinput-title">Receipts</p>
<p class="dcl-fileinput__hint" id="dcl-fileinput-hint">PDF, PNG or JPG, up to 10 MB each. You can choose several files.</p>
<input class="dcl-fileinput__input" id="dcl-fileinput-files" name="receipts" type="file" multiple accept=".pdf,.png,.jpg,.jpeg,application/pdf,image/png,image/jpeg" aria-describedby="dcl-fileinput-title dcl-fileinput-hint">
<label class="dcl-fileinput__button" for="dcl-fileinput-files">Choose files</label>
<p class="dcl-fileinput__status" role="status">No files chosen.</p>
<ul class="dcl-fileinput__list"></ul>
</div>
<style>
.dcl-fileinput { max-width: 26rem; font: inherit; color: #0f172a; background-color: #ffffff; }
.dcl-fileinput__title { margin: 0; font-weight: 600; color: #0f172a; background-color: #ffffff; }
.dcl-fileinput__hint { margin: 0.25rem 0 0.75rem; font-size: 0.9rem; color: #475569; background-color: #ffffff; }
.dcl-fileinput__input { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.dcl-fileinput__button { display: inline-flex; align-items: center; min-height: 2.75rem; padding: 0 1.1rem; border: 2px solid #1d4ed8; border-radius: 0.5rem; cursor: pointer; font-weight: 600; color: #1d4ed8; background-color: #ffffff; }
.dcl-fileinput__button:hover { color: #0f172a; background-color: #e2e8f0; }
.dcl-fileinput__input:focus-visible + .dcl-fileinput__button { outline: 2px solid #0f172a; outline-offset: 2px; }
.dcl-fileinput__status { margin: 0.6rem 0 0.25rem; color: #0f172a; background-color: #ffffff; }
.dcl-fileinput__list { margin: 0; padding: 0; list-style: none; }
.dcl-fileinput__item { margin-top: 0.35rem; padding: 0.45rem 0.65rem; border: 1px solid #cbd5e1; border-radius: 0.375rem; overflow-wrap: anywhere; color: #0f172a; background-color: #f8fafc; }
</style>
<script>
(function () {
document.querySelectorAll(".dcl-fileinput").forEach(function (root) {
if (root.dataset.dclBound) { return; }
root.dataset.dclBound = "true";
var input = root.querySelector(".dcl-fileinput__input");
var status = root.querySelector(".dcl-fileinput__status");
var list = root.querySelector(".dcl-fileinput__list");
if (!input) { return; }
function size(bytes) {
if (bytes >= 1048576) { return (bytes / 1048576).toFixed(1) + " MB"; }
return Math.max(1, Math.round(bytes / 1024)) + " KB";
}
input.addEventListener("change", function () {
var files = Array.prototype.slice.call(input.files || []);
if (list) {
list.textContent = "";
files.forEach(function (file) {
var item = document.createElement("li");
item.className = "dcl-fileinput__item";
item.textContent = file.name + " (" + size(file.size) + ")";
list.appendChild(item);
});
}
if (status) {
status.textContent = files.length === 0 ? "No files chosen." : files.length === 1 ? "1 file chosen." : files.length + " files chosen.";
}
});
});
})();
</script>
Was this useful? Send feedback.