diff --git a/src/assets/icons/_icons.ts b/src/assets/icons/_icons.ts
index a065710..7ace2d4 100644
--- a/src/assets/icons/_icons.ts
+++ b/src/assets/icons/_icons.ts
@@ -11,6 +11,7 @@ export const icons = {
'authentication': {},
'calendar': {},
'caret-down': {},
+ 'check': {},
'cloud-accounts': {},
'copy': {},
'cross': {},
diff --git a/src/assets/icons/check.svg b/src/assets/icons/check.svg
new file mode 100644
index 0000000..7b5f89c
--- /dev/null
+++ b/src/assets/icons/check.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/components/navigations/Stepper/Stepper.module.scss b/src/components/navigations/Stepper/Stepper.module.scss
new file mode 100644
index 0000000..2b13c71
--- /dev/null
+++ b/src/components/navigations/Stepper/Stepper.module.scss
@@ -0,0 +1,88 @@
+/* Copyright (c) Fortanix, Inc.
+|* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
+|* the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+@use '../../../styling/defs.scss' as bk;
+
+@layer baklava.components {
+ .bk-stepper {
+ @include bk.component-base(bk-stepper);
+
+ &.bk-stepper--horizontal {
+ display: flex;
+ gap: bk.$spacing-9;
+ }
+
+ &.bk-stepper--vertical {
+ .bk-stepper__item {
+ &:not(:first-child):not(:first-child) {
+ margin-top: bk.$spacing-9;
+
+ .bk-stepper__item__circle {
+ &::before {
+ position: absolute;
+ content: '';
+ width: 0;
+ height: bk.$spacing-9;
+ top: -42px;
+ left: 50%;
+ border: 0.5px solid #{bk.$theme-stepper-border-disabled};
+ }
+ }
+ }
+ }
+ }
+
+ .bk-stepper__item {
+ display: flex;
+ align-items: center;
+ color: #{bk.$theme-stepper-text-disabled};
+ cursor: pointer;
+
+ .bk-stepper__item__circle {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ margin-right: bk.$spacing-3;
+ border: 2px solid #{bk.$theme-stepper-border-disabled};
+ border-radius: 50%;
+ width: 28px;
+ height: 28px;
+ font-weight: bk.$font-weight-bold;
+ font-size: bk.$font-size-m;
+ }
+
+ .bk-stepper__item__circle__icon {
+ font-size: bk.$font-size-xs;
+ }
+
+ .bk-stepper__item__title {
+ font-size: bk.$font-size-m;
+ }
+
+ .bk-stepper__item__optional {
+ margin-left: bk.$spacing-2;
+ font-size: bk.$font-size-xs;
+ }
+
+ &[aria-selected="true"] {
+ color: #{bk.$theme-stepper-text-selected};
+
+ .bk-stepper__item__circle {
+ border-color: #{bk.$theme-stepper-border-default};
+ background-color: #{bk.$theme-stepper-background-default};
+ color: #{bk.$theme-stepper-text-selected-number};
+ }
+ }
+
+ &.bk-stepper__item--checked {
+ color: #{bk.$theme-stepper-text-selected};
+
+ .bk-stepper__item__circle {
+ border-color: #{bk.$theme-stepper-border-default};
+ }
+ }
+ }
+ }
+}
diff --git a/src/components/navigations/Stepper/Stepper.stories.tsx b/src/components/navigations/Stepper/Stepper.stories.tsx
new file mode 100644
index 0000000..8bea494
--- /dev/null
+++ b/src/components/navigations/Stepper/Stepper.stories.tsx
@@ -0,0 +1,68 @@
+/* Copyright (c) Fortanix, Inc.
+|* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
+|* the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import type { Meta, StoryObj } from '@storybook/react';
+
+import * as React from 'react';
+
+import { Stepper, Step } from './Stepper.tsx';
+
+
+type StepperArgs = React.ComponentProps;
+type Story = StoryObj;
+
+export default {
+ component: Stepper,
+ parameters: {
+ layout: 'padded',
+ },
+ tags: ['autodocs'],
+ argTypes: {
+ },
+ args: {},
+ render: (args) => ,
+} satisfies Meta;
+
+const defaultSteps: Step[] = [1,2,3,4].map(index => {
+ return {
+ stepKey: `${index}`,
+ title: `Step${index}`,
+ isOptional: index === 4,
+ };
+});
+
+type StepperWithTriggerProps = React.PropsWithChildren>;
+const StepperWithTrigger = (props: StepperWithTriggerProps) => {
+ const { steps = defaultSteps, activeKey, ...stepperContext } = props;
+ const [activeStepKey, setActiveStepKey] = React.useState(activeKey || '1');
+ return (
+
+ );
+};
+
+const BaseStory: Story = {
+ args: {},
+ render: (args) => ,
+};
+
+export const Standard: Story = {
+ ...BaseStory,
+ name: 'Standard',
+ args: { ...BaseStory.args },
+};
+
+export const Horizontal: Story = {
+ ...BaseStory,
+ name: 'Horizontal',
+ args: {
+ ...BaseStory.args,
+ direction: 'horizontal',
+ },
+};
+
diff --git a/src/components/navigations/Stepper/Stepper.tsx b/src/components/navigations/Stepper/Stepper.tsx
new file mode 100644
index 0000000..7700790
--- /dev/null
+++ b/src/components/navigations/Stepper/Stepper.tsx
@@ -0,0 +1,95 @@
+/* Copyright (c) Fortanix, Inc.
+|* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
+|* the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import * as React from 'react';
+import { type ClassNameArgument, type ComponentProps, classNames as cx } from '../../../util/componentUtil.ts';
+
+import { Icon } from '../../graphics/Icon/Icon.tsx';
+
+import cl from './Stepper.module.scss';
+
+export { cl as SteppersClassNames };
+
+export type Step = {
+ stepKey: string,
+ title: React.ReactNode,
+ className?: ClassNameArgument,
+ hide?: boolean,
+ isOptional?: boolean,
+};
+
+export type StepperKey = Step['stepKey'];
+
+export type StepperDirection = 'vertical' | 'horizontal';
+
+export type StepperProps = React.PropsWithChildren & {
+ /** Whether this component should be unstyled. */
+ unstyled?: undefined | boolean,
+
+ /** Step items. */
+ steps: Step[],
+
+ /** Active key of step. */
+ activeKey?: string,
+
+ /** Whether this component should be displayed vertically or horizontally. */
+ direction?: StepperDirection,
+
+ /** Callback executed when active step is changed. */
+ onSwitch: (stepKey: StepperKey) => void,
+}>;
+/**
+ * A stepper component
+ */
+export const Stepper = (props: StepperProps) => {
+ const { unstyled = false, steps = [], activeKey, direction = 'vertical', onSwitch, ...propsRest } = props;
+
+ const handleKeyDown = (event: React.KeyboardEvent, stepKey: Step['stepKey']) => {
+ if (event.key === 'Enter') {
+ onSwitch(stepKey);
+ }
+ };
+
+ return (
+
+ {steps.map((step, index) => {
+ if (step.hide) return null;
+ const isActive = step.stepKey === activeKey;
+ const isChecked = index < steps.findIndex(step => step.stepKey === activeKey);
+ return (
+ - { onSwitch(step.stepKey); }}
+ onKeyDown={(event) => { handleKeyDown(event, step.stepKey); }}
+ >
+
+ {isChecked
+ ?
+ : index + 1
+ }
+
+ {step.title}
+ {step.isOptional && (Optional)}
+
+ )
+ })}
+
+ );
+};