diff --git a/src/components/Status/Status.props.ts b/src/components/Status/Status.props.ts
new file mode 100644
index 00000000..885e0a86
--- /dev/null
+++ b/src/components/Status/Status.props.ts
@@ -0,0 +1,10 @@
+import { StatusConfig } from './Status.styles';
+import type { StatusAppearance } from './StatusAppearance.type';
+import type { StatusEmphasis } from './StatusEmphasis.type';
+
+export type StatusProps = {
+ appearance?: StatusAppearance;
+ custom?: StatusConfig;
+ emphasis?: StatusEmphasis;
+ label: string;
+};
diff --git a/src/components/Status/Status.stories.tsx b/src/components/Status/Status.stories.tsx
new file mode 100644
index 00000000..8b3dfc50
--- /dev/null
+++ b/src/components/Status/Status.stories.tsx
@@ -0,0 +1,49 @@
+import { Meta, StoryObj } from '@storybook/react';
+
+import { Status } from './Status';
+import { StatusEmphasis } from './StatusEmphasis.type';
+
+import { StatusDocs } from '@/docs-components/StatusDocs';
+import { TetDocs } from '@/docs-components/TetDocs';
+
+const meta = {
+ title: 'Status',
+ component: Status,
+ tags: ['autodocs'],
+ argTypes: {
+ emphasis: {
+ control: {
+ type: 'select',
+ options: ['high', 'medium', 'low'] satisfies StatusEmphasis[],
+ },
+ },
+ },
+ args: {
+ appearance: 'grey',
+ emphasis: 'high',
+ },
+ parameters: {
+ docs: {
+ description: {
+ component:
+ 'An indicator that conveys the current state or condition of a process, item, or user. Status indicators often use colors, icons, or text to communicate information, such as online presence, approval, or completion.',
+ },
+ page: () => (
+
+
+
+ ),
+ },
+ },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ appearance: 'grey',
+ emphasis: 'high',
+ label: 'status',
+ },
+};
diff --git a/src/components/Status/Status.styles.ts b/src/components/Status/Status.styles.ts
new file mode 100644
index 00000000..1f6927ad
--- /dev/null
+++ b/src/components/Status/Status.styles.ts
@@ -0,0 +1,214 @@
+import type { StatusAppearance } from './StatusAppearance.type';
+
+import { BaseProps } from '@/types';
+
+export type StatusConfig = {
+ appearance?: Partial<
+ Record<
+ StatusAppearance,
+ { emphasis?: Partial> }
+ >
+ >;
+ dot: {
+ appearance: Partial<
+ Record<
+ StatusAppearance,
+ { emphasis?: Partial> }
+ >
+ >;
+ } & BaseProps<'appearance'>;
+ hasLabel?: BaseProps;
+ innerElements: {
+ label?: BaseProps;
+ };
+} & BaseProps<'appearance'>;
+
+export const defaultConfig = {
+ display: 'flex',
+ w: 'fit-content',
+ h: '$size-xSmall',
+ gap: '$space-component-gap-small',
+ alignItems: 'center',
+ text: '$typo-body-medium',
+ dot: {
+ w: '8px',
+ h: '8px',
+ borderRadius: '$border-radius-full',
+ appearance: {
+ grey: {
+ emphasis: {
+ high: {
+ backgroundColor: '$color-nonSemantic-white-content-primary',
+ },
+ medium: {
+ backgroundColor: '$color-nonSemantic-grey-background-strong',
+ },
+ low: {
+ backgroundColor: '$color-nonSemantic-grey-background-strong',
+ },
+ },
+ },
+ blue: {
+ emphasis: {
+ high: {
+ backgroundColor: '$color-nonSemantic-white-content-primary',
+ },
+ medium: {
+ backgroundColor: '$color-nonSemantic-blue-background-strong',
+ },
+ low: {
+ backgroundColor: '$color-nonSemantic-blue-background-strong',
+ },
+ },
+ },
+ green: {
+ emphasis: {
+ high: {
+ backgroundColor: '$color-nonSemantic-white-content-primary',
+ },
+ medium: {
+ backgroundColor: '$color-nonSemantic-green-background-strong',
+ },
+ low: {
+ backgroundColor: '$color-nonSemantic-green-background-strong',
+ },
+ },
+ },
+ red: {
+ emphasis: {
+ high: {
+ backgroundColor: '$color-nonSemantic-white-content-primary',
+ },
+ medium: {
+ backgroundColor: '$color-nonSemantic-red-background-strong',
+ },
+ low: {
+ backgroundColor: '$color-nonSemantic-red-background-strong',
+ },
+ },
+ },
+ orange: {
+ emphasis: {
+ high: {
+ backgroundColor: '$color-nonSemantic-grey-content-primary',
+ },
+ medium: {
+ backgroundColor: '$color-nonSemantic-orange-background-strong',
+ },
+ low: {
+ backgroundColor: '$color-nonSemantic-orange-background-strong',
+ },
+ },
+ },
+ },
+ },
+ appearance: {
+ grey: {
+ emphasis: {
+ high: {
+ backgroundColor: '$color-nonSemantic-grey-background-strong',
+ color: '$color-nonSemantic-white-content-primary',
+ p: '$space-component-padding-null $space-component-padding-small',
+ borderRadius: '$border-radius-medium',
+ },
+ medium: {
+ backgroundColor: '$color-nonSemantic-grey-background-muted',
+ color: '$color-nonSemantic-grey-content-primary',
+ p: '$space-component-padding-null $space-component-padding-small',
+ borderRadius: '$border-radius-medium',
+ },
+ low: {
+ backgroundColor: '$color-transparent',
+ color: '$color-nonSemantic-grey-content-primary',
+ borderRadius: '$border-radius-medium',
+ },
+ },
+ },
+ blue: {
+ emphasis: {
+ high: {
+ backgroundColor: '$color-nonSemantic-blue-background-strong',
+ color: '$color-nonSemantic-white-content-primary',
+ p: '$space-component-padding-null $space-component-padding-small',
+ borderRadius: '$border-radius-medium',
+ },
+ medium: {
+ backgroundColor: '$color-nonSemantic-blue-background-muted',
+ color: '$color-nonSemantic-blue-content-primary',
+ p: '$space-component-padding-null $space-component-padding-small',
+ borderRadius: '$border-radius-medium',
+ },
+ low: {
+ backgroundColor: '$color-transparent',
+ color: '$color-nonSemantic-blue-content-primary',
+ },
+ },
+ },
+ green: {
+ emphasis: {
+ high: {
+ color: '$color-nonSemantic-white-content-primary',
+ backgroundColor: '$color-nonSemantic-green-background-strong',
+ p: '$space-component-padding-null $space-component-padding-small',
+ borderRadius: '$border-radius-medium',
+ },
+ medium: {
+ color: '$color-nonSemantic-green-content-primary',
+ backgroundColor: '$color-nonSemantic-green-background-muted',
+ p: '$space-component-padding-null $space-component-padding-small',
+ borderRadius: '$border-radius-medium',
+ },
+ low: {
+ color: '$color-nonSemantic-green-content-primary',
+ },
+ },
+ },
+ red: {
+ emphasis: {
+ high: {
+ color: '$color-nonSemantic-white-content-primary',
+ backgroundColor: '$color-nonSemantic-red-background-strong',
+ p: '$space-component-padding-null $space-component-padding-small',
+ borderRadius: '$border-radius-medium',
+ },
+ medium: {
+ color: '$color-nonSemantic-red-content-primary',
+ backgroundColor: '$color-nonSemantic-red-background-muted',
+ p: '$space-component-padding-null $space-component-padding-small',
+ borderRadius: '$border-radius-medium',
+ },
+ low: {
+ color: '$color-nonSemantic-red-content-primary',
+ backgroundColor: '$color-transparent',
+ },
+ },
+ },
+ orange: {
+ emphasis: {
+ high: {
+ backgroundColor: '$color-nonSemantic-orange-background-strong',
+ color: '$color-nonSemantic-grey-content-primary',
+ p: '$space-component-padding-null $space-component-padding-small',
+ borderRadius: '$border-radius-medium',
+ },
+ medium: {
+ color: '$color-nonSemantic-orange-content-primary',
+ backgroundColor: '$color-nonSemantic-orange-background-muted',
+ p: '$space-component-padding-null $space-component-padding-small',
+ borderRadius: '$border-radius-medium',
+ },
+ low: {
+ color: '$color-nonSemantic-orange-content-primary',
+ backgroundColor: '$color-transparent',
+ },
+ },
+ },
+ },
+ innerElements: {
+ label: {},
+ },
+} satisfies StatusConfig;
+
+export const statusStyles = {
+ defaultConfig,
+};
diff --git a/src/components/Status/Status.test.tsx b/src/components/Status/Status.test.tsx
new file mode 100644
index 00000000..074e7b4d
--- /dev/null
+++ b/src/components/Status/Status.test.tsx
@@ -0,0 +1,117 @@
+import { Status } from './Status';
+import { render } from '../../tests/render';
+
+import { customPropTester } from '@/tests/customPropTester';
+
+const getStatus = (jsx: JSX.Element) => {
+ const { getByTestId } = render(jsx);
+ return {
+ status: getByTestId('status'),
+ dot: getByTestId('status-dot'),
+ };
+};
+
+describe('Status', () => {
+ it('should render the status', () => {
+ const { status } = getStatus();
+ expect(status).toBeInTheDocument();
+ });
+
+ it('should render a correct color (grey, high)', () => {
+ const { status, dot } = getStatus(
+ ,
+ );
+ expect(status).toHaveStyle(
+ 'background-color: rgb(85, 95, 109); color: rgb(255, 255, 255)',
+ );
+ expect(dot).toHaveStyle('background-color: rgb(255, 255, 255)');
+ });
+
+ it('should render a correct color (grey, medium)', () => {
+ const { status, dot } = getStatus(
+ ,
+ );
+ expect(status).toHaveStyle(
+ 'background-color: rgb(222, 227, 231); color: rgb(39, 46, 53)',
+ );
+ expect(dot).toHaveStyle('background-color: rgb(85, 95, 109)');
+ });
+
+ it('should render a correct color (grey, low)', () => {
+ const { status, dot } = getStatus(
+ ,
+ );
+ expect(status).toHaveStyle(
+ 'background-color: rgba(255, 255, 255, 0); color: rgb(39, 46, 53)',
+ );
+ expect(dot).toHaveStyle('background-color: rgb(85, 95, 109)');
+ });
+
+ it('should render a correct color (blue, high)', () => {
+ const { status, dot } = getStatus(
+ ,
+ );
+ expect(status).toHaveStyle(
+ 'background-color: rgb(48, 98, 212); color: rgb(255, 255, 255)',
+ );
+ expect(dot).toHaveStyle('background-color: rgb(255, 255, 255)');
+ });
+
+ it('should render a correct color (green, high)', () => {
+ const { status, dot } = getStatus(
+ ,
+ );
+ expect(status).toHaveStyle(
+ 'background-color: rgb(29, 124, 77); color: rgb(255, 255, 255)',
+ );
+ expect(dot).toHaveStyle('background-color: rgb(255, 255, 255)');
+ });
+
+ it('should render a correct color (red, high)', () => {
+ const { status, dot } = getStatus(
+ ,
+ );
+ expect(status).toHaveStyle(
+ 'background-color: rgb(197, 52, 52); color: rgb(255, 255, 255)',
+ );
+ expect(dot).toHaveStyle('background-color: rgb(255, 255, 255)');
+ });
+
+ it('should render a correct color (orange, high)', () => {
+ const { status, dot } = getStatus(
+ ,
+ );
+ expect(status).toHaveStyle(
+ 'background-color: rgb(245, 150, 56); color: rgb(39, 46, 53)',
+ );
+ expect(dot).toHaveStyle('background-color: rgb(39, 46, 53)');
+ });
+
+ it('should render a correct color (orange, medium)', () => {
+ const { status, dot } = getStatus(
+ ,
+ );
+ expect(status).toHaveStyle(
+ 'background-color: rgb(252, 222, 192); color: rgb(122, 69, 16)',
+ );
+ expect(dot).toHaveStyle('background-color: rgb(245, 150, 56)');
+ });
+
+ it('should render a correct color (orange, low)', () => {
+ const { status, dot } = getStatus(
+ ,
+ );
+ expect(status).toHaveStyle(
+ 'background-color: rgba(255, 255, 255, 0); color: rgb(122, 69, 16)',
+ );
+ expect(dot).toHaveStyle('background-color: rgb(245, 150, 56)');
+ });
+
+ customPropTester(, {
+ containerId: 'status',
+ props: {
+ appearance: ['grey', 'blue', 'green', 'red', 'orange'],
+ emphasis: ['high', 'medium', 'low'],
+ },
+ });
+});
diff --git a/src/components/Status/Status.tsx b/src/components/Status/Status.tsx
new file mode 100644
index 00000000..4673c850
--- /dev/null
+++ b/src/components/Status/Status.tsx
@@ -0,0 +1,26 @@
+import { FC, useMemo } from 'react';
+
+import { StatusProps } from './Status.props';
+import { stylesBuilder } from './stylesBuilder';
+
+import { tet } from '@/tetrisly';
+
+export const Status: FC = ({
+ appearance,
+ emphasis,
+ label,
+ custom,
+ ...restProps
+}) => {
+ const styles = useMemo(
+ () => stylesBuilder(appearance, custom, emphasis),
+ [custom, emphasis, appearance],
+ );
+
+ return (
+
+
+ {label}
+
+ );
+};
diff --git a/src/components/Status/StatusAppearance.type.ts b/src/components/Status/StatusAppearance.type.ts
new file mode 100644
index 00000000..c8b44b22
--- /dev/null
+++ b/src/components/Status/StatusAppearance.type.ts
@@ -0,0 +1 @@
+export type StatusAppearance = 'grey' | 'blue' | 'green' | 'red' | 'orange';
diff --git a/src/components/Status/StatusEmphasis.type.ts b/src/components/Status/StatusEmphasis.type.ts
new file mode 100644
index 00000000..9a95eb87
--- /dev/null
+++ b/src/components/Status/StatusEmphasis.type.ts
@@ -0,0 +1 @@
+export type StatusEmphasis = 'high' | 'medium' | 'low';
diff --git a/src/components/Status/index.ts b/src/components/Status/index.ts
new file mode 100644
index 00000000..9dd9a067
--- /dev/null
+++ b/src/components/Status/index.ts
@@ -0,0 +1,3 @@
+export { Status } from './Status';
+export type { StatusProps } from './Status.props';
+export { statusStyles } from './Status.styles';
diff --git a/src/components/Status/stylesBuilder.ts b/src/components/Status/stylesBuilder.ts
new file mode 100644
index 00000000..b939d908
--- /dev/null
+++ b/src/components/Status/stylesBuilder.ts
@@ -0,0 +1,44 @@
+import { defaultConfig, StatusConfig } from './Status.styles';
+import type { StatusAppearance } from './StatusAppearance.type';
+import type { StatusEmphasis } from './StatusEmphasis.type';
+
+import { mergeConfigWithCustom } from '@/services';
+import { BaseProps } from '@/types';
+
+type StylesBuilderParams = {
+ container: BaseProps;
+ dot: BaseProps;
+};
+
+export const stylesBuilder = (
+ appearance?: StatusAppearance,
+ custom?: StatusConfig,
+ emphasis?: StatusEmphasis,
+): StylesBuilderParams => {
+ const {
+ innerElements,
+ appearance: containerAppearance,
+ dot,
+ ...container
+ } = mergeConfigWithCustom({ defaultConfig, custom });
+
+ const { appearance: dotAppearanceStyle, ...dotAppearance } = dot;
+
+ const containerStyles =
+ appearance &&
+ emphasis &&
+ containerAppearance[appearance].emphasis[emphasis];
+ const dotStyles =
+ appearance && emphasis && dot.appearance[appearance].emphasis[emphasis];
+
+ return {
+ container: {
+ ...container,
+ ...containerStyles,
+ },
+ dot: {
+ ...dotAppearance,
+ ...dotStyles,
+ },
+ };
+};
diff --git a/src/docs-components/StatusDocs.tsx b/src/docs-components/StatusDocs.tsx
new file mode 100644
index 00000000..b2dcb865
--- /dev/null
+++ b/src/docs-components/StatusDocs.tsx
@@ -0,0 +1,62 @@
+import { capitalize } from 'lodash';
+
+import { SectionHeader } from './common/SectionHeader';
+import { State } from './common/States';
+
+import { Status } from '@/components/Status';
+import { tet } from '@/tetrisly';
+
+const appearances = ['grey', 'blue', 'green', 'red', 'orange'] as const;
+const emphases = ['high', 'medium', 'low'] as const;
+
+export const StatusDocs = () => (
+ <>
+ {emphases.map((emphasis) => (
+
+
+ {capitalize(emphasis)} Emphasis
+
+
+
+ {appearances.map((appearance, i) => (
+
+
+
+
+
+
+ ))}
+
+
+
+ ))}
+ >
+);