packages/react-aria-components/docs/ColorSwatchPicker.mdx
{/* Copyright 2024 Adobe. All rights reserved. This file is licensed to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */}
import {Layout} from '@react-spectrum/docs'; export default Layout;
import docs from 'docs:react-aria-components'; import statelyDocs from 'docs:@react-stately/color'; import {PropTable, HeaderInfo, TypeLink, PageDescription, StateTable, ContextTable} from '@react-spectrum/docs'; import styles from '@react-spectrum/docs/src/docs.css'; import packageData from 'react-aria-components/package.json'; import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; import {Divider} from '@react-spectrum/divider'; import {ExampleList} from '@react-spectrum/docs/src/ExampleList'; import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; import {Keyboard} from '@react-spectrum/text'; import {StarterKits} from '@react-spectrum/docs/src/StarterKits';
<PageDescription>{docs.exports.ColorSwatchPicker.description}</PageDescription>
<HeaderInfo packageData={packageData} componentNames={['ColorSwatchPicker']} sourceData={[ {type: 'W3C', url: 'https://www.w3.org/WAI/ARIA/apg/patterns/listbox/'} ]} />
import {ColorSwatchPicker, ColorSwatchPickerItem, ColorSwatch} from 'react-aria-components';
<ColorSwatchPicker>
<ColorSwatchPickerItem color="#A00"><ColorSwatch /></ColorSwatchPickerItem>
<ColorSwatchPickerItem color="#f80"><ColorSwatch /></ColorSwatchPickerItem>
<ColorSwatchPickerItem color="#080"><ColorSwatch /></ColorSwatchPickerItem>
<ColorSwatchPickerItem color="#08f"><ColorSwatch /></ColorSwatchPickerItem>
<ColorSwatchPickerItem color="#088"><ColorSwatch /></ColorSwatchPickerItem>
<ColorSwatchPickerItem color="#008"><ColorSwatch /></ColorSwatchPickerItem>
</ColorSwatchPicker>
@import "./ColorSwatch.mdx";
@import "./ColorField.mdx";
@import "@react-aria/example-theme";
.react-aria-ColorSwatchPicker {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.react-aria-ColorSwatchPickerItem {
position: relative;
outline: none;
border-radius: 4px;
width: fit-content;
forced-color-adjust: none;
&[data-focus-visible] {
outline: 2px solid var(--focus-ring-color);
outline-offset: 2px;
}
&[data-selected]::after {
content: '';
position: absolute;
inset: 0;
border: 2px solid black;
outline: 2px solid white;
outline-offset: -4px;
border-radius: inherit;
}
}
A ColorSwatchPicker wraps the ListBox component to simplify building color swatch pickers that work with a ColorPicker.
A ColorSwatchPicker consists of a container element, with a list of color swatches inside. Users can select a color by clicking, tapping, or navigating with the keyboard.
import {ColorSwatchPicker, ColorSwatchPickerItem, ColorSwatch} from 'react-aria-components';
<ColorSwatchPicker>
<ColorSwatchPickerItem>
<ColorSwatch />
</ColorSwatchPickerItem>
</ColorSwatchPicker>
ColorSwatchPicker is a convenience wrapper around the ListBox component. If you need additional flexibility, such as support for multiple selection, duplicate colors, drag and drop, etc., you can use the ListBox component directly.
To help kick-start your project, we offer starter kits that include example implementations of all React Aria components with various styling solutions. All components are fully styled, including support for dark mode, high contrast mode, and all UI states. Each starter comes with a pre-configured Storybook that you can experiment with, or use as a starting point for your own component library.
<StarterKits component="colorswatchpicker" />If you will use a ColorSwatchPicker in multiple places in your app, you can wrap all of the pieces into a reusable component. This way, the DOM structure, styling code, and other logic are defined in a single place and reused everywhere to ensure consistency.
This example wraps ColorSwatchPicker and ColorSwatchPickerItem, reusing the MyColorSwatch component from the ColorSwatch docs.
import type {ColorSwatchPickerProps, ColorSwatchPickerItemProps} from 'react-aria-components';
import {MyColorSwatch} from './ColorSwatch';
export function MyColorSwatchPicker({children, ...props}: ColorSwatchPickerProps) {
return (
<ColorSwatchPicker {...props}>
{children}
</ColorSwatchPicker>
);
}
export function MyColorSwatchPickerItem(props: ColorSwatchPickerItemProps) {
return (
<ColorSwatchPickerItem {...props}>
<MyColorSwatch />
</ColorSwatchPickerItem>
);
}
<MyColorSwatchPicker>
<MyColorSwatchPickerItem color="#A00" />
<MyColorSwatchPickerItem color="#f80" />
<MyColorSwatchPickerItem color="#080" />
<MyColorSwatchPickerItem color="#08f" />
<MyColorSwatchPickerItem color="#088" />
<MyColorSwatchPickerItem color="#008" />
</MyColorSwatchPicker>
ColorSwatchPicker accepts either an uncontrolled default value or a controlled value, provided using the defaultValue or value props respectively. The value provided to either of these props should be a color string or <TypeLink links={docs.links} type={docs.exports.Color} /> object. The value is matched against the color of each ColorSwatch, including equivalent colors in different color spaces.
In the example below, the <TypeLink links={docs.links} type={docs.exports.parseColor} /> function is used to parse the initial color from a HSL string so that value's type remains consistent.
import {parseColor} from 'react-aria-components';
function Example() {
let [color, setColor] = React.useState(parseColor('hsl(0, 100%, 33.33%)'));
return (
<MyColorSwatchPicker value={color} onChange={setColor}>
<MyColorSwatchPickerItem color="#A00" />
<MyColorSwatchPickerItem color="#f80" />
<MyColorSwatchPickerItem color="#080" />
</MyColorSwatchPicker>
);
}
Note: ColorSwatches rendered inside a ColorSwatchPicker must have unique colors, even between different color spaces. For example, the values #f00, hsl(0, 100%, 50%), and hsb(0, 100%, 100%) are all equivalent and considered duplicates. This is required so that selection behavior works properly.
By default, ColorSwatchPicker has an aria-label with the localized string "Color swatches". This can be overridden with a more specific accessibility label using the aria-label or aria-labelledby props. All labels should be localized.
<MyColorSwatchPicker aria-label="Fill color">
<MyColorSwatchPickerItem color="#A00" />
<MyColorSwatchPickerItem color="#f80" />
<MyColorSwatchPickerItem color="#080" />
</MyColorSwatchPicker>
ColorSwatchPicker accepts an onChange prop which is triggered whenever the value is edited by the user. It receives a <TypeLink links={docs.links} type={docs.exports.Color} /> object as a parameter.
The example below uses onChange to update a separate element with the color value as a string.
import {parseColor} from 'react-aria-components';
function Example() {
let [value, setValue] = React.useState(parseColor('#A00'));
return (
<div>
<MyColorSwatchPicker value={value} onChange={setValue}>
<MyColorSwatchPickerItem color="#A00" />
<MyColorSwatchPickerItem color="#f80" />
<MyColorSwatchPickerItem color="#080" />
<MyColorSwatchPickerItem color="#08f" />
<MyColorSwatchPickerItem color="#088" />
<MyColorSwatchPickerItem color="#008" />
</MyColorSwatchPicker>
<p>Selected color: {value.toString('rgb')}</p>
</div>
);
}
A ColorSwatchPickerItem can be disabled using the isDisabled prop. Disabled swatches cannot be selected, and are not focusable or interactive.
<MyColorSwatchPicker>
<MyColorSwatchPickerItem color="#A00" />
<MyColorSwatchPickerItem color="#f80" isDisabled />
<MyColorSwatchPickerItem color="#080" />
</MyColorSwatchPicker>
.react-aria-ColorSwatchPickerItem {
&[data-disabled] {
opacity: 0.2;
}
}
By default, ColorSwatchPicker expects items to be arranged horizontally, optionally wrapping to form a grid, and implements keyboard navigation and drag and drop accordingly. The layout prop can be used to display the swatches as a vertical stack instead.
<MyColorSwatchPicker layout="stack">
<MyColorSwatchPickerItem color="#A00" />
<MyColorSwatchPickerItem color="#f80" />
<MyColorSwatchPickerItem color="#080" />
<MyColorSwatchPickerItem color="#08f" />
<MyColorSwatchPickerItem color="#088" />
<MyColorSwatchPickerItem color="#008" />
</MyColorSwatchPicker>
.react-aria-ColorSwatchPicker {
&[data-layout=stack] {
flex-direction: column;
}
}
A <ColorSwatchPickerItem> represents an individual item within a <ColorSwatchPicker>. It should contain a <ColorSwatch>.
React Aria components can be styled in many ways, including using CSS classes, inline styles, utility classes (e.g. Tailwind), CSS-in-JS (e.g. Styled Components), etc. By default, all components include a builtin className attribute which can be targeted using CSS selectors. These follow the react-aria-ComponentName naming convention.
.react-aria-ColorSwatchPicker {
/* ... */
}
A custom className can also be specified on any component. This overrides the default className provided by React Aria with your own.
<ColorSwatchPicker className="my-color-swatch-picker">
</ColorSwatchPicker>
In addition, some components support multiple UI states (e.g. focused, placeholder, readonly, etc.). React Aria components expose states using data attributes, which you can target in CSS selectors. For example:
.react-aria-ColorSwatchPickerItem[data-selected] {
/* ... */
}
The className and style props also accept functions which receive states for styling. This lets you dynamically determine the classes or styles to apply, which is useful when using utility CSS libraries like Tailwind.
<ColorSwatchPickerItem className={({isSelected}) => isSelected ? 'border-black' : 'border-transparent'} />
The states, selectors, and render props for each component used in a ColorSwatchPicker are documented below.
The ColorSwatchPicker component can be targeted with the .react-aria-ColorSwatchPicker CSS selector, or by overriding with a custom className. It supports the following states:
The ColorSwatchPickerItem component can be targeted with the .react-aria-ColorSwatchPickerItem CSS selector, or by overriding with a custom className. It supports the following states:
The ColorSwatch component can be targeted with the .react-aria-ColorSwatch CSS selector, or by overriding with a custom className. It supports the following states:
If you need to customize one of the components within a ColorSwatchPicker, such as ColorSwatchPickerItem or ColorSwatch, in many cases you can create a wrapper component. This lets you customize the props passed to the component.
function MyColorSwatchPickerItem(props) {
return <ColorSwatchPickerItem {...props} className="my-swatch" />
}
All React Aria Components export a corresponding context that can be used to send props to them from a parent element. This enables you to build your own compositional APIs similar to those found in React Aria Components itself. You can send any prop or ref via context that you could pass to the corresponding component. The local props and ref on the component are merged with the ones passed via context, with the local props taking precedence (following the rules documented in mergeProps).
<ContextTable components={['ColorSwatchPicker']} docs={docs} />
This example shows how to synchronize a ColorSwatchPicker and a ColorField via context.
import {ColorSwatchPickerContext} from 'react-aria-components';
import {MyColorField} from './ColorField';
function ColorSelector({children}) {
let [value, setValue] = React.useState(parseColor('#A00'));
return (
<div style={{display: 'flex', flexDirection: 'column', gap: 8}}>
<MyColorField label="Color" value={value} onChange={setValue} />
<ColorSwatchPickerContext.Provider value={{value, onChange: setValue}}>
{children}
</ColorSwatchPickerContext.Provider>
</div>
);
}
<ColorSelector>
<MyColorSwatchPicker>
<MyColorSwatchPickerItem color="#A00" />
<MyColorSwatchPickerItem color="#f80" />
<MyColorSwatchPickerItem color="#080" />
</MyColorSwatchPicker>
</ColorSelector>