diff --git a/apps/web/src/app/modules/shared.module.ts b/apps/web/src/app/modules/shared.module.ts
index 1bfbe632918..90d4faa922a 100644
--- a/apps/web/src/app/modules/shared.module.ts
+++ b/apps/web/src/app/modules/shared.module.ts
@@ -64,6 +64,7 @@ import {
FormFieldModule,
SubmitButtonModule,
MenuModule,
+ TabsModule,
} from "@bitwarden/components";
import { PasswordStrengthComponent } from "../components/password-strength.component";
@@ -138,6 +139,7 @@ registerLocaleData(localeZhTw, "zh-TW");
MenuModule,
FormFieldModule,
SubmitButtonModule,
+ TabsModule,
],
exports: [
CommonModule,
@@ -157,6 +159,7 @@ registerLocaleData(localeZhTw, "zh-TW");
FormFieldModule,
PasswordStrengthComponent,
SubmitButtonModule,
+ TabsModule,
],
providers: [DatePipe],
bootstrap: [],
diff --git a/libs/components/src/index.ts b/libs/components/src/index.ts
index cb45e12228e..f7c8962b638 100644
--- a/libs/components/src/index.ts
+++ b/libs/components/src/index.ts
@@ -6,3 +6,4 @@ export * from "./form-field";
export * from "./menu";
export * from "./utils/i18n-mock.service";
export * from "./submit-button";
+export * from "./tabs";
diff --git a/libs/components/src/tabs/index.ts b/libs/components/src/tabs/index.ts
new file mode 100644
index 00000000000..9b45ff1d43b
--- /dev/null
+++ b/libs/components/src/tabs/index.ts
@@ -0,0 +1,3 @@
+export * from "./tabs.module";
+export * from "./tab-group.component";
+export * from "./tab-item.component";
diff --git a/libs/components/src/tabs/tab-group.component.html b/libs/components/src/tabs/tab-group.component.html
new file mode 100644
index 00000000000..aa29a2f58af
--- /dev/null
+++ b/libs/components/src/tabs/tab-group.component.html
@@ -0,0 +1,6 @@
+
+
+
diff --git a/libs/components/src/tabs/tab-group.component.ts b/libs/components/src/tabs/tab-group.component.ts
new file mode 100644
index 00000000000..856ab1f1e22
--- /dev/null
+++ b/libs/components/src/tabs/tab-group.component.ts
@@ -0,0 +1,7 @@
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "bit-tab-group",
+ templateUrl: "./tab-group.component.html",
+})
+export class TabGroupComponent {}
diff --git a/libs/components/src/tabs/tab-item.component.html b/libs/components/src/tabs/tab-item.component.html
new file mode 100644
index 00000000000..951cfb3be18
--- /dev/null
+++ b/libs/components/src/tabs/tab-item.component.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
diff --git a/libs/components/src/tabs/tab-item.component.ts b/libs/components/src/tabs/tab-item.component.ts
new file mode 100644
index 00000000000..749775e91bf
--- /dev/null
+++ b/libs/components/src/tabs/tab-item.component.ts
@@ -0,0 +1,59 @@
+import { Component, Input } from "@angular/core";
+
+@Component({
+ selector: "bit-tab-item",
+ templateUrl: "./tab-item.component.html",
+})
+export class TabItemComponent {
+ @Input() route: string; // ['/route']
+ @Input() disabled = false;
+
+ get baseClassList(): string[] {
+ return [
+ "tw-block",
+ "tw-relative",
+ "tw-py-2",
+ "tw-px-4",
+ "tw-leading-5",
+ "tw-text-left",
+ "tw-font-semibold",
+ "tw-bg-transparent",
+ "tw-transition",
+ "tw-rounded-t",
+ "tw-border-0",
+ "tw-border-t-4",
+ "tw-border-t-transparent",
+ "tw-border-b",
+ "tw-border-b-secondary-300",
+ "tw-border-solid",
+ "tw-cursor-pointer",
+ "tw-box-border",
+ "tw-text-main",
+ "hover:tw-underline",
+ "hover:tw-text-main",
+ "focus:tw-z-10",
+ "focus:tw-outline-none",
+ "focus:tw-ring-2",
+ "focus:tw-ring-primary-700",
+ "disabled:tw-bg-secondary-100",
+ "disabled:tw-text-muted",
+ "disabled:tw-no-underline",
+ "disabled:tw-cursor-not-allowed",
+ ];
+ }
+
+ get activeClassList(): string {
+ return [
+ "tw-border-x",
+ "tw-border-x-secondary-300",
+ "tw-border-t-primary-500",
+ "tw-border-b-transparent",
+ "tw-bg-background",
+ "tw-text-primary-500",
+ "hover:tw-border-t-primary-700",
+ "hover:!tw-text-primary-700",
+ "focus:tw-border-t-primary-700",
+ "focus:tw-text-primary-700",
+ ].join(" ");
+ }
+}
diff --git a/libs/components/src/tabs/tabs.module.ts b/libs/components/src/tabs/tabs.module.ts
new file mode 100644
index 00000000000..016bde504a5
--- /dev/null
+++ b/libs/components/src/tabs/tabs.module.ts
@@ -0,0 +1,13 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { RouterModule } from "@angular/router";
+
+import { TabGroupComponent } from "./tab-group.component";
+import { TabItemComponent } from "./tab-item.component";
+
+@NgModule({
+ imports: [CommonModule, RouterModule],
+ exports: [TabGroupComponent, TabItemComponent],
+ declarations: [TabGroupComponent, TabItemComponent],
+})
+export class TabsModule {}
diff --git a/libs/components/src/tabs/tabs.stories.ts b/libs/components/src/tabs/tabs.stories.ts
new file mode 100644
index 00000000000..15ee57fa7d5
--- /dev/null
+++ b/libs/components/src/tabs/tabs.stories.ts
@@ -0,0 +1,84 @@
+import { CommonModule } from "@angular/common";
+import { Component } from "@angular/core";
+import { RouterModule } from "@angular/router";
+import { Meta, moduleMetadata, Story } from "@storybook/angular";
+
+import { TabGroupComponent } from "./tab-group.component";
+import { TabItemComponent } from "./tab-item.component";
+
+@Component({
+ selector: "bit-tab-active-dummy",
+ template: "Router - Active selected",
+})
+class ActiveDummyComponent {}
+
+@Component({
+ selector: "bit-tab-item-2-dummy",
+ template: "Router - Item 2 selected",
+})
+class ItemTwoDummyComponent {}
+
+@Component({
+ selector: "bit-tab-item-3-dummy",
+ template: "Router - Item 3 selected",
+})
+class ItemThreeDummyComponent {}
+
+@Component({
+ selector: "bit-tab-disabled-dummy",
+ template: "Router - Disabled selected",
+})
+class DisabledDummyComponent {}
+
+export default {
+ title: "Component Library/Tabs",
+ component: TabGroupComponent,
+ decorators: [
+ moduleMetadata({
+ declarations: [
+ TabGroupComponent,
+ TabItemComponent,
+ ActiveDummyComponent,
+ ItemTwoDummyComponent,
+ ItemThreeDummyComponent,
+ DisabledDummyComponent,
+ ],
+ imports: [
+ CommonModule,
+ RouterModule.forRoot(
+ [
+ { path: "", redirectTo: "active", pathMatch: "full" },
+ { path: "active", component: ActiveDummyComponent },
+ { path: "item-2", component: ItemTwoDummyComponent },
+ { path: "item-3", component: ItemThreeDummyComponent },
+ { path: "disabled", component: DisabledDummyComponent },
+ ],
+ { useHash: true }
+ ),
+ ],
+ }),
+ ],
+ parameters: {
+ design: {
+ type: "figma",
+ url: "https://www.figma.com/file/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?node-id=1881%3A17922",
+ },
+ },
+} as Meta;
+
+const TabGroupTemplate: Story = (args: TabGroupComponent) => ({
+ props: args,
+ template: `
+
+ Active
+ Item 2
+ Item 3
+ Disabled
+
+
+
+
+ `,
+});
+
+export const TabGroup = TabGroupTemplate.bind({});
diff --git a/libs/components/tailwind.config.base.js b/libs/components/tailwind.config.base.js
index fd0caec7490..31ff19cb501 100644
--- a/libs/components/tailwind.config.base.js
+++ b/libs/components/tailwind.config.base.js
@@ -60,6 +60,8 @@ module.exports = {
info: "var(--color-info-500)",
primary: {
300: "var(--color-primary-300)",
+ 500: "var(--color-primary-500)",
+ 700: "var(--color-primary-700)",
},
},
ringOffsetColor: ({ theme }) => ({