-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: story page to check color contrasts
- Loading branch information
Showing
6 changed files
with
451 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { Canvas, Meta } from '@storybook/addon-docs' | ||
import * as ContrastCheck from './contrastCheck.stories' | ||
|
||
<Meta of={ContrastCheck} /> | ||
|
||
# Contrast check | ||
|
||
## Usage | ||
|
||
```typescript | ||
import { getThemeContrastReport, checkColorContrast } from '@spark-ui/theme-utils' | ||
``` | ||
|
||
### getThemeContrastReport | ||
|
||
Use `getThemeContrastReport(theme)` to get a detailed report of the contrast ratio for each pair of colors in the theme. | ||
|
||
For exemple `main` colored background is supposed to have `onMain` colored text. The pair `main/onMain` must provide sufficient contrast ratio to pass [SC 1.4.3 - Contrast (Minimum) (Level AA)](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html). | ||
Same goes for every other color pair in a Spark theme. | ||
|
||
Each pair is tested for normal text size and larger text size, as they both have different contrast requirements. | ||
|
||
Score can be: `Failed`, `AA` or `AAA`. | ||
|
||
```typescript | ||
import { checkColorContrast } from '@spark-ui/theme-utils' | ||
|
||
const report = getThemeContrastReport(theme) | ||
``` | ||
|
||
Report looks like this: | ||
|
||
```json | ||
// Exemple for the main color | ||
{ | ||
"main": { | ||
"previewStyles": "bg-main text-on-main", | ||
"small": { | ||
"contrastRatio": "3.48", | ||
"score": "Failed", | ||
"textSize": "Small", | ||
"colors": ["#EC5A13", "#FFFFFF"] | ||
}, | ||
"large": { | ||
"contrastRatio": "3.48", | ||
"score": "Failed", | ||
"textSize": "Small", | ||
"colors": ["#EC5A13", "#FFFFFF"] | ||
} | ||
} | ||
// other colors... | ||
} | ||
``` | ||
|
||
### checkColorContrast | ||
|
||
Use `checkColorContrast` to compare two colors given a font-size. | ||
|
||
```typescript | ||
import { checkColorContrast } from '@spark-ui/theme-utils' | ||
|
||
const LARGE_FONT_SIZE = 24 | ||
|
||
checkColorContrast('#EC5A13', '#FFFFFF', LARGE_FONT_SIZE) // { "contrastRatio": "3.48", "score": "Failed", "textSize": "Large", "colors": ["#EC5A13", "#FFFFFF"] } | ||
``` | ||
|
||
## Spark theme contrasts | ||
|
||
This section displays card with all color schemes offered by Spark themes (default and dark). | ||
|
||
It can be used by Axe dev tools or Playwright for an a11y color check. | ||
|
||
<Canvas of={ContrastCheck.Default} /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { Icon } from '@spark-ui/icon' | ||
import { Block } from '@spark-ui/icons/dist/icons/Block' | ||
import { Check } from '@spark-ui/icons/dist/icons/Check' | ||
import { Tabs } from '@spark-ui/tabs' | ||
import { Tag } from '@spark-ui/tag' | ||
import { Meta, StoryFn } from '@storybook/react' | ||
import { cx } from 'class-variance-authority' | ||
import { ReactNode } from 'react' | ||
|
||
import { getThemeContrastReport } from './contrastCheck' | ||
import { defaultTheme } from './defaultTheme' | ||
import { defaultThemeDark } from './defaultThemeDark' | ||
import { type Theme } from './types' | ||
|
||
const meta: Meta = { | ||
title: 'Utils/theme-utils/contrast check', | ||
} | ||
|
||
export default meta | ||
|
||
const ScoreTag = ({ score }: { score: string }) => { | ||
const isSuccess = score.includes('AA') | ||
|
||
return ( | ||
<Tag design="tinted" intent={isSuccess ? 'success' : 'danger'}> | ||
{score} | ||
<Icon>{isSuccess ? <Check /> : <Block />}</Icon> | ||
</Tag> | ||
) | ||
} | ||
|
||
const Cell = ({ className, children }: { className?: string; children: ReactNode }) => { | ||
return <td className={cx('border-sm border-outline p-md', className)}>{children}</td> | ||
} | ||
|
||
const ThemeReport = ({ theme, ...rest }: { theme: Theme }) => { | ||
const report = getThemeContrastReport(theme) | ||
|
||
return ( | ||
<table className="table-auto bg-surface text-on-surface" {...rest}> | ||
<thead className="sticky"> | ||
<tr> | ||
<th className="p-sm text-left">Color</th> | ||
<th className="p-sm text-left">Preview</th> | ||
<th className="p-sm text-left">Ratio</th> | ||
<th className="p-sm text-left">Score (small text)</th> | ||
<th className="p-sm text-left">Score (large text)</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{Object.entries(report).map(([color, result]) => { | ||
return ( | ||
<tr> | ||
<Cell>{color}</Cell> | ||
<Cell className={cx('border-current px-lg', result.previewStyles)}> | ||
<span className="text-body-1">small, </span> | ||
<span className="text-headline-1">large</span> | ||
</Cell> | ||
<Cell>{result.small.contrastRatio}</Cell> | ||
<Cell> | ||
<ScoreTag score={result.small.score} /> | ||
</Cell> | ||
<Cell> | ||
<ScoreTag score={result.large.score} /> | ||
</Cell> | ||
</tr> | ||
) | ||
})} | ||
</tbody> | ||
</table> | ||
) | ||
} | ||
|
||
export const Default: StoryFn = _args => ( | ||
<div className="flex"> | ||
<Tabs defaultValue="tab1"> | ||
<Tabs.List> | ||
<Tabs.Trigger value="tab1"> | ||
<span>Default theme</span> | ||
</Tabs.Trigger> | ||
<Tabs.Trigger value="tab2"> | ||
<span>Dark theme</span> | ||
</Tabs.Trigger> | ||
</Tabs.List> | ||
|
||
<Tabs.Content value="tab1" data-theme="default"> | ||
<ThemeReport theme={defaultTheme} /> | ||
</Tabs.Content> | ||
<Tabs.Content value="tab2" data-theme="dark" className="bg-surface"> | ||
<ThemeReport theme={defaultThemeDark} /> | ||
</Tabs.Content> | ||
</Tabs> | ||
</div> | ||
) |
Oops, something went wrong.