Media and files
Avatar upload
Click, drop or paste a picture to crop it in a circle inside a dialog; saving puts the cropped File into the file input so FormData sends it, writes a data URL into a hidden input when there is one and updates the picture. It needs monsoon.js. Without it the original file is sent.
Needs monsoon.js. The styles work on their own, and the behavior comes from the optional interactive layer: add <script src="https://cdn.jsdelivr.net/npm/monsooncss/dist/monsoon.js" defer></script> or import 'monsooncss/js'. See the interactive layer.
Example
<div class="avatar-upload" data-m-avatar-upload data-m-avatar-upload-size="256" data-m-avatar-upload-max-size="8mb"><label class="avatar-upload-trigger"><img class="avatar-upload-image" src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='160'%20height='160'%20viewBox='0%200%20160%20160'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%20x2='1'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23c4b5fd'/%3E%3Cstop%20offset='1'%20stop-color='%23f9a8d4'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='160'%20height='160'%20fill='url(%23g)'/%3E%3Ccircle%20cx='80'%20cy='64'%20r='28'%20fill='%23fff'%20opacity='.9'/%3E%3Cpath%20d='M28%20150c6-30%2028-44%2052-44s46%2014%2052%2044z'%20fill='%23fff'%20opacity='.9'/%3E%3C/svg%3E" alt=""><input type="file" name="avatar" accept="image/*" aria-label="Change profile photo"><span class="avatar-upload-badge"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 8h3l2-3h6l2 3h3a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1zM12 17a4 4 0 1 0 0-8 4 4 0 0 0 0 8z"/></svg></span></label><div class="grid gap-1"><span class="text-sm font-semibold">Profile photo</span><span class="text-xs text-muted-foreground">PNG, JPG or GIF up to 8 MB. Click the picture or drop an image on it.</span></div><input type="hidden" name="avatar_data"></div>JavaScript API
Attribute
data-m-avatar-upload on an .avatar-upload with a label.avatar-upload-trigger holding an <input type="file">
Options
| Option | Description |
|---|---|
data-m-avatar-upload-size | Output width and height in pixels, 512 by default. |
data-m-avatar-upload-type | Output type: image/png (default), image/jpeg or image/webp. |
data-m-avatar-upload-shape | circle (default) or square. |
data-m-avatar-upload-max-size | Largest accepted file, like 8mb. |
data-m-avatar-upload-hidden | Selector of a hidden input for the data URL, defaults to a hidden input inside. |
data-m-avatar-upload-title | Dialog title. |
Methods
| Method | Description |
|---|---|
Monsoon.avatarUpload(el).open(file) | Opens the crop dialog for a File. |
Monsoon.avatarUpload(el).file | The last saved File. |
Monsoon.avatarUpload(el).cropper | The cropper API inside the dialog. |
Events
| Event | Description |
|---|---|
monsoon:avatar-open | detail.file when the dialog opens. |
monsoon:avatar-change | detail.file, detail.dataURL and detail.data after saving. |
monsoon:avatar-error | detail.error when the export fails. |
Classes
7 classes. Click a name to copy it. Utilities on the same element always win over component defaults, so any of them can be adjusted with plain utility classes.
| Class | Type | What it does |
|---|---|---|
| Base | Base class of the avatar upload. On a <div> in the example. Sets flex display, alignment, gap and shadow, and --tone, --tone-fg and --avatar-upload-size.CSS |
|
| Part | Part of the avatar upload. On a <label> in the example. Sets grid display, alignment, width, height, corner radius and background.CSS |
|
| Part | Part of the avatar upload. On an <img> in the example. Sets width, height and corner radius.CSS |
|
| Part | Part of the avatar upload. On a <span> in the example. Sets absolute position, offsets, grid display, alignment, width and height.CSS |
|
| Modifier | Modifier, combined with the avatar-upload class. Not used in the example above. Sets alignment, text color and text size.CSS |
|
| Modifier | Modifier, combined with the avatar-upload class. Not used in the example above. Sets --modal-w.CSS |
|
| Modifier | Modifier, combined with the avatar-upload class. Not used in the example above. Sets margin, and --cropper-h.CSS |
Usage
The styles ship inside the same stylesheet as the utilities, and the behavior comes from the optional monsoon.js script, which enhances any element with a matching data-m attribute, including markup added later.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/monsooncss">
<script src="https://cdn.jsdelivr.net/npm/monsooncss/dist/monsoon.js" defer></script>
<!-- Avatar upload: start from the base class, then add modifiers -->
<div class="avatar-upload" data-m-avatar-upload data-m-avatar-upload-size="256" data-m-avatar-upload-max-size="8mb"><label class="avatar-upload-trigger"><img class="avatar-upload-image" src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='160'%20height='160'%20viewBox='0%200%20160%20160'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%20x2='1'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%23c4b5fd'/%3E%3Cstop%20offset='1'%20stop-color='%23f9a8d4'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='160'%20height='160'%20fill='url(%23g)'/%3E%3Ccircle%20cx='80'%20cy='64'%20r='28'%20fill='%23fff'%20opacity='.9'/%3E%3Cpath%20d='M28%20150c6-30%2028-44%2052-44s46%2014%2052%2044z'%20fill='%23fff'%20opacity='.9'/%3E%3C/svg%3E" alt=""><input type="file" name="avatar" accept="image/*" aria-label="Change profile photo"><span class="avatar-upload-badge"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 8h3l2-3h6l2 3h3a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1zM12 17a4 4 0 1 0 0-8 4 4 0 0 0 0 8z"/></svg></span></label><div class="grid gap-1"><span class="text-sm font-semibold">Profile photo</span><span class="text-xs text-muted-foreground">PNG, JPG or GIF up to 8 MB. Click the picture or drop an image on it.</span></div><input type="hidden" name="avatar_data"></div>Theme it with --seed on any ancestor, for example style="--seed: #0d9488", and force a scheme with class="dark" or class="light" on a wrapper.
Accessibility
- Give every control a visible
labellinked withfor, or anaria-labelwhen the purpose is obvious from context. Link help and error text witharia-describedby; invalid styles use:user-invalid, so they appear only after the user interacts. - Give meaningful images an
alttext and decorative onesalt="". Initials avatars should expose the full name to screen readers. - Every interactive part shows a visible focus ring with
:focus-visible, and tone colors pair with-foregroundtokens that keep text at 4.5:1 or better in light and dark mode.