diff --git a/ui/components/component-library/base-icon/README.mdx b/ui/components/component-library/base-icon/README.mdx
new file mode 100644
index 000000000..519ebb745
--- /dev/null
+++ b/ui/components/component-library/base-icon/README.mdx
@@ -0,0 +1,77 @@
+import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
+
+import { BaseIcon } from './base-icon';
+
+### This is a base component. It should not be used in your feature code directly but as a "base" for other UI components
+
+# BaseIcon
+
+The `BaseIcon` is the base component for all icons. It is used in conjunction with a script to create all icons it should not be used directly.
+
+
+
+## Props
+
+The `BaseIcon` accepts all props below as well as all [Box](/docs/ui-components-ui-box-box-stories-js--default-story#props) component props
+
+
+
+### Size
+
+Use the `size` prop and the `SIZES` object from `./ui/helpers/constants/design-system.js` to change the size of `BaseIcon`. Defaults to `SIZES.SM`
+
+Possible sizes include:
+
+- `SIZES.XXS` 10px
+- `SIZES.XS` 12px
+- `SIZES.SM` 16px
+- `SIZES.MD` 20px
+- `SIZES.LG` 24px
+- `SIZES.XL` 32px
+
+
+
+```jsx
+import { SIZES } from '../../../helpers/constants/design-system';
+import { BaseIcon } from '../ui/component-library';
+
+
+
+
+
+
+
+```
+
+### Color
+
+Use the `color` prop and the `COLORS` object from `./ui/helpers/constants/design-system.js` to change the color of `BaseIcon`. Defaults to `COLORS.INHERIT` which will use the text color of the parent element. This is useful for inline icons.
+
+
+
+```jsx
+import { COLORS } from '../../../helpers/constants/design-system';
+import { BaseIcon } from '../ui/component-library';
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/ui/components/component-library/base-icon/base-icon.js b/ui/components/component-library/base-icon/base-icon.js
new file mode 100644
index 000000000..966371410
--- /dev/null
+++ b/ui/components/component-library/base-icon/base-icon.js
@@ -0,0 +1,56 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+import Box from '../../ui/box/box';
+
+import {
+ SIZES,
+ COLORS,
+ ICON_COLORS,
+} from '../../../helpers/constants/design-system';
+
+export const BaseIcon = ({
+ className,
+ size = SIZES.MD,
+ color = COLORS.INHERIT,
+ children,
+ ...props
+}) => {
+ return (
+
+ {children}
+
+ );
+};
+
+BaseIcon.propTypes = {
+ /**
+ * The size of the BaseIcon.
+ * Possible values could be 'SIZES.XXS', 'SIZES.XS', 'SIZES.SM', 'SIZES.MD', 'SIZES.LG', 'SIZES.XL',
+ * Default value is 'SIZES.MD'.
+ */
+ size: PropTypes.oneOf(Object.values(SIZES)),
+ /**
+ * The color of the icon.
+ * Defaults to COLORS.INHERIT.
+ */
+ color: PropTypes.oneOf(Object.values(ICON_COLORS)),
+ /**
+ * An additional className to apply to the icon.
+ */
+ className: PropTypes.string,
+ /**
+ * The to the icon.
+ */
+ children: PropTypes.node,
+ /**
+ * BaseIcon accepts all the props from Box
+ */
+ ...Box.propTypes,
+};
diff --git a/ui/components/component-library/base-icon/base-icon.scss b/ui/components/component-library/base-icon/base-icon.scss
new file mode 100644
index 000000000..fd81cab65
--- /dev/null
+++ b/ui/components/component-library/base-icon/base-icon.scss
@@ -0,0 +1,33 @@
+.base-icon {
+ --icon-size: var(--size, 16px);
+
+ &--size-xxs {
+ --size: 10px;
+ }
+
+ &--size-xs {
+ --size: 12px;
+ }
+
+ &--size-sm {
+ --size: 16px;
+ }
+
+ &--size-md {
+ --size: 20px;
+ }
+
+ &--size-lg {
+ --size: 24px;
+ }
+
+ &--size-xl {
+ --size: 32px;
+ }
+
+ font-size: var(--icon-size);
+ width: 1em;
+ height: 1em;
+ display: inline-block;
+ fill: currentColor;
+}
diff --git a/ui/components/component-library/base-icon/base-icon.stories.js b/ui/components/component-library/base-icon/base-icon.stories.js
new file mode 100644
index 000000000..89c7ce245
--- /dev/null
+++ b/ui/components/component-library/base-icon/base-icon.stories.js
@@ -0,0 +1,176 @@
+import React from 'react';
+import {
+ SIZES,
+ ALIGN_ITEMS,
+ DISPLAY,
+ COLORS,
+ ICON_COLORS,
+} from '../../../helpers/constants/design-system';
+import Box from '../../ui/box/box';
+
+import { BaseIcon } from './base-icon';
+import README from './README.mdx';
+
+const marginSizeControlOptions = [
+ undefined,
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 'auto',
+];
+
+export default {
+ title: 'Components/ComponentLibrary/BaseIcon',
+ id: __filename,
+ component: BaseIcon,
+ parameters: {
+ docs: {
+ page: README,
+ },
+ },
+ argTypes: {
+ size: {
+ control: 'select',
+ options: Object.values(SIZES),
+ },
+ color: {
+ control: 'select',
+ options: Object.values(ICON_COLORS),
+ },
+ className: {
+ control: 'text',
+ },
+ marginTop: {
+ options: marginSizeControlOptions,
+ control: 'select',
+ table: { category: 'box props' },
+ },
+ marginRight: {
+ options: marginSizeControlOptions,
+ control: 'select',
+ table: { category: 'box props' },
+ },
+ marginBottom: {
+ options: marginSizeControlOptions,
+ control: 'select',
+ table: { category: 'box props' },
+ },
+ marginLeft: {
+ options: marginSizeControlOptions,
+ control: 'select',
+ table: { category: 'box props' },
+ },
+ },
+ args: {
+ color: COLORS.INHERIT,
+ size: SIZES.MD,
+ children: (
+
+ ),
+ },
+};
+
+export const DefaultStory = (args) => ;
+
+DefaultStory.storyName = 'Default';
+
+export const Size = (args) => (
+
+
+
+
+
+
+
+
+);
+
+export const Color = (args) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/ui/components/component-library/base-icon/base-icon.test.js b/ui/components/component-library/base-icon/base-icon.test.js
new file mode 100644
index 000000000..6ecda4ec7
--- /dev/null
+++ b/ui/components/component-library/base-icon/base-icon.test.js
@@ -0,0 +1,64 @@
+/* eslint-disable jest/require-top-level-describe */
+import { render } from '@testing-library/react';
+import React from 'react';
+import { SIZES, COLORS } from '../../../helpers/constants/design-system';
+import { BaseIcon } from './base-icon';
+
+describe('BaseIcon', () => {
+ it('should render correctly', () => {
+ const { getByTestId, container } = render(
+ ,
+ );
+ expect(getByTestId('base-icon')).toBeDefined();
+ expect(container.querySelector('svg')).toBeDefined();
+ });
+ it('should render with different size classes', () => {
+ const { getByTestId } = render(
+ <>
+
+
+
+
+
+
+ >,
+ );
+ expect(getByTestId('base-icon-xxs')).toHaveClass('base-icon--size-xxs');
+ expect(getByTestId('base-icon-xs')).toHaveClass('base-icon--size-xs');
+ expect(getByTestId('base-icon-sm')).toHaveClass('base-icon--size-sm');
+ expect(getByTestId('base-icon-md')).toHaveClass('base-icon--size-md');
+ expect(getByTestId('base-icon-lg')).toHaveClass('base-icon--size-lg');
+ expect(getByTestId('base-icon-xl')).toHaveClass('base-icon--size-xl');
+ });
+ it('should render with icon colors', () => {
+ const { getByTestId } = render(
+ <>
+
+
+
+
+ >,
+ );
+ expect(getByTestId('base-icon-color-inherit')).toHaveClass(
+ 'box--color-inherit',
+ );
+ expect(getByTestId('base-icon-color-default')).toHaveClass(
+ 'box--color-icon-default',
+ );
+ expect(getByTestId('base-icon-color-alternative')).toHaveClass(
+ 'box--color-icon-alternative',
+ );
+ expect(getByTestId('base-icon-color-muted')).toHaveClass(
+ 'box--color-icon-muted',
+ );
+ });
+});
diff --git a/ui/components/component-library/base-icon/index.js b/ui/components/component-library/base-icon/index.js
new file mode 100644
index 000000000..f1ceeadd2
--- /dev/null
+++ b/ui/components/component-library/base-icon/index.js
@@ -0,0 +1 @@
+export { BaseIcon } from './base-icon';
diff --git a/ui/components/component-library/component-library-components.scss b/ui/components/component-library/component-library-components.scss
index b1e580564..22a8192c2 100644
--- a/ui/components/component-library/component-library-components.scss
+++ b/ui/components/component-library/component-library-components.scss
@@ -1,2 +1,3 @@
/** Please import your files in alphabetical order **/
@import 'base-avatar/base-avatar';
+@import 'base-icon/base-icon';
diff --git a/ui/components/ui/box/box.js b/ui/components/ui/box/box.js
index 61bd7e39a..716247f9a 100644
--- a/ui/components/ui/box/box.js
+++ b/ui/components/ui/box/box.js
@@ -9,6 +9,7 @@ import {
BACKGROUND_COLORS,
BORDER_COLORS,
TEXT_COLORS,
+ ICON_COLORS,
DISPLAY,
JUSTIFY_CONTENT,
SIZES,
@@ -28,6 +29,7 @@ export const ValidBackgroundColors = PropTypes.oneOf(
);
export const ValidBorderColors = PropTypes.oneOf(Object.values(BORDER_COLORS));
export const ValidTextColors = PropTypes.oneOf(Object.values(TEXT_COLORS));
+export const ValidIconColors = PropTypes.oneOf(Object.values(ICON_COLORS));
const ArrayOfValidSizes = PropTypes.arrayOf(ValidSize);
export const MultipleSizes = PropTypes.oneOfType([
@@ -54,9 +56,12 @@ export const MultipleBackgroundColors = PropTypes.oneOfType([
]);
const ArrayOfValidTextColors = PropTypes.arrayOf(ValidTextColors);
+const ArrayOfValidIconColors = PropTypes.arrayOf(ValidIconColors);
export const MultipleTextColors = PropTypes.oneOfType([
ValidTextColors,
ArrayOfValidTextColors,
+ ValidIconColors,
+ ArrayOfValidIconColors,
]);
function isValidSize(type, value) {
diff --git a/ui/helpers/constants/design-system.js b/ui/helpers/constants/design-system.js
index c5fff3da7..699f8bbe4 100644
--- a/ui/helpers/constants/design-system.js
+++ b/ui/helpers/constants/design-system.js
@@ -126,6 +126,25 @@ export const TEXT_COLORS = pick(COLORS, [
'INFO_INVERSE',
'INHERIT',
]);
+
+export const ICON_COLORS = pick(COLORS, [
+ 'ICON_DEFAULT',
+ 'ICON_ALTERNATIVE',
+ 'ICON_MUTED',
+ 'OVERLAY_INVERSE',
+ 'PRIMARY_DEFAULT',
+ 'PRIMARY_INVERSE',
+ 'ERROR_DEFAULT',
+ 'ERROR_INVERSE',
+ 'SUCCESS_DEFAULT',
+ 'SUCCESS_INVERSE',
+ 'WARNING_DEFAULT',
+ 'WARNING_INVERSE',
+ 'INFO_DEFAULT',
+ 'INFO_INVERSE',
+ 'INHERIT',
+]);
+
export const TYPOGRAPHY = {
H1: 'h1',
H2: 'h2',