Like it? I'd appreciate the support :)
The best Gradient Editor you'll find for Svelte. We use this package on our project chatstyle.gg, go check it out! Everything below this line isn't handwritten and optimized for da clankers, have fun!
It keeps two representations of the same gradient synchronized:
background-imageGradientModel object for application logicnpm install svelte-gradient-editor
Peer dependency:
svelte ^5.0.0Runtime dependencies installed with the package:
svelte-awesome-color-picker provides the stop color picker UI.colord provides color parsing, validation, conversion, alpha handling, and color math.<script lang="ts">
/* Import that shit */
import { GradientEditor } from 'svelte-gradient-editor';
import type { GradientEditorMessages, GradientModel } from 'svelte-gradient-editor';
/* THIS is the gradient result. You'll get the full css gradient value. */
let value = 'linear-gradient(90deg, #ff0000 0%, #0000ff 100%)';
let model: GradientModel | undefined;
/* Optional: pass translated strings to the editor */
const messages: GradientEditorMessages = {
gradientMode: 'Gradient type',
linear: 'Linear',
conic: 'Conic',
radial: 'Radial',
angle: 'Angle',
stopAriaLabel: ({ position, index, total }) =>
`Color stop ${index + 1} of ${total} at ${position}%`
};
</script>
<GradientEditor
bind:value
bind:model
/* If needed, you can adjust the decimal accuracy */
angleDecimals={0}
positionDecimals={1}
/* See translated messages above */
{messages}
on:change={(event) => {
console.log(event.detail.value);
console.log(event.detail.model);
}}
/>
linear-gradient(...), conic-gradient(...), and radial-gradient(...)value and model synchronized after every changesvelte-awesome-color-picker for stop color editing with hex, rgb, hsv, and alpha controlsThe package entry point exports:
import {
GradientEditor,
cloneGradientModel,
defaultGradientModel,
modelSignature,
normalizeColor,
normalizeModel,
parseGradient,
serializeGradient
} from 'svelte-gradient-editor';
import type {
ConicGradientModel,
GradientEditorMessages,
GradientModel,
GradientStop,
LinearGradientModel,
RadialGradientModel
} from 'svelte-gradient-editor';
| Prop | Type | Default | Purpose |
|---|---|---|---|
value |
string |
'' |
Bound CSS gradient string. The component normalizes it to a supported canonical form. |
model |
GradientModel | undefined |
undefined |
Bound typed model. If provided, this takes precedence over value. |
showValue |
boolean |
false |
Shows the current serialized gradient string inside the component UI. |
angleDecimals |
number |
0 |
Decimal precision for linear angle editing and emitted angle values. |
positionDecimals |
number |
0 |
Decimal precision for stop position editing, dragging, adding, and emitted values. |
messages |
GradientEditorMessages |
{} |
Overrides built-in English labels and ARIA text. |
change
type GradientEditorChangeDetail = {
value: string;
model: GradientModel;
};
The component emits change on every committed editor update.
bind:value
Canonical CSS gradient string suitable for background-imagebind:model
Canonical structured model synchronized with valuetype GradientStop = {
id: string;
color: string;
position: number;
};
type LinearGradientModel = {
type: 'linear';
angle: number;
stops: GradientStop[];
};
type ConicGradientModel = {
type: 'conic';
angle: number;
stops: GradientStop[];
};
type RadialGradientModel = {
type: 'radial';
shape: 'circle' | 'ellipse';
stops: GradientStop[];
};
type GradientModel = LinearGradientModel | ConicGradientModel | RadialGradientModel;
type GradientEditorMessages = {
gradientMode?: string;
linear?: string;
conic?: string;
radial?: string;
radialShape?: string;
ellipse?: string;
circle?: string;
angleDial?: string;
angle?: string;
gradientTrack?: string;
gradientStops?: string;
editGradientStop?: string;
stopColor?: string;
position?: string;
closeColorPicker?: string;
deleteStop?: string;
stopAriaLabel?: (input: {
position: number;
index: number;
total: number;
color: string;
}) => string;
};
This section is intended to be explicit enough for both humans and coding agents.
model is passed, the component uses model as the incoming source of truth.model is not passed, the component parses value.value or model.rgba(r, g, b, a) syntax.0..100.0..360 degree representation.'circle' or 'ellipse'.By default, the component edits and emits integers.
<GradientEditor bind:value bind:model />
Enable decimals explicitly:
<GradientEditor bind:value bind:model angleDecimals={1} positionDecimals={2} />
Rules:
angleDecimals and positionDecimals should be non-negative integers.0.parseGradient() and serializeGradient() keep their own API and are not driven by component prop precision.Supported gradient families:
linear-gradient(...)conic-gradient(...)radial-gradient(...)Gradient input may span multiple lines. Whitespace is collapsed before parsing, and serialized output is always a canonical single-line CSS gradient string.
Supported linear direction syntax:
90degto right, to top leftSupported radial syntax:
circle and ellipseSupported conic syntax:
conic-gradient(#ff0000 0%, #0000ff 100%)conic-gradient(from 45deg, #ff0000 0%, #0000ff 100%)Supported stop color syntax:
#f00, #f008, #ff0000, and #ff000080rgb(...) and rgba(...) colors supported by colordThe stop color picker exposes hex, rgb, hsv, and alpha editing. HSL is not an editor mode.
The package does not preserve full CSS gradient syntax.
Examples of unsupported or only partially supported input:
turnat centerat centerclosest-side or coverred or transparentConic support in v1 is intentionally angle-only. Parsed conic gradients round-trip canonically as conic-gradient(from Xdeg, ...).
When unsupported input is encountered, the parser falls back to the closest supported model or to a safe default gradient.
serializeGradient({
type: 'linear',
angle: 90,
stops: [
{ id: 'a', color: '#f00', position: 0 },
{ id: 'b', color: '#0000ff80', position: 100 }
]
});
// linear-gradient(90deg, #ff0000 0%, rgba(0, 0, 255, 0.5) 100%)
serializeGradient({
type: 'linear',
angle: 45,
stops: [
{ id: 'a', color: 'rgb(1, 2, 3)', position: 0 },
{ id: 'b', color: 'rgba(10, 20, 30, 0.25)', position: 100 }
]
});
// linear-gradient(45deg, #010203 0%, rgba(10, 20, 30, 0.25) 100%)
Picker edits are emitted through the same canonical model and CSS string. For example, setting a stop to 50% alpha in the picker serializes that stop as rgba(...).
The editor supports these built-in interactions:
EscapeDelete or Backspace outside editable fields to remove the selected stopGuardrails:
parseGradient(value)Parses a supported CSS gradient string into a normalized GradientModel.
const model = parseGradient('linear-gradient(to right, #111111 0%, #eeeeee 100%)');
serializeGradient(model)Serializes a GradientModel into canonical CSS.
const css = serializeGradient({
type: 'conic',
angle: 45,
stops: [
{ id: 'a', color: '#ffaa00', position: 0 },
{ id: 'b', color: '#220044', position: 100 }
]
});
normalizeModel(model)Returns a normalized clone of a model:
from <angle>degdefaultGradientModel(overrides?)Creates a valid default model.
Defaults:
900'ellipse'#ff7a18 0% and #5b4dff 100%cloneGradientModel(model)Returns a normalized deep clone of the gradient model.
modelSignature(model)Returns a JSON string signature of the normalized model. Useful for stable equality checks after normalization.
normalizeColor(value)Accepts supported hex and rgb/rgba values and returns canonical CSS, or null for unsupported input.
normalizeColor('#abc'); // '#aabbcc'
normalizeColor('#fff0'); // 'rgba(255, 255, 255, 0)'
normalizeColor('#abcd'); // 'rgba(170, 187, 204, 0.87)'
normalizeColor('rgba(1, 2, 3, 0.25)'); // 'rgba(1, 2, 3, 0.25)'
normalizeColor('red'); // null
Normalize untrusted gradient strings or models before rendering them. Do not render arbitrary user-provided gradient CSS directly outside the component.
The component only writes normalized gradient values into inline styles. Unsupported color syntax is intentionally rejected and falls back safely, which reduces CSS injection risk.
All user-facing text is consumer-controlled from code. The package does not require any specific i18n library.
<script lang="ts">
import { GradientEditor } from 'svelte-gradient-editor';
import type { GradientEditorMessages } from 'svelte-gradient-editor';
const messages: GradientEditorMessages = {
gradientMode: 'Modo de degradado',
linear: 'Lineal',
conic: 'Conico',
radial: 'Radial',
radialShape: 'Forma radial',
ellipse: 'Elipse',
circle: 'Circulo',
angleDial: 'Control de angulo',
angle: 'Angulo',
gradientTrack: 'Pista de degradado',
gradientStops: 'Paradas del degradado',
editGradientStop: 'Editar parada',
stopColor: 'Color',
position: 'Posicion',
closeColorPicker: 'Cerrar selector de color',
deleteStop: 'Eliminar parada',
stopAriaLabel: ({ position, index, total, color }) =>
`Parada ${index + 1} de ${total}: ${color} en ${position}%`
};
</script>
<GradientEditor bind:value bind:model {messages} />
The component ships with self-contained styles. Override its CSS custom properties on the component or any parent container.
<div
style="
--gradient-editor-text: #e5e7eb;
--gradient-editor-border: rgba(229, 231, 235, 0.2);
--gradient-editor-accent: #60a5fa;
"
>
<GradientEditor bind:value bind:model />
</div>
Supported CSS variables:
--gradient-editor-bg--gradient-editor-panel--gradient-editor-border--gradient-editor-text--gradient-editor-muted--gradient-editor-accent--gradient-editor-shadow--gradient-editor-radiusIf you are generating code against this package, assume these rules:
GradientEditor is the only component export.value and model are both writable bindings.model wins over value when both are provided.change.detail always contains both value and model.rgba(...) alpha stop colors.positionDecimals is set.angleDecimals is set.npm install
npm run dev
npm run test
npm run check
npm run build
Commands:
npm run dev starts the local Vite demonpm run test runs the Vitest suitenpm run check runs svelte-checknpm run build builds the library package into dist/