Color Picker
Brand and appearance hex color control with swatch, input, and optional reset.
Cross-domain brand color control used on user, team, org, embed, event-type, and onboarding appearance surfaces.
import { ColorPicker } from "@calcom/features/components/color-picker";Ownership
Feature — not @coss/ui/shared. Lives under packages/features/components because it depends on @calcom/i18n, brand hex helpers (isValidHexCode / fallBackHex from @calcom/lib/getBrandColours), and react-colorful. Reuse the Feature import; do not relocate for docs.
Use when
- A Cal surface edits a persisted brand or appearance hex color.
- The user needs the standard swatch popover, hex input, and optional reset action.
- The caller owns validation and persistence;
ColorPickeronly reports text changes.
Do not compose another picker from react-colorful in product code. Import this Feature control instead. For a generic color primitive outside Cal product UI, confirm that this product contract is not required.
Canonical usage
The appearance settings page owns WCAG validation and React Hook Form dirty state around the shared control:
<Controller
name="brandColor"
control={formMethods.control}
defaultValue={defaultBrandColors.light}
render={() => (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<Label render={<div />}>{t("light_brand_color")}</Label>
<ColorPicker
defaultValue={defaultBrandColors.light}
resetDefaultValue={DEFAULT_LIGHT_BRAND_COLOR}
disabled={disabled}
onChange={(value) => {
setLightModeError(!checkWCAGContrastColor("#ffffff", value));
formMethods.setValue("brandColor", value, { shouldDirty: true });
}}
/>
</div>
{lightModeError ? (
<Alert variant="warning">
<TriangleAlertIcon />
<AlertDescription>{t("light_theme_contrast_error")}</AlertDescription>
</Alert>
) : null}
</div>
)}
/>This matches production: ColorPicker is uncontrolled and seeded with defaultValue, so the Controller render ignores field and writes through formMethods.setValue(..., { shouldDirty: true }). Do not invent a controlled value={field.value} wiring unless the product call site does.
Read the complete call site before copying it: validation background differs between light and dark brand colors, and embed usage passes a container for the popover portal.
API
| Prop | Type | Notes |
|---|---|---|
defaultValue | string | Initial hex; invalid values fall back via brand helpers |
onChange | (text: string) => void | Fires on picker or hex input change |
resetDefaultValue | string | When set and current ≠ default, shows reset control |
useDefaultLabel | string | Tooltip for reset (defaults to t("use_default")) |
disabled | boolean | Disables trigger, input, and reset |
container | HTMLElement | Portal container (embed palette) |
popoverAlign | Popover align | Defaults to center |
Call sites
| File | Notes |
|---|---|
apps/web/modules/settings/my-account/appearance-view.tsx | User light/dark brand colors |
apps/web/modules/embed/components/Embed.tsx | Embed palette (container portal) |
apps/web/modules/ee/organizations/appearance.tsx | Org brand colors |
apps/web/modules/ee/teams/views/team-appearance-view.tsx | Team brand colors |
apps/web/modules/onboarding/organization/brand/organization-brand-view.tsx | Org onboarding brand |
apps/web/modules/event-types/components/tabs/appearance/event-appearance-tab.tsx | Event-type brand colors |
Example URLs
Not exhaustive — a few places you can open locally (e.g. http://localhost:3000) to see this in product.
| Path | Notes |
|---|---|
/settings/my-account/appearance | User brand colors |
/settings/organizations/profile | Org appearance (org admin) |
/event-types/<eventTypeId>?tabName=appearance | Event-type brand colors |