diff --git a/ui/.vscode/settings.json b/ui/.vscode/settings.json
new file mode 100644
index 0000000..fc5233d
--- /dev/null
+++ b/ui/.vscode/settings.json
@@ -0,0 +1,10 @@
+{
+ "files.exclude": {
+ "**/.git": true,
+ "**/.svn": true,
+ "**/.hg": true,
+ "**/.DS_Store": true,
+ "**/Thumbs.db": true
+ },
+ "hide-files.files": []
+}
\ No newline at end of file
diff --git a/ui/content/docs/components/tabs.mdx b/ui/content/docs/components/tabs.mdx
new file mode 100644
index 0000000..a068857
--- /dev/null
+++ b/ui/content/docs/components/tabs.mdx
@@ -0,0 +1,123 @@
+---
+title: Tabs
+description: A tabs component
+---
+
+import { Tabs, TabsList, TabsTrigger, TabsContent } from "@trakteer/tabs";
+import { TypeTable } from "fumadocs-ui/components/type-table";
+
+
+
+
+ Home
+ Profile
+ Settings
+
+
+
+ This is Home tab.
+
+
+ This is Profile tab.
+
+
+ This is Settings tab.
+
+
+
+
+### Usage
+
+```bash tab="npm"
+npx @fydemy/ui@latest add tabs
+```
+
+```bash tab="pnpm"
+pnpm dlx @fydemy/ui@latest add tabs
+```
+
+```bash tab="yarn"
+yarn @fydemy/ui@latest add tabs
+```
+
+```bash tab="bun"
+bunx --bun @fydemy/ui@latest add tabs
+```
+
+```jsx
+
+
+ Home
+ Profile
+ Settings
+
+
+
+ This is Home tab.
+
+
+ This is Profile tab.
+
+
+ This is Settings tab.
+
+
+```
+
+
+
+### Examples
+
+import { Home, UserRound, Settings } from 'lucide-react';
+
+#### With Icons
+
+A tabs component with icons beside the text. Use `lucide-react` icon is recommended.
+
+
+
+
+ Home
+ Profile
+ Settings
+
+
+
+ Welcome to your home dashboard.
+
+
+ Manage your profile settings here.
+
+
+ Configure your application preferences.
+
+
+
+
+```jsx
+
+
+ Home
+ Profile
+ Settings
+
+
+
+ Welcome to your home dashboard.
+
+
+ Manage your profile settings here.
+
+
+ Configure your application preferences.
+
+
+```
diff --git a/ui/public/trakteer/index.css b/ui/public/trakteer/index.css
index f33aa61..20cbe84 100644
--- a/ui/public/trakteer/index.css
+++ b/ui/public/trakteer/index.css
@@ -17,9 +17,49 @@
--trakteer-dark: #242525;
--trakteer-dark-border: #101010;
--trakteer-dark-color: #fefefe;
+
+ --trakteer-bg-primary: #ffffff;
+ --trakteer-bg-secondary: #f9fafb;
+
+ --trakteer-border-light: #e0e0e0;
+ --trakteer-border-medium: #cccccc;
+
+ --trakteer-text-primary: #242525;
+ --trakteer-text-secondary: #888888;
+ --trakteer-text-muted: #a0a0a0;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --trakteer-default: #3cd278;
+ --trakteer-default-border: #2b9555;
+ --trakteer-default-color: #f8f8f8;
+
+ --trakteer-outline: #333333;
+ --trakteer-outline-border: #666666;
+ --trakteer-outline-color: #a0a0a0;
+
+ --trakteer-destructive: #ff4757;
+ --trakteer-destructive-border: #c23342;
+ --trakteer-destructive-color: #f8f8f8;
+
+ --trakteer-dark: #fefefe;
+ --trakteer-dark-border: #333333;
+ --trakteer-dark-color: #242525;
+
+ --trakteer-bg-primary: #1a1a1a;
+ --trakteer-bg-secondary: #242424;
+
+ --trakteer-border-light: #333333;
+ --trakteer-border-medium: #444444;
+
+ --trakteer-text-primary: #fefefe;
+ --trakteer-text-secondary: #a0a0a0;
+ --trakteer-text-muted: #888888;
+ }
}
[data-theme="trakteer"] {
margin: 0;
padding: 0;
-}
+}
\ No newline at end of file
diff --git a/ui/public/trakteer/tabs/index.css b/ui/public/trakteer/tabs/index.css
new file mode 100644
index 0000000..86294f7
--- /dev/null
+++ b/ui/public/trakteer/tabs/index.css
@@ -0,0 +1,48 @@
+/* Tabs Container */
+.tabs[data-theme="trakteer"] {
+ padding: 1.5rem;
+ font-family: "Fredoka", sans-serif;
+ background-color: var(--trakteer-bg-primary);
+ border: 1px solid var(--trakteer-border-light);
+ border-bottom: 4px solid var(--trakteer-border-light);
+ border-radius: 1rem;
+ transition: background-color 0.3s ease, border-color 0.3s ease;
+}
+
+/* Tabs List */
+.tabs[data-theme="trakteer"]>.tabs-list {
+ display: flex;
+ align-items: baseline;
+ border-bottom: 1px solid var(--trakteer-border-light);
+ transition: border-color 0.3s ease;
+}
+
+/* Tabs Trigger */
+.tabs[data-theme="trakteer"] [role="button"].tabs-trigger {
+ position: relative;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.625rem 1rem;
+ font-weight: 500;
+ color: var(--trakteer-text-secondary);
+ cursor: pointer;
+ transition: color 0.2s ease;
+}
+
+.tabs[data-theme="trakteer"] [role="button"].tabs-trigger:hover {
+ color: var(--trakteer-text-primary);
+}
+
+.tabs[data-theme="trakteer"] [role="button"].tabs-trigger[aria-selected="true"] {
+ color: var(--trakteer-default);
+ margin-bottom: -1px;
+ border-bottom: 2px solid var(--trakteer-default);
+}
+
+/* Tabs Content */
+.tabs[data-theme="trakteer"]>.tabs-contents {
+ padding-top: 1rem;
+ color: var(--trakteer-text-primary);
+ transition: color 0.3s ease;
+}
\ No newline at end of file
diff --git a/ui/public/trakteer/tabs/index.tsx b/ui/public/trakteer/tabs/index.tsx
new file mode 100644
index 0000000..ce145d8
--- /dev/null
+++ b/ui/public/trakteer/tabs/index.tsx
@@ -0,0 +1,93 @@
+'use client';
+
+import { createContext, useContext, useState, type ReactNode } from 'react';
+import "../index.css";
+import "./index.css";
+
+// Types
+interface TabsContextType {
+ value: string;
+ setValue: (v: string) => void;
+}
+
+interface TabsProps {
+ defaultValue: string;
+ children: ReactNode;
+ variant: TabsVariant;
+}
+
+type TabsVariant = "line";
+
+interface TabsListProps {
+ children: ReactNode;
+}
+
+interface TabsTriggerProps {
+ value: string;
+ children: ReactNode;
+}
+
+interface TabsContentProps {
+ value: string;
+ children: ReactNode;
+}
+
+// Context
+const TabsContext = createContext(undefined);
+
+const useTabsContext = () => {
+ const context = useContext(TabsContext);
+ if (!context) {
+ throw new Error('Tabs components must be used within a component');
+ }
+ return context;
+};
+
+// Components
+const Tabs = ({ defaultValue, children, variant = "line" }: TabsProps) => {
+ const [value, setValue] = useState(defaultValue);
+
+ return (
+
+
+ {children}
+
+
+ );
+};
+
+const TabsList = ({ children }: TabsListProps) => {
+ return {children}
;
+};
+
+const TabsTrigger = ({ value, children }: TabsTriggerProps) => {
+ const { value: activeValue, setValue } = useTabsContext();
+ const isActive = activeValue === value;
+
+ return (
+ setValue(value)}
+ aria-selected={isActive}
+ className='tabs-trigger'
+ >
+ {children}
+
+ );
+};
+
+const TabsContent = ({ value, children }: TabsContentProps) => {
+ const { value: activeValue } = useTabsContext();
+
+ if (activeValue !== value) {
+ return null;
+ }
+
+ return {children}
;
+};
+
+export { Tabs, TabsList, TabsTrigger, TabsContent };
\ No newline at end of file