Rail
Ultra-narrow icon-only vertical navigation like VS Code's activity bar. Icons use text-muted at rest, accent-primary glow on active. Expands on hover.
<arc-rail> Overview
>
Rail is an ultra-narrow vertical navigation strip — typically 48 to 56 pixels wide — that displays icon-only items in a single column. Inspired by the activity bar in VS Code and similar IDE layouts, it provides top-level section switching without consuming the horizontal space that a full Sidebar requires. Icons render in text-muted at rest and light up with an accent-primary glow when active, giving immediate visual feedback about the current section.
On hover, the Rail can optionally expand to reveal text labels beside each icon, bridging the gap between compact icon-only navigation and a full labeled sidebar. This expand-on-hover behavior is controlled by the `expanded` prop and can also be toggled programmatically for accessibility — some users prefer the labels to remain visible at all times.
Rail is designed to sit at the far-left edge of an AppShell, occupying a fixed vertical strip from top to bottom. It works well alongside a contextual Sidebar: the Rail handles top-level section switching (e.g. Explorer, Search, Source Control) while the Sidebar shows the detail panel for the active section. The component dispatches `arc-change` on item selection so your application can swap the adjacent content area accordingly.Guidelines
When to use
- Limit items to four to seven for a scannable icon column
- Use universally recognizable icons — Rail has no visible labels by default
- Place Rail at the far-left edge of the viewport inside an AppShell
- Pair with a Sidebar to show detail content for the active Rail section
- Provide aria-label on the Rail for screen-reader context
When not to use
- Do not use Rail as the only navigation on a content-heavy site — it is too compact
- Do not add more than seven items — vertical overflow will be confusing
- Do not rely solely on icon recognition — ensure tooltips or expand-on-hover labels are available
- Do not use Rail on mobile viewports — switch to BottomNav instead
- Do not nest a Rail inside a Sidebar — Rail replaces the sidebar for top-level switching
Features
- Ultra-narrow icon-only vertical navigation strip
- Accent-primary glow on active item, text-muted at rest
- Optional expand-on-hover to reveal text labels
- Controlled expanded prop for programmatic label visibility
- `arc-change` event on item selection
- Designed for far-left positioning in AppShell layouts
- Keyboard navigable with arrow keys and Enter activation
- Pairs with Sidebar for section-detail navigation patterns
- Token-driven theming via CSS custom properties
Preview
Usage
This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.
<script type="module" src="@arclux/arc-ui"></script>
<arc-rail
value="explorer"
id="rail"
items='[
{ "icon": "folder", "label": "Explorer", "value": "explorer" },
{ "icon": "magnifying-glass", "label": "Search", "value": "search" },
{ "icon": "git-branch", "label": "Source Control", "value": "scm" },
{ "icon": "puzzle-piece", "label": "Extensions", "value": "extensions" },
{ "icon": "gear", "label": "Settings", "value": "settings" }
]'
></arc-rail>
<script>
document.querySelector('#rail').addEventListener('arc-change', (e) => {
console.log('section:', e.detail.value);
});
</script> import { Rail } from '@arclux/arc-ui-react';
const items = [
{ icon: 'folder', label: 'Explorer', value: 'explorer' },
{ icon: 'search', label: 'Search', value: 'search' },
{ icon: 'git-branch', label: 'Source Control', value: 'scm' },
{ icon: 'puzzle', label: 'Extensions', value: 'extensions' },
{ icon: 'settings', label: 'Settings', value: 'settings' },
];
export function ActivityBar() {
return (
<Rail
items={items}
value="explorer"
onArcChange={(e) => console.log('section:', e.detail.value)}
/>
);
} <script setup>
import { Rail } from '@arclux/arc-ui-vue';
const items = [
{ icon: 'folder', label: 'Explorer', value: 'explorer' },
{ icon: 'search', label: 'Search', value: 'search' },
{ icon: 'git-branch', label: 'Source Control', value: 'scm' },
{ icon: 'puzzle', label: 'Extensions', value: 'extensions' },
{ icon: 'settings', label: 'Settings', value: 'settings' },
];
function onChange(e) {
console.log('section:', e.detail.value);
}
</script>
<template>
<Rail :items="items" value="explorer" @arc-change="onChange" />
</template> <script>
import { Rail } from '@arclux/arc-ui-svelte';
const items = [
{ icon: 'folder', label: 'Explorer', value: 'explorer' },
{ icon: 'search', label: 'Search', value: 'search' },
{ icon: 'git-branch', label: 'Source Control', value: 'scm' },
{ icon: 'puzzle', label: 'Extensions', value: 'extensions' },
{ icon: 'settings', label: 'Settings', value: 'settings' },
];
</script>
<Rail
{items}
value="explorer"
on:arc-change={(e) => console.log('section:', e.detail.value)}
/> import { Component } from '@angular/core';
import { Rail } from '@arclux/arc-ui-angular';
@Component({
imports: [Rail],
template: `
<arc-rail
[items]="items"
value="explorer"
(arc-change)="onChange($event)"
/>
`,
})
export class ActivityBarComponent {
items = [
{ icon: 'folder', label: 'Explorer', value: 'explorer' },
{ icon: 'search', label: 'Search', value: 'search' },
{ icon: 'git-branch', label: 'Source Control', value: 'scm' },
{ icon: 'puzzle', label: 'Extensions', value: 'extensions' },
{ icon: 'settings', label: 'Settings', value: 'settings' },
];
onChange(e: CustomEvent) {
console.log('section:', e.detail.value);
}
} import { Rail } from '@arclux/arc-ui-solid';
const items = [
{ icon: 'folder', label: 'Explorer', value: 'explorer' },
{ icon: 'search', label: 'Search', value: 'search' },
{ icon: 'git-branch', label: 'Source Control', value: 'scm' },
{ icon: 'puzzle', label: 'Extensions', value: 'extensions' },
{ icon: 'settings', label: 'Settings', value: 'settings' },
];
export function ActivityBar() {
return (
<Rail
items={items}
value="explorer"
onArcChange={(e) => console.log('section:', e.detail.value)}
/>
);
} import { Rail } from '@arclux/arc-ui-preact';
const items = [
{ icon: 'folder', label: 'Explorer', value: 'explorer' },
{ icon: 'search', label: 'Search', value: 'search' },
{ icon: 'git-branch', label: 'Source Control', value: 'scm' },
{ icon: 'puzzle', label: 'Extensions', value: 'extensions' },
{ icon: 'settings', label: 'Settings', value: 'settings' },
];
export function ActivityBar() {
return (
<Rail
items={items}
value="explorer"
onArcChange={(e) => console.log('section:', e.detail.value)}
/>
);
} API
-
itemsArray<{icon: string, label: string, value: string}>[] - Array of navigation items, each with an icon name, text label, and value identifier.
-
valuestring'' - The value of the currently active item. Controls which icon receives the accent glow.
-
expandedbooleanfalse - When true, the Rail widens to show text labels beside each icon. Can be toggled on hover or set permanently.
Events
-
arc-changedetail: { value: string } - Fired when an item is selected with detail: { value }.
See Also
- Sidebar Collapsible navigation sidebar with grouped sections, heading labels, and active link highlighting. Ideal for documentation sites, admin panels, and any layout that needs persistent vertical navigation.
- Navigation Menu Horizontal navigation bar with hover-triggered dropdown sub-menus and full keyboard accessibility. Designed for marketing sites, documentation hubs, and product landing pages where top-level sections expand into categorised link lists.
- Bottom Nav Mobile bottom bar with icon + label items. Active item gets accent-primary glow underline with surface-overlay background and backdrop blur.