svelte-gradient-editor Svelte Themes

Svelte Gradient Editor

Svelte Gradient Editor

*** Check the DEMO ***

Standalone Svelte 5 gradient editor component with a typed model API..

Like it? I'd appreciate the support :)

Description

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!

*** Check the DEMO ***

It keeps two representations of the same gradient synchronized:

  • A CSS gradient string for direct use in background-image
  • A structured GradientModel object for application logic

Install

npm install svelte-gradient-editor

Peer dependency:

  • svelte ^5.0.0

Runtime 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.

Quick Start

<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);
  }}
/>

What The Component Does

  • Supports editing linear-gradient(...), conic-gradient(...), and radial-gradient(...)
  • Keeps value and model synchronized after every change
  • Lets users add, move, recolor, and delete gradient stops
  • Uses svelte-awesome-color-picker for stop color editing with hex, rgb, hsv, and alpha controls
  • Lets users switch between linear, conic, and radial gradients
  • Lets users edit linear/conic angle and radial shape
  • Uses the same stop editor abstraction for all supported gradient modes
  • Emits normalized output rather than preserving unsupported input syntax verbatim

Public Exports

The 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';

Component API

Props

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.

Event

change

type GradientEditorChangeDetail = {
  value: string;
  model: GradientModel;
};

The component emits change on every committed editor update.

Bindings

  • bind:value Canonical CSS gradient string suitable for background-image
  • bind:model Canonical structured model synchronized with value

Types

type 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;
};

Behavior Contract

This section is intended to be explicit enough for both humans and coding agents.

Source Of Truth

  • If model is passed, the component uses model as the incoming source of truth.
  • If model is not passed, the component parses value.
  • Internally, the component normalizes the gradient before writing back to value or model.

Normalization

  • Opaque colors are normalized to lowercase 6-digit hex.
  • Alpha colors are normalized to deterministic rgba(r, g, b, a) syntax.
  • Multiline gradient strings and repeated CSS whitespace are accepted and normalized to single-line output.
  • Stop positions are clamped to 0..100.
  • Stops are sorted by position.
  • At least 2 stops always exist after normalization.
  • Missing stop positions are distributed automatically.
  • Linear and conic angles are normalized to a 0..360 degree representation.
  • Radial shape is normalized to either 'circle' or 'ellipse'.

Precision

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.
  • Invalid, negative, or non-finite values fall back to 0.
  • Precision affects component interactions and emitted values.
  • Helper functions like parseGradient() and serializeGradient() keep their own API and are not driven by component prop precision.

Supported Input

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:

  • Degree syntax such as 90deg
  • Named directions such as to right, to top left

Supported radial syntax:

  • Shapes circle and ellipse

Supported conic syntax:

  • conic-gradient(#ff0000 0%, #0000ff 100%)
  • conic-gradient(from 45deg, #ff0000 0%, #0000ff 100%)

Supported stop color syntax:

  • 3-, 4-, 6-, and 8-digit hex colors such as #f00, #f008, #ff0000, and #ff000080
  • rgb(...) and rgba(...) colors supported by colord

The stop color picker exposes hex, rgb, hsv, and alpha editing. HSL is not an editor mode.

Unsupported CSS Features

The package does not preserve full CSS gradient syntax.

Examples of unsupported or only partially supported input:

  • Linear angle units other than degrees, such as turn
  • Conic positioning such as at center
  • Radial positioning such as at center
  • Radial sizing keywords such as closest-side or cover
  • Named colors such as red or transparent
  • HSL and other unsupported CSS color syntaxes

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.

Color Output Examples

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(...).

User Interactions

The editor supports these built-in interactions:

  • Click the gradient track to add a stop
  • Drag a stop handle to move it
  • Click a stop to open its popup editor
  • Edit stop color with the color picker
  • Edit stop position with the numeric input
  • Close the stop popup with its close icon or Escape
  • Delete the selected stop from the popup
  • Press Delete or Backspace outside editable fields to remove the selected stop
  • Switch between linear and radial modes
  • Switch between linear, conic, and radial modes
  • Drag the angle dial or use keyboard input on it

Guardrails:

  • A gradient cannot be reduced below 2 stops
  • Deletion is disabled when only 2 stops remain

Helper Functions

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:

  • stop positions clamped and sorted
  • colors normalized
  • invalid radial shapes corrected
  • conic output canonicalized to from <angle>deg
  • angle rounded to the helper's built-in precision

defaultGradientModel(overrides?)

Creates a valid default model.

Defaults:

  • linear gradients default to angle 90
  • conic gradients default to angle 0
  • radial gradients default to shape 'ellipse'
  • default stops are #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

Security Notes

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.

Messages And Localization

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} />

Theming

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-radius

AI Agent Notes

If 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.
  • Emitted change.detail always contains both value and model.
  • The component only supports linear, conic, and radial gradients.
  • Canonical output uses degree-based linear/conic gradients, hex opaque stop colors, and rgba(...) alpha stop colors.
  • The stop color picker supports hex, rgb, hsv, and alpha editing. HSL is not exposed as a UI mode.
  • Do not rely on unsupported CSS syntax surviving a parse/serialize round-trip.
  • Do not assume decimal stop positions unless positionDecimals is set.
  • Do not assume decimal angles unless angleDecimals is set.
  • The package expects Svelte 5.

Local Development

npm install
npm run dev
npm run test
npm run check
npm run build

Commands:

  • npm run dev starts the local Vite demo
  • npm run test runs the Vitest suite
  • npm run check runs svelte-check
  • npm run build builds the library package into dist/

Top categories

Loading Svelte Themes