Blocks · Navigation
Icon sidebar
An app sidebar: product mark, links with icons, the current one marked with aria-current, settings pinned at the bottom.
13% of the 2,073 sites in The UI Index have a sidebar (261 sites).
Overview
Three deploys went live today and none needed a rollback. Open the Activity tab to see who shipped what.
Overview
Three deploys went live today and none needed a rollback. Open the Activity tab to see who shipped what.
Both previews render the same markup; the HTML tab below is that markup without a framework.
Install
- React
- npx shadcn@
latest add https:// www.themes.dev/ ui/ r/ icon-sidebar.json - Astro
- npx shadcn@
latest add https:// www.themes.dev/ ui/ r/ astro/ icon-sidebar.json
Or copy the files below into your project. They need clsx, tailwind-merge and the components they import, and the themes.dev UI theme. The HTML needs only the compiled CSS.
import type { IconName } from '@/components/ui/icon';
/** An app sidebar: the product mark at the top, a list of links each with an icon, the current one marked with
* aria-current="page", a settings link pinned to the bottom. It fills the height of the space it is given (at
* least 26rem). For a docs sidebar (text links under headings) see the sidebar-nav item. */
export const iconSidebar = {
root: 'w-full',
aside: 'flex h-full min-h-[26rem] min-w-0 flex-col gap-6 bg-muted/40 p-4 text-card-foreground',
brand: 'flex min-h-10 items-center gap-2 rounded-md px-3 text-base font-semibold text-foreground transition-colors hover:bg-accent active:bg-pressed',
brandMark: 'grid size-6 shrink-0 place-items-center rounded-md bg-primary text-primary-foreground',
nav: 'flex min-w-0 flex-1 flex-col gap-1',
list: 'm-0 flex list-none flex-col gap-1 p-0',
link: 'flex min-h-10 w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground active:bg-pressed aria-[current=page]:bg-accent aria-[current=page]:text-foreground aria-[current=page]:hover:bg-input/30 aria-[current=page]:active:bg-pressed',
icon: 'shrink-0',
label: 'min-w-0 flex-1 text-left',
footer: 'mt-auto flex flex-col gap-1 border-t pt-4',
};
/** The product mark's glyph on the 16px grid (a four-point star; the free icon set has none). */
export const MARK_PATH = 'M8 2l1.8 4.2L14 8l-4.2 1.8L8 14l-1.8-4.2L2 8l4.2-1.8z';
export type SidebarLink = {
label: string;
href: string;
icon: IconName;
/** The page the visitor is on. One link in the whole sidebar. */
current?: boolean;
};
export type IconSidebarContent = {
/** The navigation's accessible name. Default: Main. */
label?: string;
brand: { name: string; href: string };
links: SidebarLink[];
/** Pinned to the bottom (Settings). */
footer?: SidebarLink[];
id?: string;
};import { Icon } from '@/components/ui/icon';
import { cn } from '@/lib/utils';
import { iconSidebar as c, MARK_PATH, type IconSidebarContent, type SidebarLink } from './icon-sidebar.classes';
export type IconSidebarProps = IconSidebarContent & { className?: string };
const row = (l: SidebarLink) => (
<li key={l.href}>
<a href={l.href} aria-current={l.current ? 'page' : undefined} className={c.link}>
<Icon name={l.icon} className={c.icon} />
<span className={c.label}>{l.label}</span>
</a>
</li>
);
/** An app sidebar: product mark, links with icons (the current one aria-current), settings pinned at the bottom. */
export function IconSidebar({ label = 'Main', brand, links, footer, id, className }: IconSidebarProps) {
return (
<div id={id} className={cn(c.root, className)}>
<aside className={c.aside}>
<a href={brand.href} className={c.brand}>
<span className={c.brandMark} aria-hidden="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className="size-4"><path d={MARK_PATH} /></svg></span>
<span>{brand.name}</span>
</a>
<nav aria-label={label} className={c.nav}>
<ul className={c.list}>{links.map(row)}</ul>
{footer?.length ? (
<div className={c.footer}>
<ul className={c.list}>{footer.map(row)}</ul>
</div>
) : null}
</nav>
</aside>
</div>
);
}---
import Icon from '@/components/ui/icon.astro';
import { cn } from '@/lib/utils';
import { iconSidebar as c, MARK_PATH, type IconSidebarContent } from './icon-sidebar.classes';
type Props = IconSidebarContent & { class?: string };
const { label = 'Main', brand, links, footer, id, class: className } = Astro.props;
---
<div id={id} class={cn(c.root, className)}>
<aside class={c.aside}>
<a href={brand.href} class={c.brand}>
<span class={c.brandMark} aria-hidden="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="size-4"><path d={MARK_PATH} /></svg></span>
<span>{brand.name}</span>
</a>
<nav aria-label={label} class={c.nav}>
<ul class={c.list}>
{links.map((l) => (
<li><a href={l.href} aria-current={l.current ? 'page' : undefined} class={c.link}><Icon name={l.icon} class={c.icon} /><span class={c.label}>{l.label}</span></a></li>
))}
</ul>
{footer?.length ? (
<div class={c.footer}>
<ul class={c.list}>
{footer.map((l) => (
<li><a href={l.href} aria-current={l.current ? 'page' : undefined} class={c.link}><Icon name={l.icon} class={c.icon} /><span class={c.label}>{l.label}</span></a></li>
))}
</ul>
</div>
) : null}
</nav>
</aside>
</div>import type { IconName } from '@/components/ui/icon';
/** An app sidebar: the product mark at the top, a list of links each with an icon, the current one marked with
* aria-current="page", a settings link pinned to the bottom. It fills the height of the space it is given (at
* least 26rem). For a docs sidebar (text links under headings) see the sidebar-nav item. */
export const iconSidebar = {
root: 'w-full',
aside: 'flex h-full min-h-[26rem] min-w-0 flex-col gap-6 bg-muted/40 p-4 text-card-foreground',
brand: 'flex min-h-10 items-center gap-2 rounded-md px-3 text-base font-semibold text-foreground transition-colors hover:bg-accent active:bg-pressed',
brandMark: 'grid size-6 shrink-0 place-items-center rounded-md bg-primary text-primary-foreground',
nav: 'flex min-w-0 flex-1 flex-col gap-1',
list: 'm-0 flex list-none flex-col gap-1 p-0',
link: 'flex min-h-10 w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground active:bg-pressed aria-[current=page]:bg-accent aria-[current=page]:text-foreground aria-[current=page]:hover:bg-input/30 aria-[current=page]:active:bg-pressed',
icon: 'shrink-0',
label: 'min-w-0 flex-1 text-left',
footer: 'mt-auto flex flex-col gap-1 border-t pt-4',
};
/** The product mark's glyph on the 16px grid (a four-point star; the free icon set has none). */
export const MARK_PATH = 'M8 2l1.8 4.2L14 8l-4.2 1.8L8 14l-1.8-4.2L2 8l4.2-1.8z';
export type SidebarLink = {
label: string;
href: string;
icon: IconName;
/** The page the visitor is on. One link in the whole sidebar. */
current?: boolean;
};
export type IconSidebarContent = {
/** The navigation's accessible name. Default: Main. */
label?: string;
brand: { name: string; href: string };
links: SidebarLink[];
/** Pinned to the bottom (Settings). */
footer?: SidebarLink[];
id?: string;
};<div class="@container">
<div class="flex flex-col overflow-hidden rounded-xl border @lg:flex-row">
<div class="border-b @lg:w-60 @lg:shrink-0 @lg:border-e @lg:border-b-0">
<div id="ex-sidebar" class="w-full">
<aside class="flex h-full min-h-[26rem] min-w-0 flex-col gap-6 bg-muted/40 p-4 text-card-foreground">
<a href="#home" class="flex min-h-10 items-center gap-2 rounded-md px-3 text-base font-semibold text-foreground transition-colors hover:bg-accent active:bg-pressed">
<span class="grid size-6 shrink-0 place-items-center rounded-md bg-primary text-primary-foreground" aria-hidden="true">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="size-4"><path d="M8 2l1.8 4.2L14 8l-4.2 1.8L8 14l-1.8-4.2L2 8l4.2-1.8z"></path></svg>
</span>
<span>Northwind</span>
</a>
<nav aria-label="App (example)" class="flex min-w-0 flex-1 flex-col gap-1">
<ul class="m-0 flex list-none flex-col gap-1 p-0">
<li>
<a href="#overview" aria-current="page" class="flex min-h-10 w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground active:bg-pressed aria-[current=page]:bg-accent aria-[current=page]:text-foreground aria-[current=page]:hover:bg-input/30 aria-[current=page]:active:bg-pressed">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="inline-block size-4 shrink-0"><path d="M2.5 4h11v9.5h-11zM2.5 7h11M5.5 2.5v3M10.5 2.5v3"></path></svg>
<span class="min-w-0 flex-1 text-left">Overview</span>
</a>
</li>
<li>
<a href="#inbox" class="flex min-h-10 w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground active:bg-pressed aria-[current=page]:bg-accent aria-[current=page]:text-foreground aria-[current=page]:hover:bg-input/30 aria-[current=page]:active:bg-pressed">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="inline-block size-4 shrink-0"><path d="M2 3.5h12v9H2zM2.5 4l5.5 4.5L13.5 4"></path></svg>
<span class="min-w-0 flex-1 text-left">Inbox</span>
</a>
</li>
<li>
<a href="#activity" class="flex min-h-10 w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground active:bg-pressed aria-[current=page]:bg-accent aria-[current=page]:text-foreground aria-[current=page]:hover:bg-input/30 aria-[current=page]:active:bg-pressed">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="inline-block size-4 shrink-0"><path d="M4 11V7a4 4 0 0 1 8 0v4l1.5 1.5h-11zM6.5 14h3"></path></svg>
<span class="min-w-0 flex-1 text-left">Activity</span>
</a>
</li>
<li>
<a href="#team" class="flex min-h-10 w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground active:bg-pressed aria-[current=page]:bg-accent aria-[current=page]:text-foreground aria-[current=page]:hover:bg-input/30 aria-[current=page]:active:bg-pressed">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="inline-block size-4 shrink-0"><path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM2.5 14.5a5.5 5.5 0 0 1 11 0"></path></svg>
<span class="min-w-0 flex-1 text-left">Team</span>
</a>
</li>
<li>
<a href="#search" class="flex min-h-10 w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground active:bg-pressed aria-[current=page]:bg-accent aria-[current=page]:text-foreground aria-[current=page]:hover:bg-input/30 aria-[current=page]:active:bg-pressed">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="inline-block size-4 shrink-0"><path d="M7 12.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM11 11l3.5 3.5"></path></svg>
<span class="min-w-0 flex-1 text-left">Search</span>
</a>
</li>
</ul>
<div class="mt-auto flex flex-col gap-1 border-t pt-4">
<ul class="m-0 flex list-none flex-col gap-1 p-0">
<li>
<a href="#settings" class="flex min-h-10 w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground active:bg-pressed aria-[current=page]:bg-accent aria-[current=page]:text-foreground aria-[current=page]:hover:bg-input/30 aria-[current=page]:active:bg-pressed">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="inline-block size-4 shrink-0"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zM8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.4 3.4l1.4 1.4M11.2 11.2l1.4 1.4M3.4 12.6l1.4-1.4M11.2 4.8l1.4-1.4"></path></svg>
<span class="min-w-0 flex-1 text-left">Settings</span>
</a>
</li>
</ul>
</div>
</nav>
</aside>
</div>
</div>
<div class="flex-1 p-6">
<h2 class="text-xl font-semibold tracking-[-0.02em]">Overview</h2>
<p class="mt-2 max-w-prose text-muted-foreground">Three deploys went live today and none needed a rollback. Open the Activity tab to see who shipped what.</p>
</div>
</div>
</div>