Monsoon v0.2.0
Made by Alexandre Kohler Star

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.

Media and files7 classesNeeds monsoon.jsLight and dark

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

Full

JavaScript API

Attribute

data-m-avatar-upload on an .avatar-upload with a label.avatar-upload-trigger holding an <input type="file">

Options

OptionDescription
data-m-avatar-upload-sizeOutput width and height in pixels, 512 by default.
data-m-avatar-upload-typeOutput type: image/png (default), image/jpeg or image/webp.
data-m-avatar-upload-shapecircle (default) or square.
data-m-avatar-upload-max-sizeLargest accepted file, like 8mb.
data-m-avatar-upload-hiddenSelector of a hidden input for the data URL, defaults to a hidden input inside.
data-m-avatar-upload-titleDialog title.

Methods

MethodDescription
Monsoon.avatarUpload(el).open(file)Opens the crop dialog for a File.
Monsoon.avatarUpload(el).fileThe last saved File.
Monsoon.avatarUpload(el).cropperThe cropper API inside the dialog.

Events

EventDescription
monsoon:avatar-opendetail.file when the dialog opens.
monsoon:avatar-changedetail.file, detail.dataURL and detail.data after saving.
monsoon:avatar-errordetail.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.

ClassTypeWhat 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
.avatar-upload-trigger:hover,
.avatar-upload[data-dragover] .avatar-upload-trigger {
  box-shadow: 0 0 0 calc(var(--m-border-width) * 2) var(--tone), var(--m-shadow-sm);
}

.avatar-upload {
  --tone: var(--primary);
  --tone-fg: var(--primary-foreground);
  --avatar-upload-size: 5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
}
Part Part of the avatar upload. On a <label> in the example. Sets grid display, alignment, width, height, corner radius and background.
CSS
.avatar-upload-trigger:hover .avatar-upload-badge {
  scale: 1.08;
}

.avatar-upload-trigger:has(> input:focus-visible) {
  outline: var(--m-ring);
  outline-offset: var(--m-ring-offset);
}
Part Part of the avatar upload. On an <img> in the example. Sets width, height and corner radius.
CSS
.avatar-upload-image {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
}
Part Part of the avatar upload. On a <span> in the example. Sets absolute position, offsets, grid display, alignment, width and height.
CSS
.avatar-upload-badge > svg {
  width: 0.875rem;
  height: 0.875rem;
}

.avatar-upload-badge {
  position: absolute;
  right: -0.125rem;
  bottom: -0.125rem;
  display: grid;
  place-items: center;
  width: 1.875rem;
  height: 1.875rem;
  border: calc(var(--m-border-width) * 2) solid var(--surface);
  border-radius: 50%;
  background: var(--tone);
  color: var(--tone-fg);
  box-shadow: var(--m-shadow-sm);
  transition: scale var(--m-duration) var(--m-ease-spring);
}
Modifier Modifier, combined with the avatar-upload class. Not used in the example above. Sets alignment, text color and text size.
CSS
.avatar-upload-error {
  flex-basis: 100%;
  color: color-mix(in oklab, var(--danger) 85%, var(--foreground));
  font-size: 0.8125rem;
}
Modifier Modifier, combined with the avatar-upload class. Not used in the example above. Sets --modal-w.
CSS
.avatar-upload-dialog {
  --modal-w: 30rem;
}
Modifier Modifier, combined with the avatar-upload class. Not used in the example above. Sets margin, and --cropper-h.
CSS
.avatar-upload-cropper {
  --cropper-h: min(20rem, 55dvh);
  margin-block: 0.5rem 0.25rem;
}

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.

index.html
<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 label linked with for, or an aria-label when the purpose is obvious from context. Link help and error text with aria-describedby; invalid styles use :user-invalid, so they appear only after the user interacts.
  • Give meaningful images an alt text and decorative ones alt="". 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 -foreground tokens that keep text at 4.5:1 or better in light and dark mode.

No recent searches

Try , , or .

Recent

Loading the search index

No results for ""

Check the spelling, or search a class name such as px-4.