Customize
Styling and theming
import '@devix-labs/image-cropper/styles.css'; // plain
import '@devix-labs/image-cropper/styles.layer.css'; // the same, inside @layer devix
Variables
.dxi {
--dxi-radius: 12px;
--dxi-font-size: 13px;
--dxi-gap: 10px;
--dxi-stage-height: 320px;
--dxi-handle: 12px;
--dxi-tool: 30px;
--dxi-bg: #fff;
--dxi-text: #18181b;
--dxi-muted: #71717a;
--dxi-border: #e4e4e7;
--dxi-surface: #fafafa; /* the drop zone */
--dxi-stage-bg: #f4f4f5; /* behind the picture */
--dxi-accent: #ea4b71; /* handles, focus rings */
--dxi-shade: rgb(17 17 28 / 0.55); /* over everything outside the crop */
--dxi-danger: #dc2626;
--dxi-checks: repeating-conic-gradient(#e4e4e7 0% 25%, #fff 0% 50%) 50% / 16px 16px;
}
A taller stage for landscape work, or a shorter one in a sidebar:
.dxi { --dxi-stage-height: 460px; }
Dark
theme: 'auto' reads the page — Tailwind's .dark, data-theme, Bootstrap's data-bs-theme, or
simply the background behind the widget — and re-reads it when a theme toggle flips.
Your classes
createImageCropper(input, {
classNames: {
empty: 'rounded-2xl border-2 border-dashed',
stage: 'rounded-2xl',
handle: 'ring-2 ring-white',
tool: 'rounded-full',
},
});
Parts: root, empty, stage, image, selection, handle, grid, tools, tool, zoom,
meta, actions, button, preview, error, aspects, aspectButton.
State
| Selector | When |
|---|---|
.dxi--empty |
No picture yet. |
.dxi--over |
A file is being dragged over it. |
.dxi--dragging |
The crop or the picture is being moved. |
.dxi--fixed |
In avatar mode. |
.dxi-selection--round |
The crop is an ellipse. |
.dxi-aspect.is-chosen |
The shape currently in use. |
Three recipes
shadcn/ui
.dxi {
--dxi-bg: hsl(var(--card));
--dxi-surface: hsl(var(--muted));
--dxi-stage-bg: hsl(var(--muted));
--dxi-text: hsl(var(--card-foreground));
--dxi-muted: hsl(var(--muted-foreground));
--dxi-border: hsl(var(--border));
--dxi-accent: hsl(var(--primary));
--dxi-radius: var(--radius);
}
Bootstrap 5
.dxi {
--dxi-bg: var(--bs-body-bg);
--dxi-surface: var(--bs-tertiary-bg);
--dxi-stage-bg: var(--bs-secondary-bg);
--dxi-text: var(--bs-body-color);
--dxi-muted: var(--bs-secondary-color);
--dxi-border: var(--bs-border-color);
--dxi-accent: var(--bs-primary);
--dxi-radius: var(--bs-border-radius-lg);
}
Material 3
.dxi {
--dxi-bg: var(--md-sys-color-surface);
--dxi-surface: var(--md-sys-color-surface-container);
--dxi-stage-bg: var(--md-sys-color-surface-container-low);
--dxi-text: var(--md-sys-color-on-surface);
--dxi-muted: var(--md-sys-color-on-surface-variant);
--dxi-border: var(--md-sys-color-outline-variant);
--dxi-accent: var(--md-sys-color-primary);
--dxi-radius: 16px;
--dxi-handle: 16px;
}
An avatar that looks like an avatar
createImageCropper(input, { mode: 'fixed', aspect: 1, round: true });
.dxi { --dxi-stage-height: 260px; --dxi-shade: rgb(17 17 28 / 0.7); }
.dxi-selection { outline-width: 2px; }
The heavier shade and the thicker outline make the circle read as the subject rather than as a rectangle with a hole in it.
Updated 15 Sep 2026