@viraui/react 2026.9.1 → 2026.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/catalog.json +6 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/tabs/tabs-list.js +29 -23
- package/dist/components/tabs/tabs-size-context.d.ts +4 -0
- package/dist/components/tabs/tabs-size-context.js +7 -0
- package/dist/components/tabs/tabs-tab.js +21 -16
- package/dist/components/tabs/tabs.d.ts +7 -0
- package/dist/components/tabs/tabs.js +12 -8
- package/dist/components/toggle-group/toggle-group.d.ts +3 -1
- package/dist/components/toggle-text-button/index.d.ts +2 -0
- package/dist/components/toggle-text-button/toggle-text-button.d.ts +27 -0
- package/dist/components/toggle-text-button/toggle-text-button.js +24 -0
- package/dist/index.js +2 -1
- package/dist/specs/components/icon-button/props.xml +2 -1
- package/dist/specs/components/icon-button/usage.xml +2 -1
- package/dist/specs/components/tabs/props.xml +3 -0
- package/dist/specs/components/tabs/usage.xml +1 -0
- package/dist/specs/components/toggle-button/props.xml +2 -0
- package/dist/specs/components/toggle-button/usage.xml +2 -0
- package/dist/specs/components/toggle-group/patterns.xml +2 -1
- package/dist/specs/components/toggle-group/usage.xml +5 -5
- package/dist/specs/components/toggle-text-button/a11y.xml +8 -0
- package/dist/specs/components/toggle-text-button/meta.xml +19 -0
- package/dist/specs/components/toggle-text-button/patterns.xml +44 -0
- package/dist/specs/components/toggle-text-button/props.xml +58 -0
- package/dist/specs/components/toggle-text-button/usage.xml +13 -0
- package/dist/specs/index.xml +11 -1
- package/package.json +2 -2
package/dist/catalog.json
CHANGED
|
@@ -302,6 +302,12 @@
|
|
|
302
302
|
"summary": "Toolbar toggle set with shared selection state; Stack direction follows orientation with zero default gap.",
|
|
303
303
|
"agentDocs": "./specs/components/toggle-group/meta.xml"
|
|
304
304
|
},
|
|
305
|
+
{
|
|
306
|
+
"id": "toggle-text-button",
|
|
307
|
+
"name": "ToggleTextButton",
|
|
308
|
+
"summary": "Labeled two-state toggle on Base UI Toggle with Button chrome; visible text children, optional addon / pressedVariant; no resting/pressed icon pair.",
|
|
309
|
+
"agentDocs": "./specs/components/toggle-text-button/meta.xml"
|
|
310
|
+
},
|
|
305
311
|
{
|
|
306
312
|
"id": "typewriter",
|
|
307
313
|
"name": "Typewriter",
|
|
@@ -3,31 +3,37 @@ import { Stack } from "../stack/stack.js";
|
|
|
3
3
|
import { Surface } from "../surface/surface.js";
|
|
4
4
|
import tabs_module_default from "./tabs.module.js";
|
|
5
5
|
import { TabsIndicator } from "./tabs-indicator.js";
|
|
6
|
+
import { useTabsSize } from "./tabs-size-context.js";
|
|
6
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
8
|
import { Tabs } from "@base-ui/react/tabs";
|
|
8
9
|
//#region src/components/tabs/tabs-list.tsx
|
|
9
|
-
var TabsList = ({ children, className, activateOnFocus = true, ref, ...otherProps }) =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
10
|
+
var TabsList = ({ children, className, activateOnFocus = true, ref, ...otherProps }) => {
|
|
11
|
+
const size = useTabsSize();
|
|
12
|
+
const listRadius = size === "s" ? "s" : "m";
|
|
13
|
+
return /* @__PURE__ */ jsxs(Tabs.List, {
|
|
14
|
+
className: clsx(tabs_module_default.List, className),
|
|
15
|
+
activateOnFocus,
|
|
16
|
+
"data-size": size,
|
|
17
|
+
render: /* @__PURE__ */ jsx(Surface, {
|
|
18
|
+
color: 1,
|
|
19
|
+
border: "all",
|
|
20
|
+
radius: listRadius,
|
|
21
|
+
vPadding: "2xs"
|
|
22
|
+
}),
|
|
23
|
+
ref,
|
|
24
|
+
...otherProps,
|
|
25
|
+
children: [/* @__PURE__ */ jsx(TabsIndicator, {}), /* @__PURE__ */ jsx("div", {
|
|
26
|
+
className: tabs_module_default.Track,
|
|
27
|
+
children: /* @__PURE__ */ jsx(Stack, {
|
|
28
|
+
expandChildren: true,
|
|
29
|
+
fullWidth: true,
|
|
30
|
+
direction: "row",
|
|
31
|
+
hAlign: "start",
|
|
32
|
+
vAlign: "center",
|
|
33
|
+
children
|
|
34
|
+
})
|
|
35
|
+
})]
|
|
36
|
+
});
|
|
37
|
+
};
|
|
32
38
|
//#endregion
|
|
33
39
|
export { TabsList };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
//#region src/components/tabs/tabs-size-context.ts
|
|
4
|
+
var TabsSizeContext = createContext("m");
|
|
5
|
+
var useTabsSize = () => useContext(TabsSizeContext);
|
|
6
|
+
//#endregion
|
|
7
|
+
export { TabsSizeContext, useTabsSize };
|
|
@@ -2,24 +2,29 @@ import { clsx } from "../../core/clsx.js";
|
|
|
2
2
|
import { Text } from "../text/text.js";
|
|
3
3
|
import { Button } from "../button/button.js";
|
|
4
4
|
import tabs_module_default from "./tabs.module.js";
|
|
5
|
+
import { useTabsSize } from "./tabs-size-context.js";
|
|
5
6
|
import { jsx } from "react/jsx-runtime";
|
|
6
7
|
import { Tabs } from "@base-ui/react/tabs";
|
|
7
8
|
//#region src/components/tabs/tabs-tab.tsx
|
|
8
|
-
var TabsTab = ({ addon, addonPosition = "start", children, className, ...otherProps }) =>
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
var TabsTab = ({ addon, addonPosition = "start", children, className, ...otherProps }) => {
|
|
10
|
+
const size = useTabsSize();
|
|
11
|
+
return /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
12
|
+
render: /* @__PURE__ */ jsx(Button, {
|
|
13
|
+
variant: "flat",
|
|
14
|
+
size,
|
|
15
|
+
addon,
|
|
16
|
+
addonPosition,
|
|
17
|
+
className: clsx(tabs_module_default.Item, className)
|
|
18
|
+
}),
|
|
19
|
+
...otherProps,
|
|
20
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
21
|
+
fluid: false,
|
|
22
|
+
tone: "muted",
|
|
23
|
+
whiteSpace: "nowrap",
|
|
24
|
+
className: tabs_module_default.Label,
|
|
25
|
+
children
|
|
26
|
+
})
|
|
27
|
+
});
|
|
28
|
+
};
|
|
24
29
|
//#endregion
|
|
25
30
|
export { TabsTab };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TabsRoot } from '@base-ui/react/tabs';
|
|
2
2
|
import { StackProps } from '..';
|
|
3
|
+
import { ButtonProps } from '../button/button';
|
|
3
4
|
import { TabsListProps } from './tabs-list';
|
|
4
5
|
import { TabsPanelProps } from './tabs-panel';
|
|
5
6
|
import { TabsTabProps } from './tabs-tab';
|
|
@@ -20,6 +21,12 @@ export type TabsProps = TabsRoot.Props & {
|
|
|
20
21
|
* @defaultValue 'start'
|
|
21
22
|
*/
|
|
22
23
|
listAlignment?: Extract<StackProps['hAlign'], 'start' | 'center' | 'end' | 'stretch'>;
|
|
24
|
+
/**
|
|
25
|
+
* Shared visual size for tab items (`Button`) and the active indicator footprint.
|
|
26
|
+
*
|
|
27
|
+
* @defaultValue 'm'
|
|
28
|
+
*/
|
|
29
|
+
size?: ButtonProps['size'];
|
|
23
30
|
};
|
|
24
31
|
type TabsComponent = React.FC<TabsProps> & {
|
|
25
32
|
List: React.FC<TabsListProps>;
|
|
@@ -2,6 +2,7 @@ import './tabs.css';
|
|
|
2
2
|
import { clsx } from "../../core/clsx.js";
|
|
3
3
|
import { Stack } from "../stack/stack.js";
|
|
4
4
|
import tabs_module_default from "./tabs.module.js";
|
|
5
|
+
import { TabsSizeContext } from "./tabs-size-context.js";
|
|
5
6
|
import { TabsList } from "./tabs-list.js";
|
|
6
7
|
import { TabsPanel } from "./tabs-panel.js";
|
|
7
8
|
import { TabsTab } from "./tabs-tab.js";
|
|
@@ -10,14 +11,17 @@ import { jsx } from "react/jsx-runtime";
|
|
|
10
11
|
import "react";
|
|
11
12
|
import { Tabs } from "@base-ui/react/tabs";
|
|
12
13
|
//#region src/components/tabs/tabs.tsx
|
|
13
|
-
var TabsRootComponent = ({ children, className, listAlignment = "start", ...otherProps }) => /* @__PURE__ */ jsx(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
var TabsRootComponent = ({ children, className, listAlignment = "start", size = "m", ...otherProps }) => /* @__PURE__ */ jsx(TabsSizeContext.Provider, {
|
|
15
|
+
value: size,
|
|
16
|
+
children: /* @__PURE__ */ jsx(Tabs.Root, {
|
|
17
|
+
render: /* @__PURE__ */ jsx(Stack, {
|
|
18
|
+
hAlign: listAlignment,
|
|
19
|
+
vAlign: "start",
|
|
20
|
+
className: clsx(tabs_module_default.Tabs, className)
|
|
21
|
+
}),
|
|
22
|
+
...otherProps,
|
|
23
|
+
children
|
|
24
|
+
})
|
|
21
25
|
});
|
|
22
26
|
var Tabs$1 = Object.assign(TabsRootComponent, {
|
|
23
27
|
List: TabsList,
|
|
@@ -4,7 +4,9 @@ import { ToggleGroup as ToggleGroupPrimitive } from '@base-ui/react/toggle-group
|
|
|
4
4
|
*
|
|
5
5
|
* Thin wrapper around Base UI `ToggleGroup` with default `Stack` direction
|
|
6
6
|
* (`row` / `column` from `orientation`) and zero gap — consumers own spacing.
|
|
7
|
-
* Compose `ToggleButton` children each with `value`
|
|
7
|
+
* Compose `ToggleButton` and/or `ToggleTextButton` children each with `value`
|
|
8
|
+
* (icon-only children also need an accessible name).
|
|
9
|
+
|
|
8
10
|
*
|
|
9
11
|
* @defaultValue Base UI pass-through props keep upstream defaults.
|
|
10
12
|
*/
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Toggle as TogglePrimitive } from '@base-ui/react/toggle';
|
|
2
|
+
import { ButtonProps } from '../button/button';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* Public ToggleTextButton props.
|
|
6
|
+
*
|
|
7
|
+
* Labeled two-state control built on Base UI `Toggle` and `Button`.
|
|
8
|
+
* Prefer a visible text `children` label; supply `aria-label` or `aria-labelledby`
|
|
9
|
+
* when children are empty or whitespace-only.
|
|
10
|
+
*
|
|
11
|
+
* @defaultValue Base UI toggle pass-through props keep upstream defaults.
|
|
12
|
+
*/
|
|
13
|
+
export type ToggleTextButtonProps = Omit<ButtonProps, 'render' | 'variant'> & Omit<TogglePrimitive.Props, 'render' | 'children'> & {
|
|
14
|
+
/**
|
|
15
|
+
* Visual variant while the toggle is not pressed.
|
|
16
|
+
*
|
|
17
|
+
* @defaultValue 'flat'
|
|
18
|
+
*/
|
|
19
|
+
variant?: ButtonProps['variant'];
|
|
20
|
+
/**
|
|
21
|
+
* Visual variant while the toggle is pressed.
|
|
22
|
+
*
|
|
23
|
+
* @defaultValue When omitted, `variant` is used in both states.
|
|
24
|
+
*/
|
|
25
|
+
pressedVariant?: ButtonProps['variant'];
|
|
26
|
+
};
|
|
27
|
+
export declare const ToggleTextButton: React.FC<ToggleTextButtonProps>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Button } from "../button/button.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import "react";
|
|
4
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
5
|
+
import { Toggle } from "@base-ui/react/toggle";
|
|
6
|
+
//#region src/components/toggle-text-button/toggle-text-button.tsx
|
|
7
|
+
var ToggleTextButton = ({ variant = "flat", pressedVariant, pressed, defaultPressed, onPressedChange, value, disabled, ref, ...otherProps }) => /* @__PURE__ */ jsx(Toggle, {
|
|
8
|
+
ref,
|
|
9
|
+
value,
|
|
10
|
+
pressed,
|
|
11
|
+
defaultPressed,
|
|
12
|
+
onPressedChange,
|
|
13
|
+
disabled,
|
|
14
|
+
render: (toggleProps, state) => {
|
|
15
|
+
const { "aria-label": _toggleAriaLabel, "aria-labelledby": _toggleAriaLabelledBy, ...restToggleProps } = toggleProps;
|
|
16
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
17
|
+
...mergeProps(restToggleProps, otherProps),
|
|
18
|
+
disabled,
|
|
19
|
+
variant: state.pressed ? pressedVariant ?? variant : variant
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
//#endregion
|
|
24
|
+
export { ToggleTextButton };
|
package/dist/index.js
CHANGED
|
@@ -70,7 +70,8 @@ import { Title } from "./components/title/title.js";
|
|
|
70
70
|
import { Toast } from "./components/toast/toast.js";
|
|
71
71
|
import { ToggleButton } from "./components/toggle-button/toggle-button.js";
|
|
72
72
|
import { ToggleGroup } from "./components/toggle-group/toggle-group.js";
|
|
73
|
+
import { ToggleTextButton } from "./components/toggle-text-button/toggle-text-button.js";
|
|
73
74
|
import { Typewriter } from "./components/typewriter/typewriter.js";
|
|
74
75
|
import { TooltipContent } from "./components/tooltip/tooltip-content.js";
|
|
75
76
|
import { Tooltip, TooltipProvider, TooltipTrigger } from "./components/tooltip/tooltip.js";
|
|
76
|
-
export { Accordion, Annotate, Autocomplete, Avatar, AvatarGroup, Badge, Beam, Bleed, BreakpointsProvider, Button, ButtonLink, Checkbox, CheckboxGroup, Chip, ClampText, Dialog, DialogClose, DialogIndent, DialogIndentBackground, DialogSheet, DialogTrigger, DirectionProvider, Disclosure, ELEVATION_DEFAULTS, Elevator, Fieldset, FitText, Glow, Grid, IconButton, IconButtonLink, LinearProgress, Masonry, Menu, MenuCheckboxItem, MenuGroup, MenuItem, MenuLinkItem, MenuPopup, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuSubmenu, MenuSubmenuTrigger, MenuTrigger, Meter, NumberField, OTPField, Popover, PopoverClose, PopoverDescription, PopoverSheet, PopoverTitle, PopoverTrigger, ProgressiveBlur, Prose, Radio, RadioGroup, Select, Separator, Shimmer, Skeleton, Slider, Spinner, Stack, StaticNoise, Surface, Switch, Tabs, Text, Textarea, Textfield, Title, Toast, ToggleButton, ToggleGroup, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Typewriter, getElevationProps, useBreakpoints, useDirection };
|
|
77
|
+
export { Accordion, Annotate, Autocomplete, Avatar, AvatarGroup, Badge, Beam, Bleed, BreakpointsProvider, Button, ButtonLink, Checkbox, CheckboxGroup, Chip, ClampText, Dialog, DialogClose, DialogIndent, DialogIndentBackground, DialogSheet, DialogTrigger, DirectionProvider, Disclosure, ELEVATION_DEFAULTS, Elevator, Fieldset, FitText, Glow, Grid, IconButton, IconButtonLink, LinearProgress, Masonry, Menu, MenuCheckboxItem, MenuGroup, MenuItem, MenuLinkItem, MenuPopup, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuSubmenu, MenuSubmenuTrigger, MenuTrigger, Meter, NumberField, OTPField, Popover, PopoverClose, PopoverDescription, PopoverSheet, PopoverTitle, PopoverTrigger, ProgressiveBlur, Prose, Radio, RadioGroup, Select, Separator, Shimmer, Skeleton, Slider, Spinner, Stack, StaticNoise, Surface, Switch, Tabs, Text, Textarea, Textfield, Title, Toast, ToggleButton, ToggleGroup, ToggleTextButton, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Typewriter, getElevationProps, useBreakpoints, useDirection };
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
<item>Actions that need visible text — use Button</item>
|
|
10
10
|
<item>Missing both aria-label and aria-labelledby</item>
|
|
11
11
|
<item>Navigation or non-button semantics — use IconButtonLink</item>
|
|
12
|
-
<item>Temporally swap icons after user interactions with temporal pressed state, like copy feedback. Use ToggleButton
|
|
12
|
+
<item>Temporally swap icons after user interactions with temporal pressed state, like copy feedback. Use ToggleButton (icon-only) or ToggleTextButton (labeled)</item>
|
|
13
|
+
|
|
13
14
|
</when_not_to_use>
|
|
14
15
|
<prop name="icon" kind="react">
|
|
15
16
|
<description>Icon rendered as the sole visible control content. Omit glyph `size` / width / height — host CSS sizes SVG via `--vui-button-icon-size`.</description>
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
<do>Icon-only navigation — use IconButtonLink (framework Link via `render`)</do>
|
|
11
11
|
<dont>Passing `children`, `addon`, or `addonPosition` — not part of the public API; use `icon`</dont>
|
|
12
12
|
<dont>Using icon-only Button instead of IconButton</dont>
|
|
13
|
-
<dont>Using IconButton for persistent or temporal pressed state — use ToggleButton</dont>
|
|
13
|
+
<dont>Using IconButton for persistent or temporal pressed state — use ToggleButton (icon-only) or ToggleTextButton (labeled)</dont>
|
|
14
|
+
|
|
14
15
|
<dont>Using IconButton with `render={<a />}` for links — use IconButtonLink</dont>
|
|
15
16
|
<dont>Omitting both `aria-label` and `aria-labelledby`</dont>
|
|
16
17
|
<dont>Passing `size={16}` / `size="1rem"` (or width/height) on the `icon` glyph — IconButton CSS sizes SVG descendants</dont>
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
<prop name="listAlignment" kind="react">
|
|
13
13
|
<description>Horizontal alignment of tab list within root Stack.</description>
|
|
14
14
|
</prop>
|
|
15
|
+
<prop name="size" kind="react">
|
|
16
|
+
<description>Shared visual size for tab items (`Button`) and the active indicator footprint. Values match `Button` size (`s` | `m` | `l`). Default `m`.</description>
|
|
17
|
+
</prop>
|
|
15
18
|
<prop name="Tab.addon" kind="react">
|
|
16
19
|
<description>Decorative addon rendered alongside the tab label. Omit glyph `size` / width / height unless expressly overriding the icon-library default.</description>
|
|
17
20
|
</prop>
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
<do>Login/signup (or similar) form swap: wrap Tabs in Surface; panels hold Stack with Textfield(s) + Button (+ optional Fieldset) — use Stack `rowGap` / `fullWidth` / `expandChildren` and Textfield `fullWidth`; invent 0 width/gap CSS</do>
|
|
5
5
|
<do>Compound order: `Tabs.List` → `Tabs.Tab`(s), then `Tabs.Viewport` → `Tabs.Panel`(s) with matching `value`</do>
|
|
6
6
|
<do>Fill list chrome with `listAlignment="stretch"`; center/end via `listAlignment` on the root</do>
|
|
7
|
+
<do>Set shared tab + indicator scale with root `size` (`m` default; `s` / `l` match `Button`)</do>
|
|
7
8
|
<do>Decorative `Tab` `addon` / `addonPosition` beside a visible text label — omit glyph `size` / width / height unless expressly overriding</do>
|
|
8
9
|
<dont>Using addons as the only visible label for a tab</dont>
|
|
9
10
|
<dont>Passing `size={16}` on every Tab `addon` icon by habit — prefer icon-library default</dont>
|
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
<item>Temporally swap icons after user interactions with temporal pressed state, like copy feedback.</item>
|
|
8
8
|
</when_to_use>
|
|
9
9
|
<when_not_to_use>
|
|
10
|
+
<item>Labeled / visible-text toggles — use ToggleTextButton</item>
|
|
10
11
|
<item>Settings-style on/off fields — use Switch</item>
|
|
11
12
|
<item>One-shot actions with no pressed state — use IconButton</item>
|
|
12
13
|
</when_not_to_use>
|
|
14
|
+
|
|
13
15
|
<prop name="interpolateIcons" kind="react">
|
|
14
16
|
<description>When `pressedIcon` is set, crossfade and rotate between resting and pressed icons (`data-interpolate="true"`). When `false`, swap instantly with no transition.</description>
|
|
15
17
|
</prop>
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
<do>Temporal pressed feedback (e.g. copy) with optional `interpolateIcons` crossfade</do>
|
|
8
8
|
<do>Segmented toolbars: nest ToggleButtons with `value` inside ToggleGroup; label each control and the group</do>
|
|
9
9
|
<do>Optional chrome: wrap with Tooltip.Trigger / Badge — keep the accessible name on the ToggleButton</do>
|
|
10
|
+
<dont>Labeled / visible-text toggles — use ToggleTextButton (do not stuff text into the icon-only API)</dont>
|
|
10
11
|
<dont>Using IconButton for persistent or temporal pressed toolbar state</dont>
|
|
11
12
|
<dont>Using Switch for compact icon-only toolbar toggles</dont>
|
|
12
13
|
<dont>Passing `icon` or `render` — ToggleButton owns icon swap and Toggle render wiring; use `restingIcon` / `pressedIcon`</dont>
|
|
@@ -14,3 +15,4 @@
|
|
|
14
15
|
<dont>Omitting `value` on ToggleButton children inside a ToggleGroup</dont>
|
|
15
16
|
<dont>Omitting both `aria-label` and `aria-labelledby`</dont>
|
|
16
17
|
</usage>
|
|
18
|
+
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<patterns surface="toggle-group">
|
|
3
|
-
<!-- Sources: viraui-app — cleaned (no handlers, no i18n, no theme store). Compound: ToggleGroup + ToggleButton children with value. -->
|
|
3
|
+
<!-- Sources: viraui-app — cleaned (no handlers, no i18n, no theme store). Compound: ToggleGroup + ToggleButton and/or ToggleTextButton children with value. -->
|
|
4
|
+
|
|
4
5
|
|
|
5
6
|
<example title="Mode switcher chrome">
|
|
6
7
|
<tsx><![CDATA[
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<usage surface="toggle-group">
|
|
3
|
-
<do>Segmented
|
|
4
|
-
<do>Single- or multi-select pressed states across related ToggleButton items (`value` / `defaultValue` as `string[]`)</do>
|
|
5
|
-
<do>Compose
|
|
3
|
+
<do>Segmented toolbars such as text alignment, formatting, or mode switchers</do>
|
|
4
|
+
<do>Single- or multi-select pressed states across related ToggleButton and/or ToggleTextButton items (`value` / `defaultValue` as `string[]`)</do>
|
|
5
|
+
<do>Compose ToggleButton and/or ToggleTextButton children — each child needs a `value`; icon-only children also need `aria-label` / `aria-labelledby`</do>
|
|
6
6
|
<do>Label the group root with `aria-label` or `aria-labelledby`</do>
|
|
7
7
|
<do>Optional Surface chrome around the group for bordered toolbar shells — Stack gap is owned by ToggleGroup</do>
|
|
8
|
-
<dont>Omitting `value` on ToggleButton children inside a group</dont>
|
|
8
|
+
<dont>Omitting `value` on ToggleButton / ToggleTextButton children inside a group</dont>
|
|
9
9
|
<dont>Using Switch for icon-only toolbar toggle sets</dont>
|
|
10
10
|
<dont>Using ToggleGroup for tabbed panel navigation — use Tabs</dont>
|
|
11
|
-
<dont>Using ToggleGroup for a lone one-off toggle — use ToggleButton alone</dont>
|
|
11
|
+
<dont>Using ToggleGroup for a lone one-off toggle — use ToggleButton or ToggleTextButton alone</dont>
|
|
12
12
|
<dont>Embedding Surface chrome inside the component — wrap demos or app chrome at the consumer</dont>
|
|
13
13
|
</usage>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<a11y surface="toggle-text-button">
|
|
3
|
+
<rule>Prefer accessible name from visible text children</rule>
|
|
4
|
+
<rule>Empty or whitespace-only children require aria-label or aria-labelledby</rule>
|
|
5
|
+
<rule>Base UI Toggle exposes aria-pressed on the rendered button</rule>
|
|
6
|
+
<rule>Addon is decorative (aria-hidden) unless it is the sole content path — prefer text label; icon-only → ToggleButton</rule>
|
|
7
|
+
<rule>Keep the accessible name stable when loading state changes</rule>
|
|
8
|
+
</a11y>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<meta id="toggle-text-button" title="ToggleTextButton" category="action" shared_contract="button">
|
|
3
|
+
<summary>Labeled two-state toggle on Base UI Toggle with Button chrome; visible text children, optional addon / pressedVariant; no resting/pressed icon pair.</summary>
|
|
4
|
+
<exports>
|
|
5
|
+
<export name="ToggleTextButton" />
|
|
6
|
+
</exports>
|
|
7
|
+
<related>
|
|
8
|
+
<ref id="button" />
|
|
9
|
+
<ref id="toggle-button" />
|
|
10
|
+
<ref id="toggle-group" />
|
|
11
|
+
</related>
|
|
12
|
+
<units>
|
|
13
|
+
<unit kind="a11y" href="a11y.xml" />
|
|
14
|
+
<unit kind="props" href="props.xml" />
|
|
15
|
+
<unit kind="usage" href="usage.xml" />
|
|
16
|
+
<unit kind="patterns" href="patterns.xml" />
|
|
17
|
+
</units>
|
|
18
|
+
<base_ui href="https://base-ui.com/react/components/toggle#api-reference" />
|
|
19
|
+
</meta>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<patterns surface="toggle-text-button">
|
|
3
|
+
<example title="Labeled press">
|
|
4
|
+
<tsx><![CDATA[
|
|
5
|
+
<ToggleTextButton variant="flat" pressedVariant="primary">
|
|
6
|
+
Bold
|
|
7
|
+
</ToggleTextButton>
|
|
8
|
+
]]></tsx>
|
|
9
|
+
<notes>Visible text supplies the accessible name. Default resting `variant` is `flat`; set `pressedVariant` for pressed chrome.</notes>
|
|
10
|
+
</example>
|
|
11
|
+
|
|
12
|
+
<example title="Addon + label">
|
|
13
|
+
<tsx><![CDATA[
|
|
14
|
+
<ToggleTextButton
|
|
15
|
+
addon={<Bell aria-hidden />}
|
|
16
|
+
addonPosition="start"
|
|
17
|
+
pressedVariant="secondary"
|
|
18
|
+
>
|
|
19
|
+
Notify
|
|
20
|
+
</ToggleTextButton>
|
|
21
|
+
]]></tsx>
|
|
22
|
+
<notes>Button addon is decorative; keep the text label for naming. Omit glyph size props — Button CSS sizes via `--vui-button-icon-size`.</notes>
|
|
23
|
+
</example>
|
|
24
|
+
|
|
25
|
+
<example title="Mixed ToggleGroup">
|
|
26
|
+
<tsx><![CDATA[
|
|
27
|
+
<ToggleGroup aria-label="Editor options" defaultValue={['bold']}>
|
|
28
|
+
<ToggleTextButton value="bold" pressedVariant="secondary">
|
|
29
|
+
Bold
|
|
30
|
+
</ToggleTextButton>
|
|
31
|
+
<ToggleTextButton value="notify" addon={<Bell aria-hidden />} pressedVariant="secondary">
|
|
32
|
+
Notify
|
|
33
|
+
</ToggleTextButton>
|
|
34
|
+
<ToggleButton
|
|
35
|
+
value="lock"
|
|
36
|
+
aria-label="Lock"
|
|
37
|
+
restingIcon={<Lock aria-hidden />}
|
|
38
|
+
pressedVariant="secondary"
|
|
39
|
+
/>
|
|
40
|
+
</ToggleGroup>
|
|
41
|
+
]]></tsx>
|
|
42
|
+
<notes>ToggleGroup children may be ToggleTextButton and/or ToggleButton; each needs `value`. Icon-only siblings still need `aria-label`.</notes>
|
|
43
|
+
</example>
|
|
44
|
+
</patterns>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<props surface="toggle-text-button">
|
|
3
|
+
<when_to_use>
|
|
4
|
+
<item>Labeled toolbar or settings toggles with visible text</item>
|
|
5
|
+
<item>Two-state actions that share Button size, variant, addon, loading, and pill chrome</item>
|
|
6
|
+
<item>ToggleGroup segments that need a text label instead of an icon-only control</item>
|
|
7
|
+
</when_to_use>
|
|
8
|
+
<when_not_to_use>
|
|
9
|
+
<item>Icon-only two-state controls — use ToggleButton</item>
|
|
10
|
+
<item>Settings-style on/off fields without button chrome — use Switch</item>
|
|
11
|
+
<item>One-shot actions with no pressed state — use Button</item>
|
|
12
|
+
</when_not_to_use>
|
|
13
|
+
<prop name="addon" kind="react">
|
|
14
|
+
<description>Decorative addon rendered alongside the button label. Pass any React element; use a named icon from `@viraui/icons/react` or `<Chip />` for status chips. Omit glyph `size` / width / height — Button CSS sizes SVG via `--vui-button-icon-size`.</description>
|
|
15
|
+
</prop>
|
|
16
|
+
<prop name="addonPosition" kind="react">
|
|
17
|
+
<description>Controls whether the addon appears before or after the label.</description>
|
|
18
|
+
</prop>
|
|
19
|
+
<prop name="loading" kind="react">
|
|
20
|
+
<description>Shows a centered spinner overlay while preserving the original content width.</description>
|
|
21
|
+
</prop>
|
|
22
|
+
<prop name="pill" kind="react">
|
|
23
|
+
<description>Applies a fully rounded pill shape to the button.</description>
|
|
24
|
+
</prop>
|
|
25
|
+
<prop name="pressedVariant" kind="react">
|
|
26
|
+
<description>Visual variant while the toggle is pressed.</description>
|
|
27
|
+
</prop>
|
|
28
|
+
<prop name="size" kind="react">
|
|
29
|
+
<description>Controls the visual button size.</description>
|
|
30
|
+
</prop>
|
|
31
|
+
<prop name="variant" kind="react">
|
|
32
|
+
<description>Visual variant while the toggle is not pressed.</description>
|
|
33
|
+
</prop>
|
|
34
|
+
<prop name="background" kind="css">
|
|
35
|
+
<description>Button fill; variant and state selectors preset this.</description>
|
|
36
|
+
</prop>
|
|
37
|
+
<prop name="color" kind="css">
|
|
38
|
+
<description>Button label and icon color.</description>
|
|
39
|
+
</prop>
|
|
40
|
+
<prop name="height" kind="css">
|
|
41
|
+
<description>Button block size; size prop presets via data attributes.</description>
|
|
42
|
+
</prop>
|
|
43
|
+
<prop name="icon-size" kind="css">
|
|
44
|
+
<description>Addon icon size inside the button (`--vui-button-icon-size`). Prefer this over setting `size` on the glyph ReactNode.</description>
|
|
45
|
+
</prop>
|
|
46
|
+
<prop name="padding-block" kind="css">
|
|
47
|
+
<description>Vertical padding inside the button.</description>
|
|
48
|
+
</prop>
|
|
49
|
+
<prop name="padding-inline" kind="css">
|
|
50
|
+
<description>Horizontal padding inside the button.</description>
|
|
51
|
+
</prop>
|
|
52
|
+
<prop name="radius" kind="css">
|
|
53
|
+
<description>Corner radius; pill sets full radius via data-pill.</description>
|
|
54
|
+
</prop>
|
|
55
|
+
<prop name="spinner-color" kind="css">
|
|
56
|
+
<description>Loading spinner color on the root.</description>
|
|
57
|
+
</prop>
|
|
58
|
+
</props>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<usage surface="toggle-text-button">
|
|
3
|
+
<do>Labeled toolbar, filter, or settings toggles with visible text children</do>
|
|
4
|
+
<do>Optional Button `addon` / `addonPosition` / `size` / `loading` / `pill` chrome beside the label</do>
|
|
5
|
+
<do>Resting `variant` default `flat`; optional `pressedVariant` when pressed</do>
|
|
6
|
+
<do>Segmented toolbars: nest ToggleTextButton (and/or ToggleButton) with `value` inside ToggleGroup; label the group</do>
|
|
7
|
+
<do>Omit glyph `size` / width / height on `addon` — host CSS sizes SVG via `--vui-button-icon-size`</do>
|
|
8
|
+
<dont>Icon-only two-state controls — use ToggleButton with `restingIcon` / `pressedIcon`</dont>
|
|
9
|
+
<dont>Passing `restingIcon`, `pressedIcon`, `interpolateIcons`, or `render` — not part of this API</dont>
|
|
10
|
+
<dont>Omitting `value` on ToggleTextButton children inside a ToggleGroup</dont>
|
|
11
|
+
<dont>Empty/whitespace children without `aria-label` or `aria-labelledby`</dont>
|
|
12
|
+
<dont>Using Switch when Button chrome and pressed variant are the desired pattern</dont>
|
|
13
|
+
</usage>
|
package/dist/specs/index.xml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<index package="@viraui/react" version="2026.
|
|
2
|
+
<index package="@viraui/react" version="2026.10.0" generated="2026-10-16T10:31:41.253Z">
|
|
3
3
|
<categories>
|
|
4
4
|
<category id="layout" />
|
|
5
5
|
<category id="action" />
|
|
@@ -511,6 +511,16 @@
|
|
|
511
511
|
<unit kind="patterns" href="components/toggle-group/patterns.xml" />
|
|
512
512
|
</units>
|
|
513
513
|
</entry>
|
|
514
|
+
<entry id="toggle-text-button" category="action" title="ToggleTextButton">
|
|
515
|
+
<summary>Labeled two-state toggle on Base UI Toggle with Button chrome; visible text children, optional addon / pressedVariant; no resting/pressed icon pair.</summary>
|
|
516
|
+
<units>
|
|
517
|
+
<unit kind="meta" href="components/toggle-text-button/meta.xml" />
|
|
518
|
+
<unit kind="a11y" href="components/toggle-text-button/a11y.xml" />
|
|
519
|
+
<unit kind="props" href="components/toggle-text-button/props.xml" />
|
|
520
|
+
<unit kind="usage" href="components/toggle-text-button/usage.xml" />
|
|
521
|
+
<unit kind="patterns" href="components/toggle-text-button/patterns.xml" />
|
|
522
|
+
</units>
|
|
523
|
+
</entry>
|
|
514
524
|
<entry id="typewriter" category="typography" title="Typewriter">
|
|
515
525
|
<summary>Decorative inline typewriter animation that inherits typography and color from a parent text primitive.</summary>
|
|
516
526
|
<units>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viraui/react",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.10.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@base-ui/react": ">=1.8.0",
|
|
31
|
-
"@viraui/foundation": "2026.
|
|
31
|
+
"@viraui/foundation": "2026.10.0",
|
|
32
32
|
"@viraui/icons": "2026.1.0",
|
|
33
33
|
"@viraui/postcss-config": "2026.0.0",
|
|
34
34
|
"@viraui/tsconfig": "2026.0.0",
|