Toggle Group
A set of toggles; spacing 2 by default, 0 joins them.
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";import { useState } from "react";import { IconPlaceholder } from "@/edmi/icon-placeholder";
export default function Demo() { const [range, setRange] = useState("1M"); // A range always has one value: ignore the empty value a second click on the active item produces. const rangeProps = { value: [range], onValueChange: (v: string[]) => v[0] && setRange(v[0]), }; return ( <div className="flex flex-wrap items-start gap-6"> <ToggleGroup multiple defaultValue={["b"]}> <ToggleGroupItem value="b" aria-label="Bold"> <IconPlaceholder lucide="BoldIcon" tabler="IconBold" hugeicons="TextBoldIcon" phosphor="TextBIcon" remixicon="RiBold" /> </ToggleGroupItem> <ToggleGroupItem value="i" aria-label="Italic"> <IconPlaceholder lucide="ItalicIcon" tabler="IconItalic" hugeicons="TextItalicIcon" phosphor="TextItalicIcon" remixicon="RiItalic" /> </ToggleGroupItem> <ToggleGroupItem value="u" aria-label="Underline"> <IconPlaceholder lucide="UnderlineIcon" tabler="IconUnderline" hugeicons="TextUnderlineIcon" phosphor="TextUnderlineIcon" remixicon="RiUnderline" /> </ToggleGroupItem> </ToggleGroup> <ToggleGroup variant="outline" spacing={0} {...rangeProps}> {["1D", "1W", "1M", "1Y", "All"].map((v) => ( <ToggleGroupItem key={v} value={v}> {v} </ToggleGroupItem> ))} </ToggleGroup> <ToggleGroup variant="segmented" {...rangeProps}> {["1D", "1W", "1M", "1Y", "All"].map((v) => ( <ToggleGroupItem key={v} value={v}> {v} </ToggleGroupItem> ))} </ToggleGroup> <ToggleGroup orientation="vertical" variant="outline"> <ToggleGroupItem value="b" aria-label="Bold"> <IconPlaceholder lucide="BoldIcon" tabler="IconBold" hugeicons="TextBoldIcon" phosphor="TextBIcon" remixicon="RiBold" /> </ToggleGroupItem> <ToggleGroupItem value="i" aria-label="Italic"> <IconPlaceholder lucide="ItalicIcon" tabler="IconItalic" hugeicons="TextItalicIcon" phosphor="TextItalicIcon" remixicon="RiItalic" /> </ToggleGroupItem> </ToggleGroup> </div> );}<script setup lang="ts">import { Bold, Italic, Underline } from "@lucide/vue";import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";import { ref } from "vue";
const ranges = ["1D", "1W", "1M", "1Y", "All"];const range = ref("1M");// A range always has one value: ignore the empty value a second click on the active item produces.function setRange(v: unknown) { if (typeof v === "string" && v) range.value = v;}</script>
<template> <div class="flex flex-wrap items-start gap-6"> <ToggleGroup type="multiple" :default-value="['b']"> <ToggleGroupItem value="b" aria-label="Bold"><Bold /></ToggleGroupItem> <ToggleGroupItem value="i" aria-label="Italic"><Italic /></ToggleGroupItem> <ToggleGroupItem value="u" aria-label="Underline"><Underline /></ToggleGroupItem> </ToggleGroup> <ToggleGroup type="single" variant="outline" :spacing="0" :model-value="range" @update:model-value="setRange"> <ToggleGroupItem v-for="v in ranges" :key="v" :value="v">{{ v }}</ToggleGroupItem> </ToggleGroup> <ToggleGroup type="single" variant="segmented" :model-value="range" @update:model-value="setRange"> <ToggleGroupItem v-for="v in ranges" :key="v" :value="v">{{ v }}</ToggleGroupItem> </ToggleGroup> <ToggleGroup type="multiple" orientation="vertical" variant="outline"> <ToggleGroupItem value="b" aria-label="Bold"><Bold /></ToggleGroupItem> <ToggleGroupItem value="i" aria-label="Italic"><Italic /></ToggleGroupItem> </ToggleGroup> </div></template><script lang="ts"> import * as ToggleGroup from "$lib/components/ui/toggle-group";
const ranges = ["1D", "1W", "1M", "1Y", "All"]; let format = $state<string[]>(["b"]); let range = $state("1M"); // A range always has one value: ignore the empty value a second click on the active item produces. function setRange(v: string) { if (v) range = v; }</script>
<div class="flex flex-wrap items-start gap-6"> <ToggleGroup.Root type="multiple" bind:value={format}> <ToggleGroup.Item value="b" aria-label="Bold"><b>B</b></ToggleGroup.Item> <ToggleGroup.Item value="i" aria-label="Italic"><i>I</i></ToggleGroup.Item> <ToggleGroup.Item value="u" aria-label="Underline"><u>U</u></ToggleGroup.Item> </ToggleGroup.Root> <ToggleGroup.Root type="single" variant="outline" spacing={0} value={range} onValueChange={setRange}> {#each ranges as v (v)} <ToggleGroup.Item value={v}>{v}</ToggleGroup.Item> {/each} </ToggleGroup.Root> <ToggleGroup.Root type="single" variant="segmented" value={range} onValueChange={setRange}> {#each ranges as v (v)} <ToggleGroup.Item value={v}>{v}</ToggleGroup.Item> {/each} </ToggleGroup.Root> <ToggleGroup.Root type="multiple" orientation="vertical" variant="outline"> <ToggleGroup.Item value="b" aria-label="Bold"><b>B</b></ToggleGroup.Item> <ToggleGroup.Item value="i" aria-label="Italic"><i>I</i></ToggleGroup.Item> </ToggleGroup.Root></div>Installation
Section titled “Installation”npx shadcn@latest add @edmi-ui/toggle-grouppnpm dlx shadcn@latest add @edmi-ui/toggle-groupyarn dlx shadcn@latest add @edmi-ui/toggle-groupbunx shadcn@latest add @edmi-ui/toggle-groupPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install @base-ui/react class-variance-authorityTerminal window pnpm add @base-ui/react class-variance-authorityTerminal window yarn add @base-ui/react class-variance-authorityTerminal window bun add @base-ui/react class-variance-authorityAdd the Edmi components this one builds on:
toggle,elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-vue@latest add @edmi-ui/toggle-grouppnpm dlx shadcn-vue@latest add @edmi-ui/toggle-groupyarn dlx shadcn-vue@latest add @edmi-ui/toggle-groupbunx shadcn-vue@latest add @edmi-ui/toggle-groupPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install reka-ui class-variance-authority @vueuse/coreTerminal window pnpm add reka-ui class-variance-authority @vueuse/coreTerminal window yarn add reka-ui class-variance-authority @vueuse/coreTerminal window bun add reka-ui class-variance-authority @vueuse/coreAdd the Edmi components this one builds on:
toggle,elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/toggle-group.jsonpnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/toggle-group.jsonyarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/toggle-group.jsonbunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/toggle-group.jsonPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Add the Edmi components this one builds on:
toggle,elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
<div className="flex flex-wrap items-start gap-6"> <ToggleGroup multiple defaultValue={["b"]}> <ToggleGroupItem value="b" aria-label="Bold"> <TextBIcon /> </ToggleGroupItem> <ToggleGroupItem value="i" aria-label="Italic"> <TextItalicIcon /> </ToggleGroupItem> <ToggleGroupItem value="u" aria-label="Underline"> <TextUnderlineIcon /> </ToggleGroupItem> </ToggleGroup> <ToggleGroup variant="outline" spacing={0} {...rangeProps}> {["1D", "1W", "1M", "1Y", "All"].map((v) => ( <ToggleGroupItem key={v} value={v}> {v} </ToggleGroupItem> ))} </ToggleGroup> {/* … */}</div><script setup lang="ts">import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";</script>
<template> <div class="flex flex-wrap items-start gap-6"> <ToggleGroup type="multiple" :default-value="['b']"> <ToggleGroupItem value="b" aria-label="Bold"><Bold /></ToggleGroupItem> <ToggleGroupItem value="i" aria-label="Italic"><Italic /></ToggleGroupItem> <ToggleGroupItem value="u" aria-label="Underline"><Underline /></ToggleGroupItem> </ToggleGroup> <ToggleGroup type="single" variant="outline" :spacing="0" @update:model-value="setRange"> <ToggleGroupItem :key="v" :value="v">{{ v }}</ToggleGroupItem> </ToggleGroup> <ToggleGroup type="single" variant="segmented" @update:model-value="setRange"> <ToggleGroupItem :key="v" :value="v">{{ v }}</ToggleGroupItem> </ToggleGroup> <ToggleGroup type="multiple" orientation="vertical" variant="outline"> <ToggleGroupItem value="b" aria-label="Bold"><Bold /></ToggleGroupItem> <ToggleGroupItem value="i" aria-label="Italic"><Italic /></ToggleGroupItem> </ToggleGroup> </div></template><script lang="ts"> import * as ToggleGroup from "$lib/components/ui/toggle-group";</script>
<div class="flex flex-wrap items-start gap-6"> <ToggleGroup.Root type="multiple"> <ToggleGroup.Item value="b" aria-label="Bold"><b>B</b></ToggleGroup.Item> <ToggleGroup.Item value="i" aria-label="Italic"><i>I</i></ToggleGroup.Item> <ToggleGroup.Item value="u" aria-label="Underline"><u>U</u></ToggleGroup.Item> </ToggleGroup.Root> <ToggleGroup.Root type="single" variant="outline" spacing={0} onValueChange={setRange}> {#each ranges as v (v)} <ToggleGroup.Item value={v}>{v}</ToggleGroup.Item> {/each} </ToggleGroup.Root> <ToggleGroup.Root type="single" variant="segmented" onValueChange={setRange}> {#each ranges as v (v)} <ToggleGroup.Item value={v}>{v}</ToggleGroup.Item> {/each} </ToggleGroup.Root> <ToggleGroup.Root type="multiple" orientation="vertical" variant="outline"> <ToggleGroup.Item value="b" aria-label="Bold"><b>B</b></ToggleGroup.Item> <ToggleGroup.Item value="i" aria-label="Italic"><i>I</i></ToggleGroup.Item> </ToggleGroup.Root></div>Component source
Section titled “Component source”"use client";
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle";import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group";import type { VariantProps } from "class-variance-authority";import { cn } from "cn";import * as React from "react";
import { type Elevation, type ElevationLevel, useElevation,} from "@/registry/edmi/ui/elevation";import { toggleVariants } from "@/registry/edmi/ui/toggle";
const ToggleGroupContext = React.createContext< VariantProps<typeof toggleVariants> & { spacing?: number; orientation?: "horizontal" | "vertical"; elevation?: ElevationLevel; }>({ size: "default", variant: "default", spacing: 2, orientation: "horizontal", elevation: undefined,});
function ToggleGroup({ className, variant, size, spacing = 2, orientation = "horizontal", elevation, children, ...props}: ToggleGroupPrimitive.Props & VariantProps<typeof toggleVariants> & { spacing?: number; orientation?: "horizontal" | "vertical"; /** ✦ depth, passed down to every item (segmented: only the ON item rises). */ elevation?: Elevation; }) { const level = useElevation(elevation, "control"); // ✦ `variant="segmented"` renders a flat track (DESIGN §4.5); gap is fixed at 2px. const segmented = variant === "segmented"; return ( <ToggleGroupPrimitive data-slot="toggle-group" data-variant={variant} data-size={size} data-spacing={segmented ? 0.5 : spacing} data-orientation={orientation} style={{ "--gap": segmented ? 0.5 : spacing } as React.CSSProperties} className={cn( "group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch", segmented && "rounded-[10px] border border-border bg-muted p-[3px] shadow-[inset_0_1px_2px_rgb(0_0_0/0.04)]", className, )} {...props} > <ToggleGroupContext.Provider value={{ variant, size, spacing, orientation, elevation: level }} > {children} </ToggleGroupContext.Provider> </ToggleGroupPrimitive> );}
function ToggleGroupItem({ className, children, variant = "default", size = "default", elevation, ...props}: TogglePrimitive.Props & Omit<VariantProps<typeof toggleVariants>, "elevation"> & { elevation?: Elevation; }) { const context = React.useContext(ToggleGroupContext);
return ( <TogglePrimitive data-slot="toggle-group-item" data-variant={context.variant || variant} data-size={context.size || size} data-spacing={context.spacing} className={cn( "shrink-0 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t", toggleVariants({ variant: context.variant || variant, size: context.size || size, elevation: elevation && elevation !== "auto" ? elevation : context.elevation, }), className, )} {...props} > {children} </TogglePrimitive> );}
export { ToggleGroup, ToggleGroupItem };<script setup lang="ts">import type { VariantProps } from 'class-variance-authority'import type { ToggleGroupRootEmits, ToggleGroupRootProps } from 'reka-ui'import type { HTMLAttributes } from 'vue'import type { toggleVariants } from '@/registry/edmi/ui/toggle'import { reactiveOmit } from '@vueuse/core'import { ToggleGroupRoot, useForwardPropsEmits } from 'reka-ui'import { computed, provide } from 'vue'import { cn } from '@/registry/edmi/lib/utils'import { type Elevation, useElevation } from '@/registry/edmi/ui/elevation'
type ToggleGroupVariants = VariantProps<typeof toggleVariants>
const props = withDefaults(defineProps<ToggleGroupRootProps & { class?: HTMLAttributes['class'] variant?: ToggleGroupVariants['variant'] size?: ToggleGroupVariants['size'] spacing?: number /** ✦ depth passed to every item (items may override); segmented: only the ON item rises */ elevation?: Elevation}>(), { spacing: 2, elevation: undefined, orientation: 'horizontal',})
const emits = defineEmits<ToggleGroupRootEmits>()
const level = useElevation(() => props.elevation, 'control')
provide('toggleGroup', { get variant() { return props.variant }, get size() { return props.size }, get spacing() { return props.spacing }, get orientation() { return props.orientation }, get elevation() { return level.value },})
// ✦ `variant="segmented"` renders a flat track (DESIGN §4.5); gap is fixed at 2px.const segmented = computed(() => props.variant === 'segmented')
const delegatedProps = reactiveOmit(props, 'class', 'size', 'variant', 'spacing', 'elevation')const forwarded = useForwardPropsEmits(delegatedProps, emits)</script>
<template> <ToggleGroupRoot v-slot="slotProps" data-slot="toggle-group" :data-size="size" :data-variant="variant" :data-spacing="segmented ? 0.5 : spacing" :style="{ '--gap': segmented ? 0.5 : spacing, }" v-bind="forwarded" :class="cn( 'group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch', segmented && 'rounded-[10px] border border-border bg-muted p-[3px] shadow-[inset_0_1px_2px_rgb(0_0_0/0.04)]', props.class, )" > <slot v-bind="slotProps" /> </ToggleGroupRoot></template><script setup lang="ts">import type { VariantProps } from 'class-variance-authority'import type { ToggleGroupItemProps } from 'reka-ui'import type { HTMLAttributes } from 'vue'import { reactiveOmit } from '@vueuse/core'import { ToggleGroupItem, useForwardProps } from 'reka-ui'import { inject } from 'vue'import { cn } from '@/registry/edmi/lib/utils'import type { Elevation, ElevationLevel } from '@/registry/edmi/ui/elevation'import { toggleVariants } from '@/registry/edmi/ui/toggle'
type ToggleGroupVariants = VariantProps<typeof toggleVariants> & { spacing?: number orientation?: 'horizontal' | 'vertical' elevation?: ElevationLevel}
const props = withDefaults(defineProps<ToggleGroupItemProps & { class?: HTMLAttributes['class'] variant?: ToggleGroupVariants['variant'] size?: ToggleGroupVariants['size'] /** ✦ overrides the group's `elevation` */ elevation?: Elevation}>(), { elevation: undefined,})
const context = inject<ToggleGroupVariants>('toggleGroup')
const delegatedProps = reactiveOmit(props, 'class', 'size', 'variant', 'elevation')const forwardedProps = useForwardProps(delegatedProps)</script>
<template> <ToggleGroupItem v-slot="slotProps" data-slot="toggle-group-item" :data-variant="context?.variant || variant" :data-size="context?.size || size" :data-spacing="context?.spacing" v-bind="forwardedProps" :class="cn( 'shrink-0 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t', toggleVariants({ variant: context?.variant || variant, size: context?.size || size, elevation: elevation && elevation !== 'auto' ? elevation : context?.elevation, }), props.class)" > <slot v-bind="slotProps" /> </ToggleGroupItem></template>export { default as ToggleGroup } from "./ToggleGroup.vue";export { default as ToggleGroupItem } from "./ToggleGroupItem.vue";<script lang="ts" module> import { getContext, setContext } from "svelte"; import { type Elevation, type ElevationLevel, useElevation } from "$lib/registry/ui/elevation/index.js"; import { toggleVariants } from "$lib/registry/ui/toggle/index.js"; import type { VariantProps } from "tailwind-variants";
type ToggleVariants = VariantProps<typeof toggleVariants>;
interface ToggleGroupContext extends ToggleVariants { spacing?: number; orientation?: "horizontal" | "vertical"; elevation?: ElevationLevel; }
export function setToggleGroupCtx(props: ToggleGroupContext) { setContext("toggleGroup", props); }
export function getToggleGroupCtx() { return getContext<Required<ToggleGroupContext>>("toggleGroup"); }</script>
<script lang="ts"> import { ToggleGroup as ToggleGroupPrimitive } from "bits-ui"; import { cn } from "$lib/utils.js";
let { ref = $bindable(null), value = $bindable(), class: className, size = "default", spacing = 2, orientation = "horizontal", variant = "default", elevation = "auto", ...restProps }: ToggleGroupPrimitive.RootProps & Omit<ToggleVariants, "elevation"> & { spacing?: number; orientation?: "horizontal" | "vertical"; /** ✦ depth passed down to every item (segmented: only the ON item rises). */ elevation?: Elevation; } = $props();
const level = useElevation(() => elevation, "control");
setToggleGroupCtx({ get variant() { return variant; }, get size() { return size; }, get spacing() { return spacing; }, get orientation() { return orientation; }, get elevation() { return level.current; }, });
// ✦ `variant="segmented"` renders a flat track (DESIGN §4.5); gap is fixed at 2px. const segmented = $derived(variant === "segmented");</script>
<!--Discriminated Unions + Destructing (required for bindable) do notget along, so we shut typescript up by casting `value` to `never`.--><ToggleGroupPrimitive.Root bind:value={value as never} bind:ref {orientation} data-slot="toggle-group" data-variant={variant} data-size={size} data-elevation={level.current === "flat" ? undefined : level.current} data-spacing={segmented ? 0.5 : spacing} style={`--gap: ${segmented ? 0.5 : spacing}`} class={cn( "group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch", segmented && "rounded-[10px] border border-border bg-muted p-[3px] shadow-[inset_0_1px_2px_rgb(0_0_0/0.04)]", className )} {...restProps}/><script lang="ts"> import { ToggleGroup as ToggleGroupPrimitive } from "bits-ui"; import { type ToggleVariants, toggleVariants } from "$lib/registry/ui/toggle/index.js"; import type { Elevation } from "$lib/registry/ui/elevation/index.js"; import { cn } from "$lib/utils.js"; import { getToggleGroupCtx } from "./toggle-group.svelte";
let { ref = $bindable(null), value = $bindable(), class: className, size, variant, elevation, ...restProps }: ToggleGroupPrimitive.ItemProps & Omit<ToggleVariants, "elevation"> & { elevation?: Elevation } = $props();
const ctx = getToggleGroupCtx(); const itemElevation = $derived(elevation && elevation !== "auto" ? elevation : ctx.elevation); const itemVariant = $derived(ctx.variant || variant);</script>
<ToggleGroupPrimitive.Item bind:ref data-slot="toggle-group-item" data-variant={ctx.variant || variant} data-size={ctx.size || size} data-spacing={ctx.spacing} class={cn( "shrink-0 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t", toggleVariants({ variant: itemVariant, size: ctx.size || size, elevation: itemElevation, }), className )} {value} {...restProps}/>import Root from "./toggle-group.svelte";import Item from "./toggle-group-item.svelte";
export { Item, Item as ToggleGroupItem, Root, // Root as ToggleGroup,};Elevation ✦
Section titled “Elevation ✦”See the Elevation guide for the levels, roles and layered mode.
elevation on the group flows to every item. For variant="segmented" only the ON item rises (the track never bevels). Raised (+1) and floating (+2) are equivalent on items.
Flat (0)
Raised (+1): only the ON item rises
Flat (0)
Raised (+1)
Flat (0)
Raised (+1)
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";import { useState } from "react";
const levels = [ { value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1): only the ON item rises" },] as const;
const ranges = ["1D", "1W", "1M", "1Y", "All"];
export default function Demo() { const [range, setRange] = useState("1M"); // A range always has one value: ignore the empty value a second click on the active item produces. const rangeProps = { value: [range], onValueChange: (v: string[]) => v[0] && setRange(v[0]), }; return ( <div className="flex flex-col gap-5"> {levels.map(({ value, label }) => ( <div key={value} className="flex flex-col gap-2"> <p className="text-xs font-medium text-muted-foreground">{label}</p> <div className="flex flex-wrap items-start gap-6"> <ToggleGroup elevation={value} variant="segmented" {...rangeProps}> {ranges.map((v) => ( <ToggleGroupItem key={v} value={v}> {v} </ToggleGroupItem> ))} </ToggleGroup> <ToggleGroup elevation={value} variant="outline" spacing={0} {...rangeProps} > {ranges.map((v) => ( <ToggleGroupItem key={v} value={v}> {v} </ToggleGroupItem> ))} </ToggleGroup> </div> </div> ))} </div> );}<script setup lang="ts">import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";import { ref } from "vue";
const ranges = ["1D", "1W", "1M", "1Y", "All"];const range = ref("1M");// A range always has one value: ignore the empty value a second click on the active item produces.function setRange(v: unknown) { if (typeof v === "string" && v) range.value = v;}
const levels = [{ value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }];</script>
<template> <div class="flex flex-col gap-5"> <div v-for="level in levels" :key="level.value" class="flex flex-col gap-2"> <p class="text-xs font-medium text-muted-foreground">{{ level.label }}</p> <div class="flex flex-wrap items-start gap-6"> <ToggleGroup type="single" variant="segmented" :elevation="level.value" :model-value="range" @update:model-value="setRange"> <ToggleGroupItem v-for="v in ranges" :key="v" :value="v">{{ v }}</ToggleGroupItem> </ToggleGroup> <ToggleGroup type="single" variant="outline" :spacing="0" :elevation="level.value" :model-value="range" @update:model-value="setRange"> <ToggleGroupItem v-for="v in ranges" :key="v" :value="v">{{ v }}</ToggleGroupItem> </ToggleGroup> </div> </div> </div></template><script lang="ts"> import * as ToggleGroup from "$lib/components/ui/toggle-group";
const ranges = ["1D", "1W", "1M", "1Y", "All"]; let range = $state("1M"); // A range always has one value: ignore the empty value a second click on the active item produces. function setRange(v: string) { if (v) range = v; }
const levels = [{ value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }] as const;</script>
<div class="flex flex-col gap-5"> {#each levels as level (level.value)} <div class="flex flex-col gap-2"> <p class="text-xs font-medium text-muted-foreground">{level.label}</p> <div class="flex flex-wrap items-start gap-6"> <ToggleGroup.Root type="single" variant="segmented" elevation={level.value} value={range} onValueChange={setRange}> {#each ranges as v (v)} <ToggleGroup.Item value={v}>{v}</ToggleGroup.Item> {/each} </ToggleGroup.Root> <ToggleGroup.Root type="single" variant="outline" spacing={0} elevation={level.value} value={range} onValueChange={setRange}> {#each ranges as v (v)} <ToggleGroup.Item value={v}>{v}</ToggleGroup.Item> {/each} </ToggleGroup.Root> </div> </div> {/each}</div>| Prop | Type | Default |
|---|---|---|
| variant | default | outline | segmented |
default |
| size | sm | default | lg |
default |
| spacing | number |
2 |
| orientation | horizontal | vertical |
horizontal |
| multiple | boolean |
false |
| value / defaultValue | string[] |
none |
elevation |
"auto" | "sunken" | "flat" | "raised" | "floating" |
"auto" |
✦ Edmi additions
Section titled “✦ Edmi additions”variant="segmented": flat track with a tab-active fill (for single selection such as ranges).