Skip to content

Add the FieldContrastColorPicker as a form field #567

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: stable-3_5_0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/components/Form/FormGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import FieldArchivingPn from './fields/FieldArchivingPn.vue';
import FieldAutosuggestPreset from './fields/FieldAutosuggestPreset.vue';
import FieldBaseAutosuggest from './fields/FieldBaseAutosuggest.vue';
import FieldColor from './fields/FieldColor.vue';
import FieldContrastColorPicker from './fields/FieldContrastColorPicker.vue';
import FieldControlledVocab from './fields/FieldControlledVocab.vue';
import FieldPubId from './fields/FieldPubId.vue';
import FieldHtml from './fields/FieldHtml.vue';
Expand Down Expand Up @@ -92,6 +93,7 @@ export default {
FieldAutosuggestPreset,
FieldBaseAutosuggest,
FieldColor,
FieldContrastColorPicker,
FieldControlledVocab,
FieldPubId,
FieldHtml,
Expand Down
81 changes: 81 additions & 0 deletions src/components/Form/fields/FieldContrastColorPicker.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {Primary, Controls, Stories, Meta} from '@storybook/blocks';
import * as FieldContrastColorPickerStories from './FieldContrastColorPicker.stories.js';

<Meta of={FieldContrastColorPickerStories} />

# FieldContrastColorPicker

This component provides a dual color picker interface for selecting a color pair with optimal contrast. It's specifically designed for theme configuration where text readability is important.

## Usage

Use this component when you need users to select two colors that work well together for accessibility purposes, such as text color against a background. The component:

- Allows selection of two colors (typically primary/background and secondary/text colors)
- Displays the calculated contrast ratio between the selected colors
- Shows visual indicators for WCAG accessibility compliance levels
- Provides keyboard accessibility for color selection
- Includes a link to documentation about contrast ratios and accessibility

## Why Contrast Matters

Color contrast is a critical aspect of web accessibility. Insufficient contrast between text and background colors can make content difficult or impossible to read for many users, including those with:

- Low vision
- Color blindness
- Age-related vision changes
- Situational limitations (e.g., bright sunlight on screens)

The Web Content Accessibility Guidelines (WCAG) establish minimum contrast ratios to ensure readability:

- **3:1** - Minimum for large text (18pt+) at Level AA
- **4.5:1** - Minimum for normal text at Level AA
- **7:1** - Enhanced contrast for normal text at Level AAA

The component visually indicates whether selected colors meet these standards, helping content creators ensure their designs are accessible to all users.

## Features

- Two color pickers with hex color selection
- Live preview of text appearance with the selected colors
- Real-time contrast ratio calculation
- Visual indicators for WCAG accessibility compliance levels
- Keyboard navigation support for better accessibility
- Documentation link for learning more about contrast requirements

## Technical Details

### Data Structure

The component stores both colors in a JSON string format:

```json
{
"color1": "#1E6292",
"color2": "#FFFFFF"
}
```

### WCAG Compliance

The component uses the WCAG formula to calculate luminance and contrast between colors:

- Relative luminance is calculated using the formula specified in WCAG 2.0
- Contrast ratio is calculated as (L1 + 0.05) / (L2 + 0.05), where L1 is the lighter color's luminance and L2 is the darker color's luminance

### Keyboard Accessibility

The component supports keyboard navigation for color selection:

- Arrow keys: Adjust saturation and brightness
- Page Up/Down: Adjust hue
- Tab: Navigate between color pickers and other interactive elements

### Events

- `change`: Emitted when either color changes, with the stringified JSON value
- `update:contrastColor`: Emitted when the second color is updated

<Primary />
<Controls />
<Stories />
105 changes: 105 additions & 0 deletions src/components/Form/fields/FieldContrastColorPicker.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import FieldContrastColorPicker from './FieldContrastColorPicker.vue';
import FieldBaseMock from '../mocks/field-base';
import FieldContrastColorPickerMock from '../mocks/field-contrast-color-picker';

export default {
title: 'Forms/FieldContrastColorPicker',
component: FieldContrastColorPicker,
render: (args) => ({
components: {FieldContrastColorPicker},
setup() {
function change(name, prop, newValue, localeKey) {
if (localeKey) {
args[prop][localeKey] = newValue;
} else {
args[prop] = newValue;
}
console.log('Value changed:', newValue);
}

function updateContrastColor(color) {
console.log('Contrast color selected:', color);
}

return {args, change, updateContrastColor};
},
template: `
<FieldContrastColorPicker
v-bind="args"
@change="change"
@update:contrastColor="updateContrastColor"
/>
`,
}),
parameters: {
docs: {
description: {
component:
'A color picker component that helps users select color combinations with sufficient contrast for accessibility compliance. Includes accessibility features like keyboard navigation and WCAG contrast validation.',
},
},
},
};

export const Base = {
args: {...FieldBaseMock, ...FieldContrastColorPickerMock},
};

export const Required = {
args: {
...Base.args,
isRequired: true,
},
};

export const WithTooltip = {
args: {
...Base.args,
tooltip:
'Select colors with a contrast ratio of at least 4.5:1 for normal text to ensure readability.',
},
};

export const WithDescription = {
args: {
...Base.args,
description:
'This color picker helps ensure your text is readable by checking contrast against WCAG standards. For more information, click the link below the contrast categories.',
},
};

export const WithHighContrast = {
args: {
...Base.args,
value: JSON.stringify({
color1: '#000000',
color2: '#ffffff',
}),
},
parameters: {
docs: {
description: {
story:
'Example with maximum contrast (black and white), which passes all WCAG accessibility levels.',
},
},
},
};

export const WithLowContrast = {
args: {
...Base.args,
value: JSON.stringify({
color1: '#777777',
color2: '#999999',
}),
},
parameters: {
docs: {
description: {
story:
'Example with insufficient contrast that fails to meet WCAG accessibility levels, showing the visual indicators for failed tests.',
},
},
},
};
Loading