130 Widgets

Building tools. Learning to build tools. Learning to build learning tools.

6. The Color Picker UI

Building a QuickPick with named presets, live swatches, and custom hue input.

The QuickPick API

VS Code’s showQuickPick is the standard way to present a list of choices to the user. It’s the same widget behind the Command Palette, file switcher, and symbol search. For Color Identity, it becomes a color picker with 14 named presets, an “automatic” option, and a custom hue entry.

The function signature tells the story:

export async function showColorPicker(
    currentHue: number,
    themeProfile: ThemeProfile,
    swatchDir: string
): Promise<{ hue: number | null } | undefined>

Three possible return values:

Color Presets

export const COLOR_PRESETS: ColorPreset[] = [
    { label: 'Red',         hue: 0   },
    { label: 'Orange',      hue: 30  },
    { label: 'Yellow',      hue: 55  },
    { label: 'Lime',        hue: 80  },
    { label: 'Green',       hue: 120 },
    { label: 'Mint',        hue: 150 },
    { label: 'Teal',        hue: 175 },
    { label: 'Cyan',        hue: 190 },
    { label: 'Blue',        hue: 220 },
    { label: 'Indigo',      hue: 245 },
    { label: 'Purple',      hue: 270 },
    { label: 'Magenta',     hue: 300 },
    { label: 'Pink',        hue: 330 },
    { label: 'Rose',        hue: 350 },
];

Fourteen presets spaced around the color wheel. The spacing isn’t uniform — there are more entries in the warm range (Red through Yellow, 0–55°) and the cool range (Teal through Indigo, 175–245°) because those regions contain more perceptually distinct colors. The gap between Green (120°) and Mint (150°) is wider because that region of the wheel has less visual variety.

Building the Picker Items

Each preset becomes a QuickPickItem with an icon, description, and optional “currently selected” indicator:

for (const preset of COLOR_PRESETS) {
    const previewHex = hslToHex(
        preset.hue,
        themeProfile.baseSaturation,
        themeProfile.baseLightness
    );
    const swatchUri = generateColorSwatch(swatchDir, previewHex);

    items.push({
        label: preset.label,
        description: `hue ${preset.hue}°  ·  ${previewHex}`,
        detail: currentHue === preset.hue
            ? '$(check) Currently selected' : undefined,
        iconPath: swatchUri,
        _hue: preset.hue,
    });
}

Several things happening here:

VS Code Concept

QuickPickItem supports label, description (shown to the right of the label, muted), and detail (shown below the label, smaller). The kind property can be set to QuickPickItemKind.Separator to add visual dividers between groups of items. Color Identity uses separators to set apart the “Automatic” option, the color presets, and the “Custom Hue” option.

Custom Hue Input

If the user picks “Custom Hue…”, the flow chains into an input box:

if ((picked as PickItem)._action === 'custom') {
    const input = await vscode.window.showInputBox({
        title: 'Color Identity: Custom Hue',
        prompt: 'Enter a hue value (0 = red, 120 = green, 240 = blue)',
        value: String(currentHue),
        validateInput: (value) => {
            const n = Number(value);
            if (isNaN(n) || n < 0 || n > 360) {
                return 'Please enter a number between 0 and 360';
            }
            return undefined;
        },
    });
    if (input === undefined) {
        return undefined;
    }
    return { hue: Number(input) };
}

The validateInput callback runs on every keystroke and shows an inline error message if the input is invalid. The user can’t submit an invalid value — the OK button is disabled while the validation message is visible. The value field pre-populates the input with the current hue, so the user can adjust from their current position rather than starting from scratch.

Tip

Notice the flow: QuickPick → InputBox is a two-step interaction. The user first picks from a list, and only sees the text input if they explicitly choose the custom option. This keeps the common case (picking a preset) fast, while still supporting power users who want precise control.

Checkpoint

The color picker is a complete UI component: 14 theme-aware presets with swatch icons, an automatic option, and a validated custom input. But where do those swatch PNG files come from? That’s the most surprising part of the whole extension.