diff --git a/packages/code-studio/src/styleguide/Pickers.tsx b/packages/code-studio/src/styleguide/Pickers.tsx
index 55438ec03..d4f504ae9 100644
--- a/packages/code-studio/src/styleguide/Pickers.tsx
+++ b/packages/code-studio/src/styleguide/Pickers.tsx
@@ -1,7 +1,13 @@
import React, { useCallback, useState } from 'react';
-import { Picker, PickerItemKey, Section } from '@deephaven/components';
+import {
+ Flex,
+ Picker,
+ PickerItemKey,
+ Section,
+ Text,
+} from '@deephaven/components';
import { vsPerson } from '@deephaven/icons';
-import { Flex, Icon, Item, Text } from '@adobe/react-spectrum';
+import { Icon, Item } from '@adobe/react-spectrum';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { sampleSectionIdAndClasses } from './utils';
diff --git a/packages/code-studio/src/styleguide/RandomAreaPlotAnimation.tsx b/packages/code-studio/src/styleguide/RandomAreaPlotAnimation.tsx
index 9c74b62e1..b9967d6a9 100644
--- a/packages/code-studio/src/styleguide/RandomAreaPlotAnimation.tsx
+++ b/packages/code-studio/src/styleguide/RandomAreaPlotAnimation.tsx
@@ -1,7 +1,9 @@
/* eslint-disable react/jsx-props-no-spreading */
import React from 'react';
-import { View } from '@adobe/react-spectrum';
-import { RandomAreaPlotAnimation as Animation } from '@deephaven/components';
+import {
+ View,
+ RandomAreaPlotAnimation as Animation,
+} from '@deephaven/components';
import { sampleSectionIdAndClasses } from './utils';
export function RandomAreaPlotAnimation(): JSX.Element {
diff --git a/packages/code-studio/src/styleguide/SpectrumComparison.tsx b/packages/code-studio/src/styleguide/SpectrumComparison.tsx
index 89d91bf0d..ca8037d3e 100644
--- a/packages/code-studio/src/styleguide/SpectrumComparison.tsx
+++ b/packages/code-studio/src/styleguide/SpectrumComparison.tsx
@@ -14,9 +14,7 @@ import {
Radio,
RadioGroup,
SpectrumButtonProps,
- Text,
TextField,
- View,
} from '@adobe/react-spectrum';
import {
Button as BootstrapButtonOld,
@@ -25,6 +23,8 @@ import {
RadioGroup as RadioGroupOld,
RadioItem,
Select,
+ View,
+ Text,
} from '@deephaven/components';
import { EMPTY_FUNCTION } from '@deephaven/utils';
import { vsPlay } from '@deephaven/icons';
diff --git a/packages/code-studio/src/styleguide/SpectrumComponents.tsx b/packages/code-studio/src/styleguide/SpectrumComponents.tsx
index c6370b6cc..bf66cb6a4 100644
--- a/packages/code-studio/src/styleguide/SpectrumComponents.tsx
+++ b/packages/code-studio/src/styleguide/SpectrumComponents.tsx
@@ -11,7 +11,6 @@ import {
Column,
ComboBox,
Form,
- Heading,
Grid,
Icon,
IllustratedMessage,
@@ -24,10 +23,8 @@ import {
TableBody,
TableHeader,
TableView,
- Text,
TextField,
ToggleButton,
- View,
Well,
DialogTrigger,
Dialog,
@@ -39,6 +36,7 @@ import {
} from '@adobe/react-spectrum';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { dhTruck, vsEmptyWindow } from '@deephaven/icons';
+import { Heading, View, Text } from '@deephaven/components';
import { SPECTRUM_COMPONENT_SAMPLES_ID } from './constants';
import { sampleSectionIdAndClassesSpectrum } from './utils';
diff --git a/packages/code-studio/src/styleguide/StyleGuide.tsx b/packages/code-studio/src/styleguide/StyleGuide.tsx
index f3b73f2d5..051a58917 100644
--- a/packages/code-studio/src/styleguide/StyleGuide.tsx
+++ b/packages/code-studio/src/styleguide/StyleGuide.tsx
@@ -1,7 +1,12 @@
/* eslint-disable react/jsx-props-no-spreading */
import React from 'react';
-import { Flex } from '@adobe/react-spectrum';
-import { ContextMenuRoot, ThemePicker, useTheme } from '@deephaven/components';
+
+import {
+ ContextMenuRoot,
+ ThemePicker,
+ useTheme,
+ Flex,
+} from '@deephaven/components';
import Buttons from './Buttons';
import Charts from './Charts';
diff --git a/packages/components/src/TextWithTooltip.tsx b/packages/components/src/TextWithTooltip.tsx
index bc46749d5..ef83d7af0 100644
--- a/packages/components/src/TextWithTooltip.tsx
+++ b/packages/components/src/TextWithTooltip.tsx
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
-import { Text } from '@adobe/react-spectrum';
+import { Text } from './spectrum';
import stylesCommon from './SpectrumComponent.module.scss';
import { PopperOptions, Tooltip } from './popper';
diff --git a/packages/components/src/dialogs/ActionButtonDialogTrigger.tsx b/packages/components/src/dialogs/ActionButtonDialogTrigger.tsx
index 51732a723..694ceaf36 100644
--- a/packages/components/src/dialogs/ActionButtonDialogTrigger.tsx
+++ b/packages/components/src/dialogs/ActionButtonDialogTrigger.tsx
@@ -1,9 +1,10 @@
import { ReactElement } from 'react';
-import { ActionButton, DialogTrigger, Icon, Text } from '@adobe/react-spectrum';
+import { ActionButton, DialogTrigger, Icon } from '@adobe/react-spectrum';
import type { SpectrumDialogClose } from '@react-types/dialog';
import type { StyleProps } from '@react-types/shared';
import type { IconDefinition } from '@fortawesome/fontawesome-common-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Text } from '../spectrum';
import { Tooltip } from '../popper';
export interface ActionButtonDialogTriggerProps extends StyleProps {
diff --git a/packages/components/src/dialogs/ConfirmationDialog.tsx b/packages/components/src/dialogs/ConfirmationDialog.tsx
index 1ff1d040c..d28244dc7 100644
--- a/packages/components/src/dialogs/ConfirmationDialog.tsx
+++ b/packages/components/src/dialogs/ConfirmationDialog.tsx
@@ -7,10 +7,10 @@ import {
Dialog,
Divider,
Form,
- Heading,
} from '@adobe/react-spectrum';
import type { SpectrumLabelableProps } from '@react-types/shared';
import { useFormWithDetachedSubmitButton } from '@deephaven/react-hooks';
+import { Heading } from '../spectrum';
import styles from '../SpectrumComponent.module.scss';
export interface ConfirmationDialogProps {
diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts
index bdd245ca0..caba816c2 100644
--- a/packages/components/src/index.ts
+++ b/packages/components/src/index.ts
@@ -49,7 +49,7 @@ export * from './SelectValueList';
export * from './shortcuts';
export { default as SocketedButton } from './SocketedButton';
export * from './spectrum';
-export * from './SpectrumUtils';
+export * from './spectrum/utils';
export * from './TableViewEmptyState';
export * from './TextWithTooltip';
export * from './theme';
diff --git a/packages/components/src/spectrum/Heading.test.tsx b/packages/components/src/spectrum/Heading.test.tsx
new file mode 100644
index 000000000..3ca4c3f50
--- /dev/null
+++ b/packages/components/src/spectrum/Heading.test.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import { Heading } from './Heading';
+
+describe('Heading', () => {
+ it('mounts and unmounts', () => {
+ render({null});
+ });
+
+ it('renders without color', () => {
+ const { getByTestId } = render();
+ const HeadingElement = getByTestId('Heading');
+ expect(HeadingElement).toBeInTheDocument();
+ });
+
+ it('renders with color', () => {
+ const color = 'red';
+ const { getByTestId } = render(
+
+ );
+ const HeadingElement = getByTestId('Heading');
+ expect(HeadingElement).toBeInTheDocument();
+ expect(HeadingElement).toHaveStyle(`color: ${color}`);
+ });
+});
diff --git a/packages/components/src/spectrum/Heading.tsx b/packages/components/src/spectrum/Heading.tsx
new file mode 100644
index 000000000..38682e6ca
--- /dev/null
+++ b/packages/components/src/spectrum/Heading.tsx
@@ -0,0 +1,36 @@
+/* eslint-disable react/jsx-props-no-spreading */
+import { useMemo } from 'react';
+import {
+ Heading as SpectrumHeading,
+ type HeadingProps as SpectrumHeadingProps,
+} from '@adobe/react-spectrum';
+import { type ColorValue, colorValueStyle } from '../theme/colorUtils';
+
+export type HeadingProps = SpectrumHeadingProps & {
+ color?: ColorValue;
+};
+
+/**
+ * A Heading component that re-exports the Spectrum Heading component.
+ * It overrides ColorValues to accept CSS color strings and custom
+ * variable names from our color paletee and semantic colors.
+ *
+ * @param props The props for the Heading component
+ * @returns The Heading component
+ *
+ */
+
+export function Heading(props: HeadingProps): JSX.Element {
+ const { color, UNSAFE_style, ...rest } = props;
+ const style = useMemo(
+ () => ({
+ ...UNSAFE_style,
+ color: colorValueStyle(color),
+ }),
+ [color, UNSAFE_style]
+ );
+
+ return ;
+}
+
+export default Heading;
diff --git a/packages/components/src/spectrum/Text.test.tsx b/packages/components/src/spectrum/Text.test.tsx
new file mode 100644
index 000000000..39b376c4c
--- /dev/null
+++ b/packages/components/src/spectrum/Text.test.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import { Text } from './Text';
+
+describe('Text', () => {
+ it('mounts and unmounts', () => {
+ render(test);
+ });
+
+ it('renders without color', () => {
+ const { getByTestId } = render(test);
+ const TextElement = getByTestId('Text');
+ expect(TextElement).toBeInTheDocument();
+ });
+
+ it('renders with color', () => {
+ const color = 'red';
+ const { getByTestId } = render(
+
+ test
+
+ );
+ const TextElement = getByTestId('Text');
+ expect(TextElement).toBeInTheDocument();
+ expect(TextElement).toHaveStyle(`color: ${color}`);
+ });
+});
diff --git a/packages/components/src/spectrum/Text.tsx b/packages/components/src/spectrum/Text.tsx
new file mode 100644
index 000000000..3164c2eaf
--- /dev/null
+++ b/packages/components/src/spectrum/Text.tsx
@@ -0,0 +1,36 @@
+/* eslint-disable react/jsx-props-no-spreading */
+import { useMemo } from 'react';
+import {
+ Text as SpectrumText,
+ type TextProps as SpectrumTextProps,
+} from '@adobe/react-spectrum';
+import { type ColorValue, colorValueStyle } from '../theme/colorUtils';
+
+export type TextProps = SpectrumTextProps & {
+ color?: ColorValue;
+};
+
+/**
+ * A Text component that re-exports the Spectrum Text component.
+ * It overrides ColorValues to accept CSS color strings and custom
+ * variable names from our color paletee and semantic colors.
+ *
+ * @param props The props for the Text component
+ * @returns The Text component
+ *
+ */
+
+export function Text(props: TextProps): JSX.Element {
+ const { color, UNSAFE_style, ...rest } = props;
+ const style = useMemo(
+ () => ({
+ ...UNSAFE_style,
+ color: colorValueStyle(color),
+ }),
+ [color, UNSAFE_style]
+ );
+
+ return ;
+}
+
+export default Text;
diff --git a/packages/components/src/spectrum/View.test.tsx b/packages/components/src/spectrum/View.test.tsx
new file mode 100644
index 000000000..52b5d989c
--- /dev/null
+++ b/packages/components/src/spectrum/View.test.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import { View } from './View';
+
+describe('View', () => {
+ it('mounts and unmounts', () => {
+ render({null});
+ });
+
+ it('renders without backgroundColor', () => {
+ const { getByTestId } = render();
+ const viewElement = getByTestId('view');
+ expect(viewElement).toBeInTheDocument();
+ });
+
+ it('renders with backgroundColor', () => {
+ const backgroundColor = 'red';
+ const { getByTestId } = render(
+
+ );
+ const viewElement = getByTestId('view');
+ expect(viewElement).toBeInTheDocument();
+ expect(viewElement).toHaveStyle(`background-color: ${backgroundColor}`);
+ });
+});
diff --git a/packages/components/src/spectrum/View.tsx b/packages/components/src/spectrum/View.tsx
new file mode 100644
index 000000000..847900541
--- /dev/null
+++ b/packages/components/src/spectrum/View.tsx
@@ -0,0 +1,36 @@
+/* eslint-disable react/jsx-props-no-spreading */
+import { useMemo } from 'react';
+import {
+ View as SpectrumView,
+ type ViewProps as SpectrumViewProps,
+} from '@adobe/react-spectrum';
+import { type ColorValue, colorValueStyle } from '../theme/colorUtils';
+
+export type ViewProps = Omit, 'backgroundColor'> & {
+ backgroundColor?: ColorValue;
+};
+
+/**
+ * A View component that re-exports the Spectrum View component.
+ * However, it overrides ColorValues to accept CSS color strings and
+ * our custom variable names from our color paletee and semantic colors.
+ *
+ * @param props The props for the View component
+ * @returns The View component
+ *
+ */
+
+export function View(props: ViewProps): JSX.Element {
+ const { backgroundColor, UNSAFE_style, ...rest } = props;
+ const style = useMemo(
+ () => ({
+ ...UNSAFE_style,
+ backgroundColor: colorValueStyle(backgroundColor),
+ }),
+ [backgroundColor, UNSAFE_style]
+ );
+
+ return ;
+}
+
+export default View;
diff --git a/packages/components/src/__snapshots__/SpectrumUtils.test.ts.snap b/packages/components/src/spectrum/__snapshots__/utils.test.ts.snap
similarity index 100%
rename from packages/components/src/__snapshots__/SpectrumUtils.test.ts.snap
rename to packages/components/src/spectrum/__snapshots__/utils.test.ts.snap
diff --git a/packages/components/src/spectrum/content.ts b/packages/components/src/spectrum/content.ts
index 6a98ab528..d2ef153db 100644
--- a/packages/components/src/spectrum/content.ts
+++ b/packages/components/src/spectrum/content.ts
@@ -7,18 +7,12 @@ export {
type SpectrumDividerProps as DividerProps,
Footer,
type FooterProps,
- Heading,
- type HeadingProps,
IllustratedMessage,
type SpectrumIllustratedMessageProps as IllustratedMessageProps,
Image,
type SpectrumImageProps as ImageProps,
Keyboard,
type KeyboardProps,
- Text,
- type TextProps,
- View,
- type ViewProps,
Well,
type SpectrumWellProps as WellProps,
} from '@adobe/react-spectrum';
diff --git a/packages/components/src/spectrum/index.ts b/packages/components/src/spectrum/index.ts
index 211513022..e001ef8be 100644
--- a/packages/components/src/spectrum/index.ts
+++ b/packages/components/src/spectrum/index.ts
@@ -17,3 +17,11 @@ export * from './status';
* Custom DH components wrapping React Spectrum components.
*/
export * from './picker';
+export * from './Heading';
+export * from './Text';
+export * from './View';
+
+/**
+ * Custom DH spectrum utils
+ */
+export * from './utils';
diff --git a/packages/components/src/spectrum/picker/PickerUtils.test.tsx b/packages/components/src/spectrum/picker/PickerUtils.test.tsx
index 578726a00..b63f294d1 100644
--- a/packages/components/src/spectrum/picker/PickerUtils.test.tsx
+++ b/packages/components/src/spectrum/picker/PickerUtils.test.tsx
@@ -17,7 +17,7 @@ import {
} from './PickerUtils';
import type { PickerProps } from './Picker';
import { Item, Section } from '../shared';
-import { Text } from '../content';
+import { Text } from '../Text';
beforeEach(() => {
expect.hasAssertions();
diff --git a/packages/components/src/SpectrumUtils.test.ts b/packages/components/src/spectrum/utils.test.ts
similarity index 76%
rename from packages/components/src/SpectrumUtils.test.ts
rename to packages/components/src/spectrum/utils.test.ts
index 7f5ce7133..1176a18f9 100644
--- a/packages/components/src/SpectrumUtils.test.ts
+++ b/packages/components/src/spectrum/utils.test.ts
@@ -1,4 +1,4 @@
-import { themeDHDefault } from './SpectrumUtils';
+import { themeDHDefault } from './utils';
describe('themeDHDefault', () => {
it('should merge Spectrum default with DH custom styles', () => {
diff --git a/packages/components/src/SpectrumUtils.ts b/packages/components/src/spectrum/utils.ts
similarity index 95%
rename from packages/components/src/SpectrumUtils.ts
rename to packages/components/src/spectrum/utils.ts
index 95e6f7e99..2ce048b5f 100644
--- a/packages/components/src/SpectrumUtils.ts
+++ b/packages/components/src/spectrum/utils.ts
@@ -1,5 +1,5 @@
import { theme } from '@react-spectrum/theme-default';
-import { themeSpectrumClassesCommon } from './theme/theme-spectrum';
+import { themeSpectrumClassesCommon } from '../theme/theme-spectrum';
const { global, light, dark, medium, large } = theme;
diff --git a/packages/components/src/theme/SpectrumThemeProvider.tsx b/packages/components/src/theme/SpectrumThemeProvider.tsx
index 1e433cd6e..91dc7d198 100644
--- a/packages/components/src/theme/SpectrumThemeProvider.tsx
+++ b/packages/components/src/theme/SpectrumThemeProvider.tsx
@@ -2,7 +2,7 @@ import { ReactNode, useState } from 'react';
import { Provider } from '@adobe/react-spectrum';
import type { Theme } from '@react-types/provider';
import shortid from 'shortid';
-import { themeDHDefault } from '../SpectrumUtils';
+import { themeDHDefault } from '../spectrum/utils';
export interface SpectrumThemeProviderProps {
children: ReactNode;
diff --git a/packages/components/src/theme/colorUtils.test.ts b/packages/components/src/theme/colorUtils.test.ts
new file mode 100644
index 000000000..9bd6ab109
--- /dev/null
+++ b/packages/components/src/theme/colorUtils.test.ts
@@ -0,0 +1,15 @@
+import { colorValueStyle } from './colorUtils';
+
+describe('ColorValues', () => {
+ test('should return the correct color style', () => {
+ // dh-color variables
+ expect(colorValueStyle('blue-1000')).toBe('var(--dh-color-blue-1000)');
+ expect(colorValueStyle('accent-1000')).toBe('var(--dh-color-accent-1000)');
+ expect(colorValueStyle('bg')).toBe('var(--dh-color-bg)');
+ // pass-through variables
+ expect(colorValueStyle('red')).toBe('red');
+ expect(colorValueStyle('rgb(255, 0, 0)')).toBe('rgb(255, 0, 0)');
+ expect(colorValueStyle('#ff0000')).toBe('#ff0000');
+ expect(colorValueStyle(undefined)).toBe(undefined);
+ });
+});
diff --git a/packages/components/src/theme/colorUtils.ts b/packages/components/src/theme/colorUtils.ts
new file mode 100644
index 000000000..8d32a22f9
--- /dev/null
+++ b/packages/components/src/theme/colorUtils.ts
@@ -0,0 +1,255 @@
+import { CSSProperties } from 'react';
+
+/**
+ * Color values for the DH color palette exposed to end users in spectrum components.
+ */
+export const ColorValues = [
+ 'gray-50',
+ 'gray-75',
+ 'gray-100',
+ 'gray-200',
+ 'gray-300',
+ 'gray-400',
+ 'gray-500',
+ 'gray-600',
+ 'gray-700',
+ 'gray-800',
+ 'gray-900',
+ 'red-100',
+ 'red-200',
+ 'red-300',
+ 'red-400',
+ 'red-500',
+ 'red-600',
+ 'red-700',
+ 'red-800',
+ 'red-900',
+ 'red-1000',
+ 'red-1100',
+ 'red-1200',
+ 'red-1300',
+ 'red-1400',
+ 'orange-100',
+ 'orange-200',
+ 'orange-300',
+ 'orange-400',
+ 'orange-500',
+ 'orange-600',
+ 'orange-700',
+ 'orange-800',
+ 'orange-900',
+ 'orange-1000',
+ 'orange-1100',
+ 'orange-1200',
+ 'orange-1300',
+ 'orange-1400',
+ 'yellow-100',
+ 'yellow-200',
+ 'yellow-300',
+ 'yellow-400',
+ 'yellow-500',
+ 'yellow-600',
+ 'yellow-700',
+ 'yellow-800',
+ 'yellow-900',
+ 'yellow-1000',
+ 'yellow-1100',
+ 'yellow-1200',
+ 'yellow-1300',
+ 'yellow-1400',
+ 'chartreuse-100',
+ 'chartreuse-200',
+ 'chartreuse-300',
+ 'chartreuse-400',
+ 'chartreuse-500',
+ 'chartreuse-600',
+ 'chartreuse-700',
+ 'chartreuse-800',
+ 'chartreuse-900',
+ 'chartreuse-1000',
+ 'chartreuse-1100',
+ 'chartreuse-1200',
+ 'chartreuse-1300',
+ 'chartreuse-1400',
+ 'celery-100',
+ 'celery-200',
+ 'celery-300',
+ 'celery-400',
+ 'celery-500',
+ 'celery-600',
+ 'celery-700',
+ 'celery-800',
+ 'celery-900',
+ 'celery-1000',
+ 'celery-1100',
+ 'celery-1200',
+ 'celery-1300',
+ 'celery-1400',
+ 'green-100',
+ 'green-200',
+ 'green-300',
+ 'green-400',
+ 'green-500',
+ 'green-600',
+ 'green-700',
+ 'green-800',
+ 'green-900',
+ 'green-1000',
+ 'green-1100',
+ 'green-1200',
+ 'green-1300',
+ 'green-1400',
+ 'seafoam-100',
+ 'seafoam-200',
+ 'seafoam-300',
+ 'seafoam-400',
+ 'seafoam-500',
+ 'seafoam-600',
+ 'seafoam-700',
+ 'seafoam-800',
+ 'seafoam-900',
+ 'seafoam-1000',
+ 'seafoam-1100',
+ 'seafoam-1200',
+ 'seafoam-1300',
+ 'seafoam-1400',
+ 'cyan-100',
+ 'cyan-200',
+ 'cyan-300',
+ 'cyan-400',
+ 'cyan-500',
+ 'cyan-600',
+ 'cyan-700',
+ 'cyan-800',
+ 'cyan-900',
+ 'cyan-1000',
+ 'cyan-1100',
+ 'cyan-1200',
+ 'cyan-1300',
+ 'cyan-1400',
+ 'blue-100',
+ 'blue-200',
+ 'blue-300',
+ 'blue-400',
+ 'blue-500',
+ 'blue-600',
+ 'blue-700',
+ 'blue-800',
+ 'blue-900',
+ 'blue-1000',
+ 'blue-1100',
+ 'blue-1200',
+ 'blue-1300',
+ 'blue-1400',
+ 'indigo-100',
+ 'indigo-200',
+ 'indigo-300',
+ 'indigo-400',
+ 'indigo-500',
+ 'indigo-600',
+ 'indigo-700',
+ 'indigo-800',
+ 'indigo-900',
+ 'indigo-1000',
+ 'indigo-1100',
+ 'indigo-1200',
+ 'indigo-1300',
+ 'indigo-1400',
+ 'purple-100',
+ 'purple-200',
+ 'purple-300',
+ 'purple-400',
+ 'purple-500',
+ 'purple-600',
+ 'purple-700',
+ 'purple-800',
+ 'purple-900',
+ 'purple-1000',
+ 'purple-1100',
+ 'purple-1200',
+ 'purple-1300',
+ 'purple-1400',
+ 'fuchsia-100',
+ 'fuchsia-200',
+ 'fuchsia-300',
+ 'fuchsia-400',
+ 'fuchsia-500',
+ 'fuchsia-600',
+ 'fuchsia-700',
+ 'fuchsia-800',
+ 'fuchsia-900',
+ 'fuchsia-1000',
+ 'fuchsia-1100',
+ 'fuchsia-1200',
+ 'fuchsia-1300',
+ 'fuchsia-1400',
+ 'magenta-100',
+ 'magenta-200',
+ 'magenta-300',
+ 'magenta-400',
+ 'magenta-500',
+ 'magenta-600',
+ 'magenta-700',
+ 'magenta-800',
+ 'magenta-900',
+ 'magenta-1000',
+ 'magenta-1100',
+ 'magenta-1200',
+ 'magenta-1300',
+ 'magenta-1400',
+ 'negative',
+ 'notice',
+ 'positive',
+ 'info',
+ // Additional DH ColorValues:
+ 'accent',
+ 'accent-100',
+ 'accent-200',
+ 'accent-300',
+ 'accent-400',
+ 'accent-500',
+ 'accent-600',
+ 'accent-700',
+ 'accent-800',
+ 'accent-900',
+ 'accent-1000',
+ 'accent-1100',
+ 'accent-1200',
+ 'accent-1300',
+ 'accent-1400',
+ 'bg',
+ 'content-bg',
+ 'subdued-content-bg',
+ 'surface-bg',
+ 'fg',
+] as const;
+
+type DHColorValue = (typeof ColorValues)[number];
+
+export type ColorValue = DHColorValue | CSSProperties['color'];
+
+export function isDHColorValue(value: string): value is DHColorValue {
+ return (
+ typeof value === 'string' && ColorValues.includes(value as DHColorValue)
+ );
+}
+
+/**
+ * Returns the a css variable color value for a given theme color.
+ * If the color value is a DH color value, it returns the CSS variable.
+ * Otherwise, it returns the color value as is.
+ *
+ * @param value a string representing a color value
+ * @returns CSS variable for DH color values, otherwise the color value as is
+ *
+ * ex. colorValueStyle('blue-1000') => 'var(--dh-color-blue-1000)'
+ * ex. colorValueStyle('red') => 'red'
+ * ex. colorValueStyle('#F00') => '#F00'
+ */
+export function colorValueStyle(value: string | undefined): string | undefined {
+ if (value != null && isDHColorValue(value)) {
+ return `var(--dh-color-${value})`;
+ }
+
+ return value;
+}
diff --git a/packages/components/src/theme/index.ts b/packages/components/src/theme/index.ts
index 13c862b6f..523fd23dd 100644
--- a/packages/components/src/theme/index.ts
+++ b/packages/components/src/theme/index.ts
@@ -6,3 +6,4 @@ export * from './ThemeProvider';
export * from './ThemeUtils';
export * from './useTheme';
export * from './Logo';
+export * from './colorUtils';
diff --git a/packages/eslint-config/index.js b/packages/eslint-config/index.js
index 75917b89f..e29dcc205 100644
--- a/packages/eslint-config/index.js
+++ b/packages/eslint-config/index.js
@@ -54,6 +54,13 @@ module.exports = {
'react/require-default-props': 'off',
'react/jsx-no-bind': 'off',
'react-refresh/only-export-components': 'warn',
+ camelcase: [
+ 'error',
+ {
+ allow: ['^UNSAFE_'], // allow UNSAFE_styles UNSAFE_className
+ properties: 'never', // restore airbnb default, just setting allow overrides it
+ },
+ ],
},
parserOptions: {
ecmaFeatures: {