diff --git a/apps/ui/content/docs/(root)/changelog.mdx b/apps/ui/content/docs/(root)/changelog.mdx
index 61597e582..bd2c89900 100644
--- a/apps/ui/content/docs/(root)/changelog.mdx
+++ b/apps/ui/content/docs/(root)/changelog.mdx
@@ -3,6 +3,38 @@ title: Changelog
description: Breaking changes, migration guides, and notable updates.
---
+## August 17, 2026
+
+### Tabs sizing and segmented control styles
+
+**`TabsList`** now supports a **`size`** prop with **`"sm"`**, **`"default"`**, and **`"lg"`** values. The size propagates to its **`TabsTab`** children, and an individual tab can override the inherited size when necessary.
+
+The default Tabs appearance now uses the same optical item sizing as the [Segmented Control](/ui/docs/components/segmented-control) pattern. The default rendered height is slightly smaller than before so the inset active indicator remains visually balanced beside a Button of the corresponding size.
+
+Tabs now imports its size map from the **`@coss/segmented-control`** registry library. **`@coss/tabs`** declares this as a registry dependency, so the shadcn CLI installs it automatically with a fresh installation or CLI-managed update.
+
+**Migration:**
+
+Existing Tabs JSX requires no changes because **`"default"`** remains the default size. If you replace your local Tabs component through the CLI, review any local customizations before accepting the overwrite:
+
+```bash
+npx shadcn@latest add @coss/tabs
+```
+
+If you update **`tabs.tsx`** manually, install the shared library first and then merge the latest Tabs implementation. Copying only the new Tabs file without this dependency results in an unresolved import.
+
+```bash
+npx shadcn@latest add @coss/segmented-control
+```
+
+After updating, remove local height or horizontal-padding utilities from **`TabsList`** and **`TabsTab`** only where they unintentionally override the new **`size`** API. Keep intentional product-specific overrides.
+
+**Agent migration prompt:**
+
+```text
+Update the local coss Tabs component to the latest segmented-control sizing while preserving project-specific customizations. Install the shared registry dependency with `npx shadcn@latest add @coss/segmented-control`, then merge the latest @coss/tabs implementation. TabsList now accepts size="sm" | "default" | "lg" (default: "default") and propagates it to TabsTab; a TabsTab size prop overrides the inherited value. Replace the old hard-coded TabsTab height and horizontal-padding classes with segmentedControlItemSizeClassNames from the shared library. Existing Tabs JSX does not need a size prop. Audit local h-* and px-* overrides on TabsList/TabsTab and remove only those that unintentionally conflict with the new size API. Run formatting and typechecking, then visually verify default and underline Tabs in every size used by the app.
+```
+
## July 31, 2026
### Scroll Area — overscroll contain
diff --git a/apps/ui/content/docs/components/meta.json b/apps/ui/content/docs/components/meta.json
index 92f91f4f3..63fd3eae6 100644
--- a/apps/ui/content/docs/components/meta.json
+++ b/apps/ui/content/docs/components/meta.json
@@ -40,6 +40,7 @@
"radio-group",
"scroll-area",
"select",
+ "segmented-control",
"separator",
"sheet",
"skeleton",
diff --git a/apps/ui/content/docs/components/radio-group.mdx b/apps/ui/content/docs/components/radio-group.mdx
index 5765ed8d9..2954521d6 100644
--- a/apps/ui/content/docs/components/radio-group.mdx
+++ b/apps/ui/content/docs/components/radio-group.mdx
@@ -81,6 +81,8 @@ Individual radio button. Styled wrapper for `Radio.Root` from Base UI with built
For accessible labelling and validation, prefer using the `Field` component to wrap radio buttons. See the related example: [Radio Group field](/ui/docs/components/field#radio-group-field).
+For tabs-style mutually exclusive choices, see the [Segmented Control](/ui/docs/components/segmented-control) pattern.
+
### Disabled
diff --git a/apps/ui/content/docs/components/segmented-control.mdx b/apps/ui/content/docs/components/segmented-control.mdx
new file mode 100644
index 000000000..9ccbf2113
--- /dev/null
+++ b/apps/ui/content/docs/components/segmented-control.mdx
@@ -0,0 +1,114 @@
+---
+title: Segmented Control
+description: A visual pattern for presenting related choices, navigation destinations, filters, or content views.
+---
+
+
+
+## About
+
+A segmented control is a visual pattern, not a standalone behavior. COSS uses the same presentation across several components while preserving the semantics, keyboard interactions, and state model of each underlying primitive.
+
+## Choose the right primitive
+
+| Intent | Use | Why |
+| ------ | --- | --- |
+| Choose one value in a form | [Radio Group](/ui/docs/components/radio-group) | Represents a mutually exclusive value and participates in form state. |
+| Navigate to another URL or route | Navigation links | Preserves link behavior, browser history, and `aria-current`. |
+| Apply an exclusive filter or mode | [Toggle Group](/ui/docs/components/toggle-group) | Represents the pressed state of an action that may be cleared. |
+| Switch between related panels | [Tabs](/ui/docs/components/tabs) | Connects each tab to an associated content panel. |
+
+Choose the primitive from the interaction first, then apply the segmented-control styling. Visual similarity alone is not a reason to use Tabs or Toggle Group.
+
+## Installation
+
+Segmented controls are provided as particles. Install the implementation and size that match your interaction:
+
+| Implementation | Small | Default | Large |
+| -------------- | ----- | ------- | ----- |
+| Radio Group | `@coss/p-radio-group-7` | `@coss/p-radio-group-8` | `@coss/p-radio-group-9` |
+| Navigation | `@coss/p-navigation-2` | `@coss/p-navigation-1` | `@coss/p-navigation-3` |
+
+For example, install the default Radio Group version with:
+
+```bash
+npx shadcn@latest add @coss/p-radio-group-8
+```
+
+The CLI installs the shared `segmented-control` styling library and the required primitive automatically.
+
+## Shared styling
+
+For a custom composition, install the styling library directly:
+
+```bash
+npx shadcn@latest add @coss/segmented-control
+```
+
+The library exports a root class and an item recipe:
+
+```tsx
+import {
+ segmentedControlItemVariants,
+ segmentedControlRootClassName,
+} from "@/lib/segmented-control"
+```
+
+```tsx
+const itemClassName = segmentedControlItemVariants({
+ size: "default",
+ state: "checked",
+})
+```
+
+| Option | Values | Description |
+| ------ | ------ | ----------- |
+| `size` | `"sm" \| "default" \| "lg"` | Controls item height and horizontal padding. |
+| `state` | `"checked" \| "current" \| "pressed"` | Selects the state attribute used by the underlying element. |
+
+Use `checked` with Radio Group, `current` with navigation links, and `pressed` with Toggle Group. Tabs retain their own animated indicator and do not use the shared state recipe.
+
+At the outside edges, the item padding and the surface's `p-0.5` inset combine to match the horizontal padding of the corresponding Button size. The outer segmented surface is slightly taller than that Button to optically balance its inset selected item when the controls appear next to each other.
+
+
+
+## Radio options
+
+Use Radio Group when the selected segment represents a mutually exclusive value, especially in forms.
+
+### Small Radio Group
+
+
+
+### Default Radio Group
+
+
+
+### Large Radio Group
+
+
+
+## Navigation
+
+Use links when each segment points to a different destination. Apply `aria-current="page"` to the active link.
+
+### Small Navigation
+
+
+
+### Default Navigation
+
+
+
+### Large Navigation
+
+
+
+## Related content
+
+Use Tabs when each segment controls an associated content panel. Tabs share the visual language of segmented controls but keep their animated indicator, orientation support, and panel semantics.
+
+
diff --git a/apps/ui/content/docs/components/tabs.mdx b/apps/ui/content/docs/components/tabs.mdx
index 60e7699f3..c08dd1dca 100644
--- a/apps/ui/content/docs/components/tabs.mdx
+++ b/apps/ui/content/docs/components/tabs.mdx
@@ -22,13 +22,25 @@ links:
npx shadcn@latest add @coss/tabs
```
+The CLI installs the shared `@coss/segmented-control` registry dependency automatically.
+
-Copy and paste the following code into your project.
+Install the following dependencies:
+
+```bash
+npm install @base-ui/react class-variance-authority
+```
+
+Copy the shared segmented control styling into your project.
+
+
+
+Copy and paste the Tabs component into your project.
@@ -65,14 +77,15 @@ import { Tabs, TabsList, TabsPanel, TabsTab } from "@/components/ui/tabs"
Root component. Styled wrapper for `Tabs.Root` from Base UI.
-| Prop | Type | Default | Description |
-| --------- | ---------------------------- | ----------- | ------------------------------------------------ |
-| `variant` | `"default" \| "underline"` | `"default"` | Controls the tabs styling |
-
### TabsList
Container for tab triggers. Styled wrapper for `Tabs.List` from Base UI.
+| Prop | Type | Default | Description |
+| --------- | -------------------------- | ----------- | ----------------------------------- |
+| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Controls the size of all tab items. |
+| `variant` | `"default" \| "underline"` | `"default"` | Controls the tabs styling. |
+
### TabsTab
Individual tab trigger. Styled wrapper for `Tabs.Tab` from Base UI.
@@ -87,6 +100,18 @@ Visual indicator for the active tab. Styled wrapper for `Tabs.Indicator` from Ba
## Examples
+For guidance on choosing between Tabs, Radio Group, Toggle Group, and navigation links, see the [Segmented Control](/ui/docs/components/segmented-control) pattern.
+
+Tabs use the same optical sizing as segmented controls. The default surface is slightly taller than a Button of the same size so its inset active indicator remains visually balanced alongside adjacent controls.
+
+### Small
+
+
+
+### Large
+
+
+
### Underline Variant
diff --git a/apps/ui/lib/docs.ts b/apps/ui/lib/docs.ts
index d9f5d1169..baff695c5 100644
--- a/apps/ui/lib/docs.ts
+++ b/apps/ui/lib/docs.ts
@@ -1,5 +1,4 @@
export const PAGES_NEW = [
// "/docs/components/{component-name}",
- "/docs/components/context-menu",
- "/docs/components/otp-field",
+ "/docs/components/segmented-control",
];
diff --git a/apps/ui/public/r/p-navigation-1.json b/apps/ui/public/r/p-navigation-1.json
new file mode 100644
index 000000000..256c65ca6
--- /dev/null
+++ b/apps/ui/public/r/p-navigation-1.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
+ "name": "p-navigation-1",
+ "description": "Segmented navigation built with links",
+ "registryDependencies": [
+ "@coss/segmented-control"
+ ],
+ "files": [
+ {
+ "path": "registry/default/particles/p-navigation-1.tsx",
+ "content": "import {\n segmentedControlItemVariants,\n segmentedControlRootClassName,\n} from \"@/registry/default/lib/segmented-control\";\n\nconst itemClassName = segmentedControlItemVariants({ state: \"current\" });\n\nexport default function Particle() {\n return (\n \n );\n}\n",
+ "type": "registry:block"
+ }
+ ],
+ "categories": [
+ "navigation",
+ "segmented control"
+ ],
+ "type": "registry:block"
+}
\ No newline at end of file
diff --git a/apps/ui/public/r/p-navigation-2.json b/apps/ui/public/r/p-navigation-2.json
new file mode 100644
index 000000000..155ad9c63
--- /dev/null
+++ b/apps/ui/public/r/p-navigation-2.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
+ "name": "p-navigation-2",
+ "description": "Small segmented navigation built with links",
+ "registryDependencies": [
+ "@coss/segmented-control"
+ ],
+ "files": [
+ {
+ "path": "registry/default/particles/p-navigation-2.tsx",
+ "content": "import {\n segmentedControlItemVariants,\n segmentedControlRootClassName,\n} from \"@/registry/default/lib/segmented-control\";\n\nconst itemClassName = segmentedControlItemVariants({\n size: \"sm\",\n state: \"current\",\n});\n\nexport default function Particle() {\n return (\n \n );\n}\n",
+ "type": "registry:block"
+ }
+ ],
+ "categories": [
+ "navigation",
+ "segmented control"
+ ],
+ "type": "registry:block"
+}
\ No newline at end of file
diff --git a/apps/ui/public/r/p-navigation-3.json b/apps/ui/public/r/p-navigation-3.json
new file mode 100644
index 000000000..bc2a61eb7
--- /dev/null
+++ b/apps/ui/public/r/p-navigation-3.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
+ "name": "p-navigation-3",
+ "description": "Large segmented navigation built with links",
+ "registryDependencies": [
+ "@coss/segmented-control"
+ ],
+ "files": [
+ {
+ "path": "registry/default/particles/p-navigation-3.tsx",
+ "content": "import {\n segmentedControlItemVariants,\n segmentedControlRootClassName,\n} from \"@/registry/default/lib/segmented-control\";\n\nconst itemClassName = segmentedControlItemVariants({\n size: \"lg\",\n state: \"current\",\n});\n\nexport default function Particle() {\n return (\n \n );\n}\n",
+ "type": "registry:block"
+ }
+ ],
+ "categories": [
+ "navigation",
+ "segmented control"
+ ],
+ "type": "registry:block"
+}
\ No newline at end of file
diff --git a/apps/ui/public/r/p-radio-group-7.json b/apps/ui/public/r/p-radio-group-7.json
new file mode 100644
index 000000000..be08b123d
--- /dev/null
+++ b/apps/ui/public/r/p-radio-group-7.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
+ "name": "p-radio-group-7",
+ "description": "Small segmented control built with a radio group",
+ "registryDependencies": [
+ "@coss/radio-group",
+ "@coss/segmented-control"
+ ],
+ "files": [
+ {
+ "path": "registry/default/particles/p-radio-group-7.tsx",
+ "content": "\"use client\";\n\nimport {\n segmentedControlItemVariants,\n segmentedControlRootClassName,\n} from \"@/registry/default/lib/segmented-control\";\nimport {\n RadioGroupPrimitive,\n RadioPrimitive,\n} from \"@/registry/default/ui/radio-group\";\n\nconst itemClassName = segmentedControlItemVariants({\n className: \"grow\",\n size: \"sm\",\n state: \"checked\",\n});\n\nexport default function Particle() {\n return (\n \n \n Monthly\n \n \n Yearly\n \n \n );\n}\n",
+ "type": "registry:block"
+ }
+ ],
+ "categories": [
+ "radio group",
+ "segmented control"
+ ],
+ "type": "registry:block"
+}
\ No newline at end of file
diff --git a/apps/ui/public/r/p-radio-group-8.json b/apps/ui/public/r/p-radio-group-8.json
new file mode 100644
index 000000000..5294b70e9
--- /dev/null
+++ b/apps/ui/public/r/p-radio-group-8.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
+ "name": "p-radio-group-8",
+ "description": "Segmented control built with a radio group",
+ "registryDependencies": [
+ "@coss/radio-group",
+ "@coss/segmented-control"
+ ],
+ "files": [
+ {
+ "path": "registry/default/particles/p-radio-group-8.tsx",
+ "content": "\"use client\";\n\nimport {\n segmentedControlItemVariants,\n segmentedControlRootClassName,\n} from \"@/registry/default/lib/segmented-control\";\nimport {\n RadioGroupPrimitive,\n RadioPrimitive,\n} from \"@/registry/default/ui/radio-group\";\n\nconst itemClassName = segmentedControlItemVariants({\n className: \"grow\",\n state: \"checked\",\n});\n\nexport default function Particle() {\n return (\n \n \n Monthly\n \n \n Yearly\n \n \n );\n}\n",
+ "type": "registry:block"
+ }
+ ],
+ "categories": [
+ "radio group",
+ "segmented control"
+ ],
+ "type": "registry:block"
+}
\ No newline at end of file
diff --git a/apps/ui/public/r/p-radio-group-9.json b/apps/ui/public/r/p-radio-group-9.json
new file mode 100644
index 000000000..f81f3f5e5
--- /dev/null
+++ b/apps/ui/public/r/p-radio-group-9.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
+ "name": "p-radio-group-9",
+ "description": "Large segmented control built with a radio group",
+ "registryDependencies": [
+ "@coss/radio-group",
+ "@coss/segmented-control"
+ ],
+ "files": [
+ {
+ "path": "registry/default/particles/p-radio-group-9.tsx",
+ "content": "\"use client\";\n\nimport {\n segmentedControlItemVariants,\n segmentedControlRootClassName,\n} from \"@/registry/default/lib/segmented-control\";\nimport {\n RadioGroupPrimitive,\n RadioPrimitive,\n} from \"@/registry/default/ui/radio-group\";\n\nconst itemClassName = segmentedControlItemVariants({\n className: \"grow\",\n size: \"lg\",\n state: \"checked\",\n});\n\nexport default function Particle() {\n return (\n \n \n Monthly\n \n \n Yearly\n \n \n );\n}\n",
+ "type": "registry:block"
+ }
+ ],
+ "categories": [
+ "radio group",
+ "segmented control"
+ ],
+ "type": "registry:block"
+}
\ No newline at end of file
diff --git a/apps/ui/public/r/p-tabs-1.json b/apps/ui/public/r/p-tabs-1.json
index 7ecaecc3a..5f219095b 100644
--- a/apps/ui/public/r/p-tabs-1.json
+++ b/apps/ui/public/r/p-tabs-1.json
@@ -1,7 +1,7 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "p-tabs-1",
- "description": "Basic tabs",
+ "description": "Segmented control built with tabs",
"registryDependencies": [
"@coss/tabs"
],
diff --git a/apps/ui/public/r/p-tabs-14.json b/apps/ui/public/r/p-tabs-14.json
new file mode 100644
index 000000000..ba56c5454
--- /dev/null
+++ b/apps/ui/public/r/p-tabs-14.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
+ "name": "p-tabs-14",
+ "description": "Small segmented control built with tabs",
+ "registryDependencies": [
+ "@coss/tabs"
+ ],
+ "files": [
+ {
+ "path": "registry/default/particles/p-tabs-14.tsx",
+ "content": "import { Tabs, TabsList, TabsPanel, TabsTab } from \"@/registry/default/ui/tabs\";\n\nexport default function Particle() {\n return (\n \n \n Tab 1\n Tab 2\n Tab 3\n \n \n
\n Tab 1 content\n
\n \n \n
\n Tab 2 content\n
\n \n \n
\n Tab 3 content\n
\n \n \n );\n}\n",
+ "type": "registry:block"
+ }
+ ],
+ "categories": [
+ "tabs"
+ ],
+ "type": "registry:block"
+}
\ No newline at end of file
diff --git a/apps/ui/public/r/p-tabs-15.json b/apps/ui/public/r/p-tabs-15.json
new file mode 100644
index 000000000..4e23b4a39
--- /dev/null
+++ b/apps/ui/public/r/p-tabs-15.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
+ "name": "p-tabs-15",
+ "description": "Large segmented control built with tabs",
+ "registryDependencies": [
+ "@coss/tabs"
+ ],
+ "files": [
+ {
+ "path": "registry/default/particles/p-tabs-15.tsx",
+ "content": "import { Tabs, TabsList, TabsPanel, TabsTab } from \"@/registry/default/ui/tabs\";\n\nexport default function Particle() {\n return (\n \n \n Tab 1\n Tab 2\n Tab 3\n \n \n