Source Code

<div class="gp-card">
  <div class="gp-preview" id="gpPreview"></div>
  <div class="gp-bar" id="gpBar">
    <div class="gp-stops" id="gpStops"></div>
  </div>
  <div class="gp-row">
    <label class="gp-angle">Angle <strong id="gpAngVal">90°</strong>
      <input type="range" id="gpAngle" min="0" max="360" value="90">
    </label>
    <input type="color" id="gpColor" value="#6366f1" aria-label="Stop color">
    <button type="button" class="gp-del" id="gpDel" title="Delete stop">Delete</button>
  </div>
  <code class="gp-css" id="gpCss"></code>
</div>

Gradient Picker — CSS Linear Gradient Builder

Gradient Picker · Forms · Plain HTML, CSS & JS · Live preview

What's included

Features

Draggable stops
Pointer-dragged handles with document-level tracking.
Click to add
Clicking the bar inserts a stop at that position.
Selected-stop color
The color input edits exactly the chosen handle.
Delete stops
Remove a stop while keeping at least two.
Angle control
A slider sets the output gradient angle 0–360°.
Live CSS output
Copyable linear-gradient string updates instantly.
Position-sorted
Stops sort by position so dragging past each other works.
No library
Pure HTML/CSS/JS — no color or gradient dependency.

About this UI Snippet

Gradient Picker — Draggable Color Stops and Angle to CSS

Screenshot of the Gradient Picker snippet rendered live

A gradient picker lets you build a CSS linear-gradient visually — drag colour stops along a bar, pick each stop's colour, set the angle, and copy the result. It's the core of any theme editor or design tool's gradient control. This snippet builds a complete one with draggable stops, click-to-add, delete, and live output, in plain HTML, CSS, and vanilla JavaScript.

Stops as data, rendered to a bar

The gradient is an array of { pos, color } stops. The editor renders a preview, a gradient bar (always shown at 90° so the stop positions map directly to the bar's left-to-right axis regardless of the chosen output angle), and a draggable handle per stop. Sorting the stops by position when building the CSS means you can drag stops past each other freely without breaking the output.

Drag, add, and delete

Dragging a handle updates its position live via Pointer Events with document-level tracking (so the drag continues outside the bar). Clicking an empty part of the bar adds a new stop there, inheriting the selected colour, and a delete button removes the selected stop (keeping a minimum of two). The selected stop is highlighted and bound to the colour input, so picking a colour recolours exactly the handle you're working on.

Angle and live CSS

A range slider sets the gradient angle (0–360°) for the output, shown live on the preview while the editing bar stays horizontal for predictable stop placement. The generated background: linear-gradient(…) string updates on every change and is one click to copy — paste-ready CSS, no manual typing of percentages.

A checkerboard for transparency

The bar sits on a CSS checkerboard so semi-transparent stops read correctly (if you extend it to support alpha), the same affordance every colour tool uses. The preview shows the real angled gradient as it will appear in your UI.

Self-contained and portable

The whole picker is the stops array, a render function, and pointer handlers, with no dependencies. Swap the initial stops, restyle the handles, or wire the output into your theme state — it's a clean reference for the gradient-editor pattern that design tools and CSS generators are built around.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Rather than tracing the drag math yourself, feed this snippet's HTML, CSS, and JS to an AI coding assistant like Claude and ask it to explain why the editing bar's cssGradient call is hardcoded to 90 degrees while the preview uses the angle variable, and how that separation keeps the stop percentages meaningful regardless of the chosen output angle. The same assistant can help optimize it — ask whether rebuilding the entire stopsWrap innerHTML on every pointermove during a drag is wasteful compared to just repositioning the dragged handle's own style.left, especially with many stops. It's also useful for extending the tool: ask it to add support for radial gradients alongside linear, add an alpha channel with a checkerboard-aware color input, or let users type a raw CSS gradient string and parse it back into the stops array. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a draggable CSS linear-gradient picker in plain HTML, CSS, and vanilla JavaScript, no color-picker library, using the Pointer Events API for dragging.

Requirements:
- Maintain gradient state as an array of stop objects, each with a position percentage (0-100) and a hex color string, plus a separate numeric angle value (0-360) for the output gradient's direction.
- Render a preview element whose background is the real gradient at the chosen angle, and a separate editing bar whose background is always rendered at a fixed 90 degrees regardless of the chosen output angle, so a stop's horizontal position on the bar always maps directly to its percentage.
- Each stop must render as a small circular handle positioned with left equal to its percentage, draggable via pointerdown starting a document-level pointermove/pointerup listener pair (not just listeners on the handle itself, so the drag continues even if the pointer leaves the handle), computing the new position by clamping (clientX - barLeft) / barWidth * 100 between 0 and 100.
- Clicking an empty part of the bar (not an existing handle) must insert a new stop at that position, inheriting the currently selected stop's color, and select the new stop.
- A color input must always reflect and edit the currently selected stop's color, and a delete button must remove the selected stop but refuse to go below two stops.
- Before building the final CSS string, sort the stops by position so stops can be dragged past each other without producing invalid or reordered gradient syntax.
- Display the generated linear-gradient CSS declaration as text, and copy it to the clipboard when clicked.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA gradient picker renders with two stops and a preview.
  2. 2
    Drag the stopsMove handles along the bar to reposition colors.
  3. 3
    Add and recolorClick the bar to add a stop; use the color input to recolor the selected one.
  4. 4
    Set the angleDrag the angle slider to rotate the gradient.
  5. 5
    Delete a stopRemove the selected stop (a minimum of two remain).
  6. 6
    Copy the CSSClick the output to copy the linear-gradient declaration.

Real-world uses

Common Use Cases

Theme editors
Author gradients for buttons and heroes, then blend with a color mixer.
Design tools
A gradient control in a builder or settings panel.
Hero and background design
Craft a gradient mesh hero base.
Button and badge styling
Generate gradients for a gradient button.
Brand palette work
Define brand gradients to reuse across a site.
Learning gradients
A reference for stop math and pointer dragging.
Related: Price Range Slider
See the Price Range Slider for a related forms pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

The bar represents stop positions from 0% to 100% along the gradient line, which is one-dimensional regardless of angle. Keeping the bar at 90° (horizontal) means a handle's left position maps directly to its percentage, so dragging is predictable. The angle only affects the output and the preview, where the real rotated gradient is shown — separating editing axis from display angle is how gradient tools stay usable.

Click an empty part of the gradient bar to add a stop at that position (it inherits the currently selected color), and use the delete button to remove the selected stop. A minimum of two stops is enforced because a gradient needs at least two colors. The selected stop is highlighted and bound to the color input so recoloring affects the right handle.

Yes. You can drag any stop past another freely; when building the CSS the stops are sorted by position, so the output is always valid even if the array order does not match the visual order. This lets you reorder colors by dragging without any special handling.

The picker outputs a ready-to-paste declaration like background: linear-gradient(90deg, #6366f1 0%, #22d3ee 100%); and clicking it copies it to the clipboard. You can paste that into CSS, a style attribute, or a CSS-in-JS value. To bind it to app state instead, read the stops array and angle and build the string yourself.

Hold the stops array, angle, and selected index in state and render the bar, handles, preview, and CSS string from them. Move the pointer drag into handlers that update state (attach window listeners in an effect and clean them up). Emit the gradient string via a callback or v-model so a parent theme store can consume it. Tailwind users swap the classes for utilities.