diff --git a/Documentation/Toolbar/active-state.md b/Documentation/Toolbar/active-state.md
new file mode 100644
index 0000000..cc73c4e
--- /dev/null
+++ b/Documentation/Toolbar/active-state.md
@@ -0,0 +1,26 @@
+# Active State
+
+Use the `active` prop on `ToolbarButton` to highlight the selected tool:
+
+```tsx
+function DrawingToolbar() {
+ const [activeTool, setActiveTool] = useState('select');
+
+ return (
+
+ setActiveTool('select')}
+ />
+ setActiveTool('draw')}
+ />
+
+ );
+}
+```
diff --git a/Documentation/Toolbar/basic-usage.md b/Documentation/Toolbar/basic-usage.md
new file mode 100644
index 0000000..5e73370
--- /dev/null
+++ b/Documentation/Toolbar/basic-usage.md
@@ -0,0 +1,37 @@
+# Basic Usage
+
+Place `ToolbarButton` elements inside a `Toolbar`:
+
+```tsx
+import { Toolbar, ToolbarButton } from '@cratis/components';
+
+function MyToolbar() {
+ return (
+
+
+
+
+
+ );
+}
+```
+
+`ToolbarButton` supports either an icon, text, or both. For text-first controls such as zoom indicators, provide the `text` prop:
+
+```tsx
+import { Toolbar, ToolbarButton, ToolbarSeparator } from '@cratis/components';
+
+function ZoomToolbar() {
+ const [zoom, setZoom] = useState(120);
+
+ return (
+
+ setZoom(value => value - 10)} />
+ setZoom(100)} />
+ setZoom(value => value + 10)} />
+
+
+
+ );
+}
+```
diff --git a/Documentation/Toolbar/context-switching.md b/Documentation/Toolbar/context-switching.md
new file mode 100644
index 0000000..5bea0a2
--- /dev/null
+++ b/Documentation/Toolbar/context-switching.md
@@ -0,0 +1,29 @@
+# Context Switching
+
+`ToolbarSection` and `ToolbarContext` enable smooth animated transitions between different sets of tools. When `activeContext` changes, the current buttons fade out, the section morphs to the new size, then the new buttons fade in.
+
+```tsx
+function ContextualToolbar() {
+ const [mode, setMode] = useState('drawing');
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+Only the section transitions — buttons outside the section are unaffected.
diff --git a/Documentation/Toolbar/fan-out.md b/Documentation/Toolbar/fan-out.md
new file mode 100644
index 0000000..2673107
--- /dev/null
+++ b/Documentation/Toolbar/fan-out.md
@@ -0,0 +1,22 @@
+# Fan-Out Sub-Panel
+
+`ToolbarFanOutItem` replaces a regular button with one that slides out a horizontal panel of additional tools when clicked. The panel closes when clicking the button again or anywhere outside it.
+
+```tsx
+
+
+
+
+
+
+
+
+```
+
+By default the panel fans out to the right. Use `fanOutDirection='left'` when the toolbar is positioned on the right side of the screen:
+
+```tsx
+
+ ...
+
+```
diff --git a/Documentation/Toolbar/index.md b/Documentation/Toolbar/index.md
index e5b6005..068ead3 100644
--- a/Documentation/Toolbar/index.md
+++ b/Documentation/Toolbar/index.md
@@ -1,149 +1,15 @@
# Toolbar
-The `Toolbar` component provides a canvas-style icon toolbar with support for orientations, active states, animated context switching, and fan-out sub-panels.
+The `Toolbar` component provides a canvas-style icon toolbar with support for orientations, active states, animated context switching, separators, and fan-out sub-panels.
## Components
| Component | Description |
|---|---|
| `Toolbar` | Container that groups toolbar buttons into a pill-shaped bar |
-| `ToolbarButton` | Icon button with a hover tooltip |
+| `ToolbarButton` | Button with optional icon and optional text, including hover tooltip |
+| `ToolbarSeparator` | Visual divider that separates groups of buttons |
| `ToolbarSection` | Section within a toolbar that animates between named contexts |
| `ToolbarContext` | Named context (set of buttons) inside a `ToolbarSection` |
| `ToolbarFanOutItem` | Button that slides out a horizontal sub-panel on click |
-## Basic Usage
-
-Place `ToolbarButton` elements inside a `Toolbar`:
-
-```tsx
-import { Toolbar, ToolbarButton } from '@cratis/components';
-
-function MyToolbar() {
- return (
-
-
-
-
-
- );
-}
-```
-
-## Orientation
-
-The toolbar defaults to `vertical`. Pass `orientation='horizontal'` for a horizontal layout:
-
-```tsx
-
-
-
-
-```
-
-## Active State
-
-Use the `active` prop on `ToolbarButton` to highlight the selected tool:
-
-```tsx
-function DrawingToolbar() {
- const [activeTool, setActiveTool] = useState('select');
-
- return (
-
- setActiveTool('select')}
- />
- setActiveTool('draw')}
- />
-
- );
-}
-```
-
-## Context Switching
-
-`ToolbarSection` and `ToolbarContext` enable smooth animated transitions between different sets of tools. When `activeContext` changes, the current buttons fade out, the section morphs to the new size, then the new buttons fade in.
-
-```tsx
-function ContextualToolbar() {
- const [mode, setMode] = useState('drawing');
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-```
-
-Only the section transitions — buttons outside the section are unaffected.
-
-## Fan-Out Sub-Panel
-
-`ToolbarFanOutItem` replaces a regular button with one that slides out a horizontal panel of additional tools when clicked. The panel closes when clicking the button again or anywhere outside it.
-
-```tsx
-
-
-
-
-
-
-
-
-```
-
-By default the panel fans out to the right. Use `fanOutDirection='left'` when the toolbar is positioned on the right side of the screen:
-
-```tsx
-
- ...
-
-```
-
-## Multiple Toolbar Groups
-
-Render multiple `Toolbar` instances to create separate groups, matching the style of canvas-based tools panels:
-
-```tsx
-
-
-
-
-
-
-
-
-
-
-```
-
-## Tooltip Position
-
-Both `ToolbarButton` and `ToolbarFanOutItem` default to showing tooltips on the `right`. Use `tooltipPosition` to override:
-
-```tsx
-
-```
-
-Valid values are `'top'`, `'right'`, `'bottom'`, and `'left'`.
diff --git a/Documentation/Toolbar/multiple-groups.md b/Documentation/Toolbar/multiple-groups.md
new file mode 100644
index 0000000..2d91e7f
--- /dev/null
+++ b/Documentation/Toolbar/multiple-groups.md
@@ -0,0 +1,16 @@
+# Multiple Toolbar Groups
+
+Render multiple `Toolbar` instances to create separate groups, matching the style of canvas-based tools panels:
+
+```tsx
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/Documentation/Toolbar/orientation.md b/Documentation/Toolbar/orientation.md
new file mode 100644
index 0000000..38dbc17
--- /dev/null
+++ b/Documentation/Toolbar/orientation.md
@@ -0,0 +1,10 @@
+# Orientation
+
+The toolbar defaults to `vertical`. Pass `orientation='horizontal'` for a horizontal layout:
+
+```tsx
+
+
+
+
+```
diff --git a/Documentation/Toolbar/sections.md b/Documentation/Toolbar/sections.md
new file mode 100644
index 0000000..1e47eb9
--- /dev/null
+++ b/Documentation/Toolbar/sections.md
@@ -0,0 +1,34 @@
+# Separators
+
+`ToolbarSeparator` renders a thin visual divider between groups of buttons. Unlike `ToolbarSection`, it has no behavioral logic — it simply draws a line perpendicular to the toolbar orientation. In a horizontal toolbar the separator is a vertical rule; in a vertical toolbar it is a horizontal rule.
+
+```tsx
+import { Toolbar, ToolbarButton, ToolbarSeparator } from '@cratis/components';
+
+function ZoomToolbar() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+Pass the same `orientation` value to `ToolbarSeparator` as you pass to the enclosing `Toolbar` so the line is drawn perpendicular to the toolbar direction.
+
+## Vertical toolbar
+
+In a vertical toolbar (the default) the separator is a horizontal rule:
+
+```tsx
+
+
+
+
+
+```
diff --git a/Documentation/Toolbar/toc.yml b/Documentation/Toolbar/toc.yml
index 1ba183c..09e367b 100644
--- a/Documentation/Toolbar/toc.yml
+++ b/Documentation/Toolbar/toc.yml
@@ -1,2 +1,18 @@
- name: Overview
href: index.md
+- name: Basic Usage
+ href: basic-usage.md
+- name: Orientation
+ href: orientation.md
+- name: Active State
+ href: active-state.md
+- name: Separators
+ href: sections.md
+- name: Context Switching
+ href: context-switching.md
+- name: Fan-Out Sub-Panel
+ href: fan-out.md
+- name: Multiple Groups
+ href: multiple-groups.md
+- name: Tooltip Position
+ href: tooltip-position.md
diff --git a/Documentation/Toolbar/tooltip-position.md b/Documentation/Toolbar/tooltip-position.md
new file mode 100644
index 0000000..c18ff8e
--- /dev/null
+++ b/Documentation/Toolbar/tooltip-position.md
@@ -0,0 +1,9 @@
+# Tooltip Position
+
+Both `ToolbarButton` and `ToolbarFanOutItem` default to showing tooltips on the `right`. Use `tooltipPosition` to override:
+
+```tsx
+
+```
+
+Valid values are `'top'`, `'right'`, `'bottom'`, and `'left'`.
diff --git a/Source/Toolbar/Toolbar.css b/Source/Toolbar/Toolbar.css
index 499d154..13063c1 100644
--- a/Source/Toolbar/Toolbar.css
+++ b/Source/Toolbar/Toolbar.css
@@ -31,6 +31,13 @@
color: var(--primary-color-text);
}
+.toolbar-button__text {
+ font-size: 0.875rem;
+ font-weight: 600;
+ line-height: 1;
+ white-space: nowrap;
+}
+
/* ── Toolbar project name label ──────────────────────────────────────────── */
.toolbar-project-name {
padding: 0 8px;
@@ -77,6 +84,26 @@
pointer-events: none;
}
+/* ── Toolbar separator ───────────────────────────────────────────────────── */
+
+.toolbar-separator {
+ background: var(--surface-border);
+ flex-shrink: 0;
+ align-self: stretch;
+}
+
+/* Separator inside a vertical toolbar — renders as a horizontal rule */
+.toolbar-separator--in-vertical {
+ height: 1px;
+ margin: 0.125rem 0.25rem;
+}
+
+/* Separator inside a horizontal toolbar — renders as a vertical rule */
+.toolbar-separator--in-horizontal {
+ width: 1px;
+ margin: 0.25rem 0.125rem;
+}
+
/* ── Toolbar fan-out item ────────────────────────────────────────────────── */
/*
diff --git a/Source/Toolbar/Toolbar.stories.tsx b/Source/Toolbar/Toolbar.stories.tsx
index 23005b7..5a7c5cc 100644
--- a/Source/Toolbar/Toolbar.stories.tsx
+++ b/Source/Toolbar/Toolbar.stories.tsx
@@ -8,6 +8,7 @@ import { ToolbarButton } from './ToolbarButton';
import { ToolbarContext } from './ToolbarContext';
import { ToolbarFanOutItem } from './ToolbarFanOutItem';
import { ToolbarSection } from './ToolbarSection';
+import { ToolbarSeparator } from './ToolbarSeparator';
const meta: Meta = {
title: 'Components/Toolbar',
@@ -154,6 +155,56 @@ export const WithContexts: Story = {
},
};
+/**
+ * Demonstrates {@link ToolbarSeparator} in a horizontal toolbar.
+ *
+ * The separator renders as a thin vertical line between groups of buttons,
+ * matching the style seen in canvas-based tools (e.g. Miro, Figma).
+ * When the toolbar is vertical the line is horizontal.
+ */
+export const WithSeparators: Story = {
+ render: () => (
+
+
+
+
+
+
+
+
+ ),
+};
+
+/**
+ * Demonstrates a zoom-style horizontal toolbar where the center text button
+ * resets the zoom level to 100% when clicked.
+ */
+export const ZoomBar: Story = {
+ render: () => {
+ const ZoomBarDemo = () => {
+ const [zoom, setZoom] = useState(120);
+
+ const zoomOut = () => setZoom(current => Math.max(50, current - 10));
+ const zoomIn = () => setZoom(current => Math.min(300, current + 10));
+ const resetZoom = () => setZoom(100);
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+ };
+
+ return ;
+ },
+};
+
/**
* Demonstrates a {@link ToolbarFanOutItem} inside a vertical toolbar.
*
diff --git a/Source/Toolbar/ToolbarButton.tsx b/Source/Toolbar/ToolbarButton.tsx
index 4d3ad71..5ee5bbf 100644
--- a/Source/Toolbar/ToolbarButton.tsx
+++ b/Source/Toolbar/ToolbarButton.tsx
@@ -7,7 +7,10 @@ import type { TooltipPosition } from '../Common/Tooltip';
/** Props for the {@link ToolbarButton} component. */
export interface ToolbarButtonProps {
/** The PrimeIcons CSS class to use as the icon (e.g. 'pi pi-home'). */
- icon: string;
+ icon?: string;
+
+ /** Optional text to render inside the button (e.g. '120%'). */
+ text?: string;
/** Tooltip text shown when the user hovers over the button. */
tooltip: string;
@@ -26,8 +29,16 @@ export interface ToolbarButtonProps {
* An icon button with a tooltip, intended to be placed inside a {@link Toolbar}.
* Uses the shared {@link Tooltip} component for consistent hover labels.
*/
-export const ToolbarButton = ({ icon, tooltip, active = false, onClick, tooltipPosition = 'right' }: ToolbarButtonProps) => {
+export const ToolbarButton = ({ icon, text, tooltip, active = false, onClick, tooltipPosition = 'right' }: ToolbarButtonProps) => {
const activeClass = active ? 'toolbar-button--active' : '';
+ const hasText = typeof text === 'string' && text.length > 0;
+ const hasIcon = typeof icon === 'string' && icon.length > 0;
+ const buttonContent = hasText
+ ? {text}
+ : hasIcon
+ ?
+ : null;
+ const buttonSizeClass = hasText ? 'h-10 px-3 min-w-[4rem]' : 'w-10 h-10';
return (
@@ -35,9 +46,9 @@ export const ToolbarButton = ({ icon, tooltip, active = false, onClick, tooltipP
type='button'
aria-label={tooltip}
onClick={onClick}
- className={`toolbar-button w-10 h-10 flex items-center justify-center rounded-lg cursor-pointer ${activeClass}`}
+ className={`toolbar-button ${buttonSizeClass} flex items-center justify-center rounded-lg cursor-pointer ${activeClass}`}
>
-
+ {buttonContent}
);
diff --git a/Source/Toolbar/ToolbarSeparator.tsx b/Source/Toolbar/ToolbarSeparator.tsx
new file mode 100644
index 0000000..6f78aa6
--- /dev/null
+++ b/Source/Toolbar/ToolbarSeparator.tsx
@@ -0,0 +1,22 @@
+// Copyright (c) Cratis. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+/** Props for the {@link ToolbarSeparator} component. */
+export interface ToolbarSeparatorProps {
+ /** Layout direction matching the parent {@link Toolbar} (default: 'vertical'). */
+ orientation?: 'vertical' | 'horizontal';
+}
+
+/**
+ * A visual divider for use inside a {@link Toolbar}.
+ *
+ * In a vertical toolbar (default) the separator renders as a horizontal rule.
+ * In a horizontal toolbar it renders as a vertical rule.
+ */
+export const ToolbarSeparator = ({ orientation = 'vertical' }: ToolbarSeparatorProps) => (
+
+);
diff --git a/Source/Toolbar/index.ts b/Source/Toolbar/index.ts
index 9dee619..cdcece9 100644
--- a/Source/Toolbar/index.ts
+++ b/Source/Toolbar/index.ts
@@ -9,5 +9,7 @@ export { ToolbarContext } from './ToolbarContext';
export type { ToolbarContextProps } from './ToolbarContext';
export { ToolbarSection } from './ToolbarSection';
export type { ToolbarSectionProps } from './ToolbarSection';
+export { ToolbarSeparator } from './ToolbarSeparator';
+export type { ToolbarSeparatorProps } from './ToolbarSeparator';
export { ToolbarFanOutItem } from './ToolbarFanOutItem';
export type { ToolbarFanOutItemProps } from './ToolbarFanOutItem';