Sections
Components
- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- ColorPicker
- Combobox
- Command
- Context Menu
- Curves
- Data Table
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- FontPicker
- Form
- Gradient Picker
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Native Select
- Navigation Menu
- Number Field
- Pagination
- Pin Input
- Popover
- Progress
- Questionnaire
- Radio Group
- Range Calendar
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Stepper
- Switch
- Table
- Tabs
- Tags Input
- Textarea
- Toast
- Toggle
- Toggle Group
- Tool Rail
- Tooltip
- Tree
- Typography
- Vector Pad
Directory
Forms
Utilities
<script setup lang="ts">
import { ref } from 'vue'
import {
IconCursor,
IconMove,
IconSelectEdge,
IconSelectFace,
IconSelectObject,
} from '@/components/ui/icons'
import { ToolRail, ToolRailButton } from '@/components/ui/tool-rail'
const tools = [
{ id: 'select', label: 'Select', icon: IconCursor },
{ id: 'move', label: 'Move', icon: IconMove },
{ id: 'object', label: 'Object mode', icon: IconSelectObject, arrow: true },
{ id: 'edge', label: 'Edge mode', icon: IconSelectEdge },
{ id: 'face', label: 'Face mode', icon: IconSelectFace },
]
const active = ref('object')
// Only the ends of the rail keep their rounding, so the buttons weld together.
function positionOf(index: number) {
if (tools.length === 1)
return 'default'
if (index === 0)
return 'top'
if (index === tools.length - 1)
return 'bottom'
return 'middle'
}
</script>
<template>
<ToolRail>
<ToolRailButton
v-for="(tool, index) in tools"
:key="tool.id"
:label="tool.label"
:position="positionOf(index)"
:active="active === tool.id"
:show-arrow="tool.arrow"
@click="active = tool.id"
>
<component :is="tool.icon" />
</ToolRailButton>
</ToolRail>
</template>Installation
pnpm dlx hui-kit@latest add tool-rail
Usage
<script setup lang="ts">
import { ToolRail, ToolRailButton } from '@/components/ui/tool-rail'
</script>
<template>
<ToolRail>
<ToolRailButton label="Select" position="top" active />
<ToolRailButton label="Move" position="middle" />
<ToolRailButton label="Rotate" position="bottom" show-arrow />
</ToolRail>
</template>position controls which corners stay round, so stacked buttons weld into one
strip. Give the first button top, the last bottom, everything between
middle — the same idea as position on Input, where
it welds fields into a group.
Every button needs a label: it has no visible text, so without one the button
is unnamed for a screen reader.
Examples
Horizontal
<script setup lang="ts">
import { ref } from 'vue'
import { IconAddObjectCube, IconAddObjectCylinder, IconAddObjectHex } from '@/components/ui/icons'
import { ToolRail, ToolRailButton } from '@/components/ui/tool-rail'
const shapes = [
{ id: 'cube', label: 'Add cube', icon: IconAddObjectCube },
{ id: 'hex', label: 'Add hexagon', icon: IconAddObjectHex },
{ id: 'cylinder', label: 'Add cylinder', icon: IconAddObjectCylinder },
]
const active = ref('cube')
</script>
<template>
<ToolRail orientation="horizontal">
<ToolRailButton
v-for="(shape, index) in shapes"
:key="shape.id"
:label="shape.label"
:position="index === 0 ? 'top' : index === shapes.length - 1 ? 'bottom' : 'middle'"
:active="active === shape.id"
@click="active = shape.id"
>
<component :is="shape.icon" />
</ToolRailButton>
</ToolRail>
</template>