import styled from '@emotion/styled'; import DoAccentColors from 'sentry-images/stories/color/do-accent-colors.svg'; import DoContrast from 'sentry-images/stories/color/do-contrast.svg'; import DoDifferentiation from 'sentry-images/stories/color/do-differentiation.svg'; import DontAccentColors from 'sentry-images/stories/color/dont-accent-colors.svg'; import DontContrast from 'sentry-images/stories/color/dont-contrast.svg'; import DontDifferentiation from 'sentry-images/stories/color/dont-differentiation.svg'; import ExternalLink from 'sentry/components/links/externalLink'; import Panel from 'sentry/components/panels/panel'; import PanelItem from 'sentry/components/panels/panelItem'; import ThemeToggle from 'sentry/components/stories/themeToggle'; import {IconCheckmark, IconClose} from 'sentry/icons'; import {space} from 'sentry/styles/space'; import type {ColorOrAlias} from 'sentry/utils/theme'; import theme from 'sentry/utils/theme'; interface Palette { color: ColorOrAlias; text: ColorOrAlias; } const GRAY_PALETTES: Palette[][] = [ [{color: 'gray500', text: 'lightModeWhite'}], [{color: 'gray400', text: 'lightModeWhite'}], [{color: 'gray300', text: 'lightModeWhite'}], [{color: 'gray200', text: 'lightModeBlack'}], [{color: 'gray100', text: 'lightModeBlack'}], ]; const LEVELS_PALETTES: Palette[][] = [ [ {color: 'purple400', text: 'lightModeWhite'}, {color: 'purple300', text: 'lightModeWhite'}, {color: 'purple200', text: 'lightModeBlack'}, {color: 'purple100', text: 'lightModeBlack'}, ], [ {color: 'blue400', text: 'lightModeWhite'}, {color: 'blue300', text: 'lightModeWhite'}, {color: 'blue200', text: 'lightModeBlack'}, {color: 'blue100', text: 'lightModeBlack'}, ], [ {color: 'green400', text: 'lightModeWhite'}, {color: 'green300', text: 'lightModeBlack'}, {color: 'green200', text: 'lightModeBlack'}, {color: 'green100', text: 'lightModeBlack'}, ], [ {color: 'yellow400', text: 'lightModeBlack'}, {color: 'yellow300', text: 'lightModeBlack'}, {color: 'yellow200', text: 'lightModeBlack'}, {color: 'yellow100', text: 'lightModeBlack'}, ], [ {color: 'red400', text: 'lightModeWhite'}, {color: 'red300', text: 'lightModeWhite'}, {color: 'red200', text: 'lightModeBlack'}, {color: 'red100', text: 'lightModeBlack'}, ], [ {color: 'pink400', text: 'lightModeWhite'}, {color: 'pink300', text: 'lightModeWhite'}, {color: 'pink200', text: 'lightModeBlack'}, {color: 'pink100', text: 'lightModeBlack'}, ], ]; const FixedWidth = styled('div')` max-width: 800px; `; export default function ColorStories() { return (

Colors

Sentry has a flexible, tiered color system that adapts to both light and dark mode. Our color palette consists of neutral grays and 6 accent colors.


Grays

There are 5 shades of gray, ranging from Gray 500 (darkest) to Gray 100 (lightest).

Gray 300 and above are accessible foreground colors that conform to{' '} WCAG standards . Use them as text and icon colors.

Here are the recommended use cases:


Accent Colors

Accent colors help shift the user's focus to certain interactive and high-priority elements, like links, buttons, and warning banners.

Hues

There are 6 hues to choose from. Each has specific connotations:

Levels

Each hue comes in 4 levels: 400 (dark), 300 (full opacity), 200 (medium opacity), and 100 (low opacity).


Accessibility

When it comes to using color, there are two main accessibility concerns: readability and separation.

Readability

WCAG requires that normal text elements have a contrast ratio of at least 4.5:1 against the background. For large text (at least 16px in size AND in medium/bold weight), the required ratio is lower, at 3:1. This is to ensure a comfortable reading experience in different lighting conditions.{' '} Use this tool {' '} to confirm text contrast ratios.

In Sentry's color palette, only Gray 300 and above satisfy the contrast requirement for normal text. This applies to both light and dark mode.

Accent colors in the 300 series, except for Yellow 300, satisfy the contrast requirement for large text.

Separation

Color can be an effective way to visually separate elements in the user interface. However, not all users see color in the same way. Some are color-blind and cannot reliably differentiate one color from another. Some have color filters on their screens, like Night Shift in MacOS. Others are in bright environments with high levels of glare, reducing their ability to see color clearly.

As such, color is an unreliable way to separate elements. Whenever possible, provide additional visual cues like icons, text labels, line type (solid, dashed, dotted),… to further reinforce the separation.

); } const SideBySideList = styled('ul')` /* Reset */ list-style-type: none; margin: 0; padding: 0; & > li { margin: 0; } & > li > div { margin-bottom: 0; } /* Side-by-side display */ display: grid; grid-template-columns: 1fr 1fr; gap: ${space(2)}; `; const PalettePanel = styled(Panel)` margin-bottom: 0; `; const PalettePanelItem = styled(PanelItem)<{ color: ColorOrAlias; text: ColorOrAlias; }>` flex-direction: column; gap: ${space(0.5)}; &:first-child { border-radius: ${p => p.theme.borderRadiusTop}; } &:last-child { border-radius: ${p => p.theme.borderRadiusBottom}; } &:first-child:last-child { border-radius: ${p => p.theme.borderRadius}; } background: ${p => p.theme[p.color]}; color: ${p => p.theme[p.text]}; `; function ColorPalette({name, palette}: {name: string; palette: Palette[][]}) { return ( {palette.map((section, i) => { return (
  • {section.map(color => { return ( {color.color} {theme[color.color]} ); })}
  • ); })}
    ); } const ExampleImg = styled('img')` border: 1px solid ${p => p.theme.border}; border-radius: ${p => p.theme.borderRadius}; max-width: 400px; `; const PositiveLabel = styled(({className}: {className?: string}) => (
    DO
    ))` color: ${p => p.theme.green400}; align-items: center; display: flex; font-weight: bold; gap: ${space(0.5)}; `; const NegativeLabel = styled(({className}: {className?: string}) => (
    DON'T
    ))` color: ${p => p.theme.red400}; align-items: center; display: flex; font-weight: bold; gap: ${space(0.5)}; `; const ExampleCardGrid = styled('figcaption')` display: grid; grid-template-columns: 1fr 2fr; align-items: flex-start; color: ${p => p.theme.subText}; padding: ${space(1)} ${space(1)} 0; `; interface ExampleCardProps { imgSrc: string; text: string; isPositive?: boolean; } function ExampleCard({imgSrc, text, isPositive}: ExampleCardProps) { return (
    {isPositive ? : } {text}
    ); }