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-component
Peer dependency:
svelte ^5.0.0<script lang="ts">
import { GradientEditor } from 'svelte-gradient-editor-component';
import type { GradientEditorMessages, GradientModel } from 'svelte-gradient-editor-component';
let value = 'linear-gradient(90deg, #ff0000 0%, #0000ff 100%)';
let model: GradientModel | undefined;
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
angleDecimals={0}
positionDecimals={1}
{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 changeThe package entry point exports:
import {
GradientEditor,
cloneGradientModel,
defaultGradientModel,
modelSignature,
normalizeHexColor,
normalizeModel,
parseGradient,
serializeGradient
} from 'svelte-gradient-editor-component';
import type {
ConicGradientModel,
GradientEditorMessages,
GradientModel,
GradientStop,
LinearGradientModel,
RadialGradientModel
} from 'svelte-gradient-editor-component';
| 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;
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.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(...)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%)The package does not preserve full CSS gradient syntax.
Examples of unsupported or only partially supported input:
turnat centerat centerclosest-side or coverrgb(...)Conic 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.
The editor supports these built-in interactions:
Delete or Backspace outside editable fields to remove the selected stopEscape to close the stop popupGuardrails:
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.
normalizeHexColor(value)Accepts 3-digit or 6-digit hex values and returns normalized lowercase 6-digit hex, or null for unsupported input.
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-component';
import type { GradientEditorMessages } from 'svelte-gradient-editor-component';
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',
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.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/