Forms
Date pickers, form alignment, and input patterns.
Single Column
<form method="post">
{% csrf_token %}
<div class="form-row">
<div class="form-group">
<label for="full_name">Full Name</label>
<input type="text" id="full_name" name="full_name"
class="vTextField" placeholder="Jane Smith">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email"
class="vTextField" placeholder="jane@example.com">
<span class="helptext">Help text below input</span>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="bio">Bio</label>
<textarea id="bio" name="bio" class="vLargeTextField"
rows="3" placeholder="..."></textarea>
</div>
</div>
<div class="form-row" style="margin-top: 16px;">
<button type="button" class="button button-secondary">Cancel</button>
<button type="submit" class="button button-primary">Save</button>
</div>
</form>
Two-Column Alignment
<form method="post">
{% csrf_token %}
<div class="form-row form-row-2col">
<div class="form-group">
<label for="first_name">First Name</label>
<input type="text" id="first_name" name="first_name"
class="vTextField" placeholder="Jane">
</div>
<div class="form-group">
<label for="last_name">Last Name</label>
<input type="text" id="last_name" name="last_name"
class="vTextField" placeholder="Smith">
</div>
</div>
<div class="form-row form-row-2col">
<div class="form-group">
<label for="department">Department</label>
<select id="department" name="department" class="vTextField">
<option value="">Choose...</option>
<option value="eng">Engineering</option>
</select>
</div>
<div class="form-group">
<label for="role">Role</label>
<select id="role" name="role" class="vTextField">
<option value="">Choose...</option>
<option value="ic">Individual Contributor</option>
</select>
</div>
</div>
<div class="form-row" style="margin-top: 16px;">
<button type="button" class="button button-secondary">Cancel</button>
<button type="submit" class="button button-primary">Save</button>
</div>
</form>
Date Pickers
<!-- Include admin widgets CSS in extra_css block -->
<link rel="stylesheet" href="{% static 'admin/css/widgets.css' %}">
<div class="form-row form-row-2col">
<div class="form-group">
<label for="start_date">Start Date</label>
<div class="date-input-wrapper">
<input type="date" id="start_date" name="start_date"
class="vTextField vDateField">
<button type="button" class="date-picker-btn"
data-target="start_date" aria-label="Open calendar">
<svg viewBox="0 0 24 24" width="20" height="20"
fill="currentColor">
<path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99
.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9
2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11z..."/>
</svg>
</button>
</div>
<span class="helptext">Click the calendar icon</span>
</div>
<div class="form-group">
<label for="end_date">End Date</label>
<div class="date-input-wrapper">
<input type="date" id="end_date" name="end_date"
class="vTextField vDateField">
<button type="button" class="date-picker-btn"
data-target="end_date">...</button>
</div>
</div>
</div>
<!-- datetime-local for combined date+time -->
<input type="datetime-local" id="meeting_time"
class="vTextField vDateField">
<!-- JS for date picker buttons (add to extra_js block) -->
<script>
document.querySelectorAll('.date-picker-btn').forEach(function(btn) {
btn.addEventListener('click', function() {
var input = document.getElementById(this.dataset.target);
if (input.showPicker) {
input.showPicker();
} else {
input.focus();
input.click();
}
});
});
</script>
File & Image Upload
<!-- Basic file input -->
<input type="file" id="attachment" name="attachment" class="vTextField">
<!-- Drag & drop upload zone -->
<div class="file-upload-wrapper">
<div class="file-upload-dropzone" id="avatar-dropzone">
<svg viewBox="0 0 24 24" width="32" height="32"
fill="currentColor"
style="color: var(--body-quiet-color);">
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2
2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5
3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
</svg>
<span class="dropzone-text">
Drag & drop an image or click to browse
</span>
<span class="dropzone-filename" id="avatar-filename"
style="display: none;"></span>
<input type="file" id="avatar" name="avatar"
accept="image/*" class="file-upload-input">
</div>
</div>
<!-- CSS needed (add to extra_css block) -->
<style>
.file-upload-wrapper { width: 100%; }
.file-upload-dropzone {
position: relative;
display: flex; flex-direction: column;
align-items: center; justify-content: center;
gap: 8px; padding: 24px 16px;
border: 2px dashed var(--card-border);
border-radius: var(--radius-md);
background: var(--body-bg); cursor: pointer;
}
.file-upload-dropzone:hover,
.file-upload-dropzone.dragover {
border-color: var(--primary);
background: color-mix(in srgb, var(--primary) 5%, var(--body-bg));
}
.file-upload-input {
position: absolute; inset: 0;
width: 100%; height: 100%;
opacity: 0; cursor: pointer;
}
</style>
<!-- JS for dropzone (add to extra_js block) -->
<script>
document.querySelectorAll('.file-upload-dropzone')
.forEach(function(zone) {
var input = zone.querySelector('.file-upload-input');
var text = zone.querySelector('.dropzone-text');
var filename = zone.querySelector('.dropzone-filename');
input.addEventListener('change', function() {
if (this.files.length > 0) {
text.style.display = 'none';
filename.textContent = this.files[0].name;
filename.style.display = '';
}
});
zone.addEventListener('dragover', function(e) {
e.preventDefault();
zone.classList.add('dragover');
});
zone.addEventListener('dragleave', function() {
zone.classList.remove('dragover');
});
zone.addEventListener('drop', function(e) {
e.preventDefault();
zone.classList.remove('dragover');
if (e.dataTransfer.files.length > 0) {
input.files = e.dataTransfer.files;
input.dispatchEvent(new Event('change'));
}
});
});
</script>