Components
Color picker
A colour picked from a palette, dragged out of a saturation and brightness area, or typed, for theme settings and brand colours. Its value is CSS text in the format it is set to.
Drawn and behaving as Flux UI's color picker does, measured against its live docs.
Color picker
The swatch button opens the popover; a typed colour is taken on Enter or on leaving the input.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ColorPickerExample : Component
{
private string _theme = "#bada55";
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.ColorPicker.Value(_theme).OnChange(color => { _theme = color; }).Id("ui-color-picker-intro"),
Ui.Text.Data("testid", "ui-color-picker-state")[$"Theme: {(_theme.Length == 0 ? "none" : _theme)}."]
];
}Theme: #bada55.
Format
One colour written six ways. An alpha format adds an opacity slider to the popover.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ColorPickerFormatExample : Component
{
// One colour, written six ways: each picker writes it back in its own format.
private const string Indigo = "rgb(99 102 241 / 0.5)";
protected override Component? Render() =>
Div.Class("grid max-w-xl gap-4 sm:grid-cols-2")[
Ui.ColorPicker.Value(Indigo).Format(Ui.ColorPickerFormat.Hex),
Ui.ColorPicker.Value(Indigo).Format(Ui.ColorPickerFormat.Hexa),
Ui.ColorPicker.Value(Indigo).Format(Ui.ColorPickerFormat.Rgb),
Ui.ColorPicker.Value(Indigo).Format(Ui.ColorPickerFormat.Rgba),
Ui.ColorPicker.Value(Indigo).Format(Ui.ColorPickerFormat.Hsl),
Ui.ColorPicker.Value(Indigo).Format(Ui.ColorPickerFormat.Hsla)
];
}Sizes
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ColorPickerSizesExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs space-y-4")[
Ui.ColorPicker.Value("#c0ffee"),
Ui.ColorPicker.Value("#c0ffee").Sm,
Ui.ColorPicker.Value("#c0ffee").Xs
];
}Button trigger
One button that shows the colour, for a toolbar or a palette where nothing is typed.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ColorPickerButtonExample : Component
{
protected override Component? Render() =>
Ui.ColorPicker.Value("#ca1ebb").Type(Ui.ColorPickerType.Button);
}Custom swatches
Your palette in place of the 24 Tailwind colours: strings, or (value, label) pairs.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ColorPickerSwatchesExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs space-y-4")[
Ui.ColorPicker.Value("#ef4444").Swatches(["#ef4444", "#22c55e", "#3b82f6", "#f59e0b", "#a855f7"]),
// With a label for each swatch, which assistive tech reads.
Ui.ColorPicker.Value("#ef4444").Swatches([("#ef4444", "Red"), ("#22c55e", "Green"), ("#3b82f6", "Blue")])
];
}No swatches
An empty list hides the palette, Flux's :swatches="false": the area, the hue slider and the input.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ColorPickerNoSwatchesExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.ColorPicker.Value("#ef4444").Swatches([])
];
}Custom layout
Children compose the popover from its parts, in any order.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ColorPickerLayoutExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.ColorPicker.Value("#facade")[
Div.Class("flex flex-col gap-3")[
Ui.ColorPickerInput.Placeholder("#000000"),
Ui.Separator,
Ui.ColorPickerArea,
Ui.ColorPickerSlider.Channel(Ui.ColorPickerChannel.Hue)
]
]
];
}Dropper
A button that picks a colour from anywhere on the screen, there only in a browser with an EyeDropper.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ColorPickerDropperExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.ColorPicker.Value("#dec0de").Dropper()
];
}Clearable
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ColorPickerClearableExample : Component
{
private string _color = "#deface";
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.ColorPicker.Value(_color).OnChange(color => { _color = color; }).Clearable(),
Ui.Text.Data("testid", "ui-color-picker-cleared")[$"Color: {(_color.Length == 0 ? "none" : _color)}."]
];
}Color: #deface.
Copyable
One click copies the colour as it is written.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ColorPickerCopyableExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.ColorPicker.Value("#0ff1ce").Copyable()
];
}Disabled
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ColorPickerDisabledExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.ColorPicker.Value("#beefed").Disabled()
];
}In a form
Bound in a form, the colour travels with the save, and text that is no colour holds the save back.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ColorPickerFormExample : Component
{
private readonly Brand _brand = new();
private string _saved = "nothing yet";
protected override Component? Render() =>
Form.Model(_brand).OnSubmit(_ => { _saved = _brand.Accent; }).Class("flex max-w-xs flex-col items-start gap-3")[
Ui.ColorPicker.Bind(() => _brand.Accent).Label("Accent").Description("Saved with the form.").Id("ui-color-picker-accent"),
Ui.Button.Primary.Type(Ui.ButtonType.Submit)["Save"],
Ui.Text.Data("testid", "ui-color-picker-saved")[$"Saved: {_saved}."]
];
public sealed class Brand
{
public string Accent { get; set; } = "#6366f1";
}
}Behaviour
Its value
The value is CSS text in Format, whatever CSS it was given in, and no colour is the empty string. A bound colour is written with the reader's next action, .Live() writes it as it is chosen, and under a pointer every change is the page's at once. Name adds a hidden input for a plain form post.
The keyboard
The swatch button, the button trigger or ArrowDown in the input opens the popover and focuses the area; Escape and a press outside close it and the focus returns.
Key | What happens |
|---|---|
| The arrows, on the area | Move the thumb by 2, and by 10 with Shift. |
| PageUp, PageDown, Home, End, on the area | Full and no brightness; no and full saturation. |
| The arrows, on a slider | Step by 1, by 10 with Shift, and by a tenth of the track with a page key. |
| The arrows, on the palette | Walk it eight to a row; Enter or Space picks. The palette is one tab stop. |
| Enter, or leaving the input | Takes a typed colour. |
On a phone it is a sheet
Below sm the popover is centred over the page, which does not scroll behind it, and it keeps what is chosen until Select: Cancel, Escape and a press outside leave the colour as it was. A swatch does not wait: it is taken, and the sheet closes.
What a typed colour becomes
Hex, a CSS colour name, rgb(), hsl() and hwb() are read as CSS reads them. Flux hands every other form to the browser and reads the numbers of the colour the browser writes back as red, green and blue, so the wide-gamut functions come out as something else: oklch(0.7 0.1 200) is #0100c8. The kit reproduces it digit for digit, by the owner's decision.
Text that is no colour is refused, not put back, which is the one departure from Flux, and only in a form: a bound picker keeps #12 in the field, says "Enter a valid color." under it with the next action and holds the save. Outside a form it puts the last colour back, as Flux does.
No script of the kit's
The popover is a native modal <dialog> the button opens by command="show-modal", hung under the trigger by CSS anchor rules. The area and each track are data-rask-drag surfaces, so the thumb is under the pointer every frame; the sliders hold native range inputs for the keyboard; a press on the area leaves the focus where it was (data-rask-press-keeps-focus); the dropper shows where the browser has one (data-rask-requires); and the copy button writes the clipboard in the click (data-rask-copy). The colour maths and every key are C#.
Reference
Ui.ColorPicker
A colour from a palette, an area and sliders, or typed. Its children, when it has any, compose the popover.
Step | Takes | What it does |
|---|---|---|
.Bind(…) | () => a string | The member it reads and writes: () => m.Theme. Or Value with OnChange. |
.Value(…) | string | The colour as any CSS colour string, for a page that holds it itself. |
.OnChange(…) | a handler given the new value | Runs with the new colour, in Format. |
.AfterBind(…) | a handler given the new value | Runs after Bind has written the model. |
.Validate(…) | a validator | A rule the colour is checked by when it is written. |
.Live() | bool | Writes the model as soon as a colour is chosen, not with the next action. |
.Format(…) | Ui.ColorPickerFormatHex Hexa Rgb Rgba Hsl Hsla Each is a step of its own too, unless an HTML tag has its name. | What the colour is written as. An alpha format adds an alpha slider. Hex when unset. |
.Type(…) | Ui.ColorPickerTypeInput Button Each is a step of its own too, unless an HTML tag has its name. | An input with a swatch button, or one button showing the colour. The input when unset. |
.Swatches(…) | hex strings, or (value, label) pairs | The palette in place of the 24 Tailwind colours. An empty list shows none. |
.Dropper() | bool | A button in the popover that picks a colour from the screen, where the browser has one. |
.Clearable() | bool | A button on the input that clears the colour, while there is one. |
.Copyable() | bool | A button on the input that copies the colour as it is written. |
.Placeholder(…) | string | Shown while there is no colour. By format when unset: #000000 for hex. |
.Size(…) | Ui.ColorPickerSizeBase Sm Xs Each is a step of its own too, unless an HTML tag has its name. | How tall the trigger is. 40px when unset. |
.Label(…) | string | Draws a field around it, with this label and the error. |
.Description(…) | string | Help text between the label and the picker. |
.Name(…) | string | Adds a hidden input of this name, so a plain form posts the colour. |
.Disabled() | bool | A picker that cannot be opened or changed. |
.Invalid() | bool | Error styling the form did not ask for. |
Ui.ColorPickerArea
The saturation and brightness area of a composed popover.
No steps of its own: what goes inside it is its content.
Ui.ColorPickerSlider
The hue or the opacity slider of a composed popover.
Step | Takes | What it does |
|---|---|---|
.Channel(…) | Ui.ColorPickerChannelHue Alpha Each is a step of its own too, unless an HTML tag has its name. | What the slider moves. The hue when unset. |
Ui.ColorPickerInput
The text input of a composed popover.
Step | Takes | What it does |
|---|---|---|
.Placeholder(…) | string | Shown while there is no colour. The picker's own when unset. |
Ui.ColorPickerSwatches
The palette of a composed popover: the picker's own, or the Ui.ColorPickerSwatch children it is given.
No steps of its own: what goes inside it is its content.
Ui.ColorPickerSwatch
One colour of a palette.
Step | Takes | What it does |
|---|---|---|
.Value(…) | string | The colour, as a CSS colour string: #ef4444. |
.Label(…) | string | What assistive tech calls it, and its tooltip. The value when unset. |
Ui.ColorPickerDropper
The dropper button of a composed popover, there only where the browser has an EyeDropper.
No steps of its own: what goes inside it is its content.