diff --git a/docs/app/docs/components/label/docs/codeUsage.js b/docs/app/docs/components/label/docs/codeUsage.js
new file mode 100644
index 000000000..e0c6c4e97
--- /dev/null
+++ b/docs/app/docs/components/label/docs/codeUsage.js
@@ -0,0 +1,26 @@
+// Import API documentation
+import label_api_SourceCode from './component_api/label.tsx';
+
+const code = {
+ javascript: {
+ code: `import Label from "@radui/ui/Label"\n\nconst LabelExample = () => (\n
\n \n \n
\n)`,
+ },
+ css: {
+ code: `.rad-ui-label{\n display: block;\n font-weight: 500;\n margin-bottom: 4px;\n}`
+ },
+};
+
+// API documentation
+export const api_documentation = {
+ label: label_api_SourceCode
+};
+
+// Component features
+export const features = [
+ "Pairs text with form controls using htmlFor/id attributes",
+ "Clicking the label focuses the associated field",
+ "Improves accessibility and assistive technology support",
+ "Supports custom root class names",
+];
+
+export default code;
diff --git a/docs/app/docs/components/label/docs/component_api/label.tsx b/docs/app/docs/components/label/docs/component_api/label.tsx
new file mode 100644
index 000000000..4a1d9e6d5
--- /dev/null
+++ b/docs/app/docs/components/label/docs/component_api/label.tsx
@@ -0,0 +1,45 @@
+const data = {
+ name: "Label",
+ description: "Associates text with form controls for accessibility.",
+ columns: [
+ { name: "Prop", id: "prop" },
+ { name: "Type", id: "type" },
+ { name: "Default", id: "default" }
+ ],
+ data: [
+ {
+ prop: {
+ name: "htmlFor",
+ info_tooltips: "ID of the form element this label describes",
+ },
+ type: "string",
+ default: "undefined",
+ },
+ {
+ prop: {
+ name: "children",
+ info_tooltips: "The label content",
+ },
+ type: "ReactNode",
+ default: "--",
+ },
+ {
+ prop: {
+ name: "className",
+ info_tooltips: "Additional CSS class names to apply",
+ },
+ type: "string",
+ default: "''",
+ },
+ {
+ prop: {
+ name: "customRootClass",
+ info_tooltips: "Custom root class name to override default styling",
+ },
+ type: "string",
+ default: "''",
+ }
+ ]
+};
+
+export default data;
diff --git a/docs/app/docs/components/label/page.mdx b/docs/app/docs/components/label/page.mdx
new file mode 100644
index 000000000..359e28c25
--- /dev/null
+++ b/docs/app/docs/components/label/page.mdx
@@ -0,0 +1,34 @@
+import PageDetails from '@/components/seo/PageDetails';
+import Documentation from "@/components/layout/Documentation/Documentation";
+import Label from "@radui/ui/Label";
+import codeUsage, { api_documentation, features } from "./docs/codeUsage";
+import labelMetadata from "./seo";
+
+export const metadata = labelMetadata;
+
+
+ {/* Component Hero */}
+
+
+
+
+
+
+
+ {/* Component Features */}
+
+
+ {/* API Documentation */}
+
+
+
diff --git a/docs/app/docs/components/label/seo.ts b/docs/app/docs/components/label/seo.ts
new file mode 100644
index 000000000..0158b9991
--- /dev/null
+++ b/docs/app/docs/components/label/seo.ts
@@ -0,0 +1,8 @@
+import generateSeoMetadata from "@/utils/seo/generateSeoMetadata";
+
+const labelMetadata = generateSeoMetadata({
+ title: "Label - Rad UI",
+ description: "Accessible label component for pairing text with form inputs and improving usability.",
+});
+
+export default labelMetadata;
diff --git a/docs/app/docs/docsNavigationSections.tsx b/docs/app/docs/docsNavigationSections.tsx
index d034c5f67..dec949451 100644
--- a/docs/app/docs/docsNavigationSections.tsx
+++ b/docs/app/docs/docsNavigationSections.tsx
@@ -97,6 +97,10 @@ export const docsNavigationSections = [
title:"Kbd",
path:"/docs/components/kbd"
},
+ {
+ title:"Label",
+ path:"/docs/components/label"
+ },
{
title:"Progress",
path:"/docs/components/progress"
diff --git a/scripts/RELEASED_COMPONENTS.cjs b/scripts/RELEASED_COMPONENTS.cjs
index 8fb9e97f0..820dbe5d8 100644
--- a/scripts/RELEASED_COMPONENTS.cjs
+++ b/scripts/RELEASED_COMPONENTS.cjs
@@ -16,6 +16,7 @@ const RELEASED_COMPONENTS = [
'Heading',
'Text',
'Kbd',
+ 'Label',
'Progress',
'Separator',
'Strong',
diff --git a/src/components/ui/Label/Label.tsx b/src/components/ui/Label/Label.tsx
new file mode 100644
index 000000000..ab3efb81b
--- /dev/null
+++ b/src/components/ui/Label/Label.tsx
@@ -0,0 +1,34 @@
+'use client';
+import React, { ElementRef, ComponentPropsWithoutRef } from 'react';
+import { clsx } from 'clsx';
+import { customClassSwitcher } from '~/core';
+
+const COMPONENT_NAME = 'Label';
+
+export interface LabelProps extends ComponentPropsWithoutRef<'label'> {
+ /**
+ * Custom root class to override default styling.
+ */
+ customRootClass?: string;
+}
+
+type LabelElement = ElementRef<'label'>;
+
+const Label = React.forwardRef(
+ ({ className, customRootClass, htmlFor, ...props }, ref) => {
+ const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME);
+ return (
+
+ );
+ }
+);
+
+Label.displayName = COMPONENT_NAME;
+
+export default Label;
+export type { LabelElement };
diff --git a/src/components/ui/Label/tests/Label.test.tsx b/src/components/ui/Label/tests/Label.test.tsx
new file mode 100644
index 000000000..625918d56
--- /dev/null
+++ b/src/components/ui/Label/tests/Label.test.tsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import Label from '../Label';
+
+/**
+ * Tests for Label component ensuring proper linkage and accessibility.
+ */
+describe('Label', () => {
+ test('associates with form field via htmlFor', () => {
+ render(
+
+
+
+
+ );
+ const input = screen.getByLabelText('Email');
+ expect(input).toBeInTheDocument();
+ });
+
+ test('clicking label focuses associated input', async () => {
+ const user = userEvent.setup();
+ render(
+
+
+
+
+ );
+ const label = screen.getByText('Name');
+ const input = screen.getByLabelText('Name');
+ await user.click(label);
+ expect(document.activeElement).toBe(input);
+ });
+});
diff --git a/styles/themes/components/label.scss b/styles/themes/components/label.scss
new file mode 100644
index 000000000..02b3152f4
--- /dev/null
+++ b/styles/themes/components/label.scss
@@ -0,0 +1,5 @@
+.rad-ui-label {
+ display: block;
+ font-weight: 500;
+ margin-bottom: 4px;
+}
diff --git a/styles/themes/default.scss b/styles/themes/default.scss
index bddf14cfc..df7175922 100644
--- a/styles/themes/default.scss
+++ b/styles/themes/default.scss
@@ -16,6 +16,7 @@
@use "components/heading";
@use "components/hover-card";
@use "components/kbd";
+@use "components/label";
@use "components/link";
@use "components/separator";
@use "components/tabs";