diff --git a/js/react/lib/components/topic/index.ts b/js/react/lib/components/topic/index.ts
new file mode 100644
index 0000000..cf409b7
--- /dev/null
+++ b/js/react/lib/components/topic/index.ts
@@ -0,0 +1,3 @@
+export * from "./topic.component";
+export * from "./subtopic.component";
+export * from "./topic.types";
diff --git a/js/react/lib/components/topic/subtopic.component.tsx b/js/react/lib/components/topic/subtopic.component.tsx
new file mode 100644
index 0000000..428ee79
--- /dev/null
+++ b/js/react/lib/components/topic/subtopic.component.tsx
@@ -0,0 +1,16 @@
+import { Badge } from "../badge";
+import { Level } from "../level";
+import { TopicElement } from "./topic.types";
+import { withAs } from "@/utils/hoc";
+
+export const SubTopic = withAs(
+ (Component, { level, state, title, ...rest }: TopicElement) => {
+ return (
+
+
+ {title}
+
+
+ );
+ }
+);
diff --git a/js/react/lib/components/topic/topic.component.tsx b/js/react/lib/components/topic/topic.component.tsx
new file mode 100644
index 0000000..573cb4e
--- /dev/null
+++ b/js/react/lib/components/topic/topic.component.tsx
@@ -0,0 +1,20 @@
+import { PropsWithChildren } from "react";
+import { ArrowDown } from "@/icons";
+import { Badge } from "../badge";
+import { Level } from "../level";
+import { TopicElement } from "./topic.types";
+
+type TopicProps = PropsWithChildren & TopicElement;
+export const Topic = ({ level, title, state, children }: TopicProps) => {
+ return (
+
+
+
+ {title}
+
+
+
+ {children}
+
+ );
+};
diff --git a/js/react/lib/components/topic/topic.types.ts b/js/react/lib/components/topic/topic.types.ts
new file mode 100644
index 0000000..87d0a28
--- /dev/null
+++ b/js/react/lib/components/topic/topic.types.ts
@@ -0,0 +1,8 @@
+import { BadgeVariants } from "../badge/badge.const";
+import { LevelVariants } from "../level/level.const";
+
+export type TopicElement = {
+ level: LevelVariants;
+ title: string;
+ state: BadgeVariants;
+};
diff --git a/js/react/lib/index.ts b/js/react/lib/index.ts
index dda37ef..564607d 100644
--- a/js/react/lib/index.ts
+++ b/js/react/lib/index.ts
@@ -11,4 +11,5 @@ export * from "./components/radio";
export * from "./components/badge";
export * from "./components/dropdown";
export * from "./components/calendar";
+export * from "./components/topic";
export * from "./icons";
diff --git a/js/react/showcase/App.tsx b/js/react/showcase/App.tsx
index 89fe5ef..e600b74 100644
--- a/js/react/showcase/App.tsx
+++ b/js/react/showcase/App.tsx
@@ -14,6 +14,8 @@ import {
DropdownState,
Calendar,
CalendarRangeDate,
+ Topic,
+ SubTopic,
} from "@rustlanges/react";
import { ShowComponent } from "./ShowComponent";
import { useState } from "react";
@@ -279,6 +281,40 @@ export function App() {
+
+
);
}
diff --git a/styles/components.css b/styles/components.css
index dc9ae68..fb78fe5 100644
--- a/styles/components.css
+++ b/styles/components.css
@@ -9,3 +9,4 @@
@import "./components/radio.css";
@import "./components/tag.css";
@import "./components/text.css";
+@import "./components/topic.css";
diff --git a/styles/components/topic.css b/styles/components/topic.css
new file mode 100644
index 0000000..daaee30
--- /dev/null
+++ b/styles/components/topic.css
@@ -0,0 +1,35 @@
+@layer component {
+ .rustlanges-topic {
+ @apply w-full;
+ }
+ .rustlanges-topic[open] svg {
+ @apply -rotate-180;
+ }
+
+ .rustlanges-topic__summary::-webkit-details-marker {
+ display: none;
+ }
+
+ .rustlanges-topic__summary {
+ @apply grid w-full grid-cols-[auto_1fr_auto_auto] items-center gap-2 border-y border-black px-3 py-4;
+
+ & > svg {
+ @apply duration-400 size-6 transition;
+ }
+ }
+
+ .rustlanges-topic__title {
+ @apply text-neutral-950 dark:text-neutral-50;
+ }
+
+ .rustlanges-subtopic {
+ @apply grid w-full grid-cols-[auto_1fr_auto] items-center gap-2 border-b px-6 py-2.5;
+ @apply border-b-gray dark:border-b-neutral-600;
+ }
+
+ .rustlanges-subtopic__title {
+ @apply text-neutral-950;
+ @apply desktop:text-sm text-xs;
+ @apply dark:text-neutral-50;
+ }
+}