ATOM
Components (registry)Navigation

Sidebar

Collapsible app navigation with nested items and sections. Primary product shell chrome.

Preview

Editorial

<!-- F12c editorial — non-derivable only. Review: Karen. -->

## Ejemplos

App shell chrome:

```tsx import { SidebarProvider, Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarItem, SidebarTrigger, } from '@/components/molecules/sidebar';

<SidebarProvider> <Sidebar side="left"> <SidebarHeader> <SidebarTrigger /> </SidebarHeader> <SidebarContent> <SidebarItem href="/home">Home</SidebarItem> </SidebarContent> <SidebarFooter>v1</SidebarFooter> </Sidebar> </SidebarProvider> ```

## Accesibilidad

- Must wrap with `SidebarProvider` (collapse state); `useSidebar()` outside throws. - Collapse is provider state, not a prop on `Sidebar` — toggle via `SidebarTrigger` / `toggle()`.

## Cuándo no usar

- Temporary filters or mobile sheets → `Drawer` / `Sheet`. - A single short link list in a page body → plain nav, not the full shell sidebar.

## Criterio de uso

- Usa Sidebar para navegacion persistente de una aplicacion, no para menus de sitio: su valor esta en mantener el contexto entre vistas. - El estado colapsado debe sobrevivir la navegacion; recordar la preferencia evita que el usuario la reajuste en cada pagina. - Agrupa por tarea del usuario, no por modulo tecnico, y limita la anidacion a un nivel.

## Gotchas

- Son 11 sub-componentes con un Provider: montar Content sin el Provider deja el colapso sin estado compartido. - `side` cambia la relacion espacial con el contenido, no solo el lado: en `right` el foco de lectura invierte y conviene revisar el orden de tabulacion.

Uso

import {
  SidebarProvider, Sidebar, SidebarHeader, SidebarContent,
  SidebarFooter, SidebarItem, SidebarTrigger,
} from '@/components/molecules/sidebar';

<SidebarProvider>
  <Sidebar side="left">
    <SidebarHeader>
      <SidebarTrigger />
    </SidebarHeader>
    <SidebarContent>
      <SidebarItem href="/home">Home</SidebarItem>
    </SidebarContent>
    <SidebarFooter>v1</SidebarFooter>
  </Sidebar>
</SidebarProvider>

Props

PropTipoDefaultRango / opcionesWhatHow
sideselectleft`left`, `right`Which edge of the layout the sidebar docks to.left for app chrome; right for inspector panels. Default: left.

Gotchas

  • react

    Must wrap with SidebarProvider (collapsed state). Compose SidebarHeader/Content/Footer + SidebarItem/Trigger; useSidebar() outside provider throws.

  • layout

    collapsed class is driven by provider state, not a Sidebar prop — toggle via SidebarTrigger or useSidebar().toggle.

Anatomía CSS

<div class="sidebar-collapsible">
  <span class="sidebar-collapsible__chevron"></span>
  <span class="sidebar-collapsible__content"></span>
  <span class="sidebar-collapsible__inner"></span>
  <span class="sidebar-collapsible__label"></span>
  <span class="sidebar-collapsible__trigger"></span>
</div>
ClasePropósito
sidebar-collapsibleroot
sidebar-collapsible--openmodifier
sidebar-collapsible__chevronelement
sidebar-collapsible__contentelement
sidebar-collapsible__innerelement
sidebar-collapsible__labelelement
sidebar-collapsible__triggerelement

Tokens resueltos

Valores finales tras seguir la cadena de tokens. Derivados del source: si un token cambia, esta tabla cambia sola.

VariantePropValorToken
allbgnone(unparsed)
allfg#525252muted.foreground
allbordernone(unparsed)
allhover-bg#f5f5f5muted
allhover-fg#0a0a0aforeground
collapsedbg#fafafabackground
collapsedfg#0a0a0aforeground
collapsedhover-bg#f5f5f5muted
collapsedhover-fg#525252placeholder
collapsedborder#e5e5e5border

Animaciones

PropiedadDuraciónEasing
width0.3scubic-bezier(0.19
1
0.22
1)
background-color0.15scubic-bezier(0.19
1
0.22
1)
color0.15scubic-bezier(0.19
1
0.22
1)
scale0.45scubic-bezier(0.32
0.72
0
1)
rotate0.5scubic-bezier(0.19
1
0.22
1)
rotate0.6s0.05s cubic-bezier(0.19
1
0.22
1)
background-color0.15sease
color0.15sease
box-shadow0.15sease
background-color0.15sease
color0.15sease
transform0.2sease
grid-template-rows0.25scubic-bezier(0.19
1
0.22
1)

CSS mínimo funcional

Autocontenido: sin imports ni tokens. Para previews y prototipos — en producción se consume el CSS del DS (@atom-uikit/css/components.css</code>, <code>@atom-uikit/tokens/tokens.css).

/* -------------------------------------------------------------------------
   Sidebar

   Composable: Provider (state) + Container + Header/Content/Footer
   Collapse: .sidebar--collapsed toggles layout via CSS
   Labels: [data-sidebar-label] hidden in collapsed via display:none
   Tooltips: [data-tooltip] from tooltip.css, overridden to right/light
   Animations: scale on hover, icon rotation in collapsed

   240px expanded, 48px collapsed. Semantic tokens for colors.
   ------------------------------------------------------------------------- */

/* ---- Container ---- */

.sidebar {
  --sidebar-w: 240px;
  --sidebar-w-collapsed: 48px;

  display: flex;
  flex-direction: column;
  width: var(--sidebar-w);
  height: 100%;
  min-height: 100%;
  background: #f5f5f5;
  border-right: 1px solid #efefef;
  flex-shrink: 0;
  transition: width 0.3s cubic-bezier(0.19, 1, 0.22, 1);
}

.sidebar--collapsed { width: var(--sidebar-w-collapsed); }

/* ---- Sections ---- */

.sidebar__header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 12px;
  flex-shrink: 0;
}

.sidebar__content {
  flex: 1;
  overflow-y: auto;
  padding: 8px;
}

.sidebar__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px;
  flex-shrink: 0;
  margin-top: auto;
  border-top: 1px solid #efefef;
}

.sidebar__divider {
  border: none;
  border-top: 1px solid #e5e5e5;
  margin: 8px 0;
}

/* Collapsed sections */
.sidebar--collapsed .sidebar__header { flex-direction: column; gap: 8px; padding: 8px; align-items: center; }
.sidebar--collapsed .sidebar__content { overflow: visible; padding: 4px; }
.sidebar__footer .dropdown-menu { width: 100%; }
.sidebar__footer .dropdown-menu__trigger { width: 100%; }
.sidebar__footer .dropdown-menu__content { width: 100%; }

.sidebar--collapsed .sidebar__footer { padding: 8px; justify-content: center; }

/* ---- Labels: hide in collapsed ---- */

.sidebar--collapsed [data-sidebar-label] { display: none; }

/* ---- Group ---- */

.sidebar__group { display: flex; flex-direction: column; gap: 2px; }
.sidebar__group + .sidebar__group { margin-top: 24px; }

.sidebar__group-label {
  font-family: 'inter tight', -apple-system, blinkmacsystemfont, 'segoe ui', roboto, helvetica, arial, sans-serif, ui-sans-serif, system-ui, sans-serif;
  font-size: 10.24px;
  font-weight: 500;
  color: #525252;
  padding: 4px 8px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sidebar--collapsed .sidebar__group-label { display: none; }

/* ---- Item ---- */

.sidebar__item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 8px;
  height: 36px;
  border-radius: 6px;
  font-family: 'inter tight', -apple-system, blinkmacsystemfont, 'segoe ui', roboto, helvetica, arial, sans-serif, ui-sans-serif, system-ui, sans-serif;
  font-size: 12.8px;
  font-weight: 500;
  line-height: 1;
  color: #0a0a0a;
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  transition:
    background-color 0.15s cubic-bezier(0.19, 1, 0.22, 1),
    color 0.15s cubic-bezier(0.19, 1, 0.22, 1),
    scale 0.45s cubic-bezier(0.32, 0.72, 0, 1);
}

.sidebar__item:hover { background: #f5f5f5; color: #525252; }
.sidebar__item:active:not(.sidebar__item--disabled) { scale: 0.955 0.925; }
.sidebar__item--active { background: #fff4ed; color: #a44200; }
.sidebar__item--disabled { color: #525252; cursor: not-allowed; pointer-events: none; }

/* Item parts */
.sidebar__item-icon { display: flex; align-items: center; justify-content: center; flex-shrink: 0; width: 18px; height: 18px; transition: rotate 0.5s cubic-bezier(0.19, 1, 0.22, 1); }
.sidebar__item-icon svg { width: 100%; height: 100%; }
.sidebar__item-label { flex: 1; overflow: hidden; text-overflow: ellipsis; }
.sidebar__item-badge { font-size: 10.24px; font-weight: 500; color: #525252; flex-shrink: 0; }

/* Collapsed items: icon-only, centered */
.sidebar--collapsed .sidebar__item { justify-content: center; padding: 0; width: 32px; height: 32px; margin: 0 auto; }
.sidebar--collapsed .sidebar__item-label,
.sidebar--collapsed .sidebar__item-badge { display: none; }

/* ---- Hover animations ---- */

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  /* Expanded: subtle Button-style grow */
  .sidebar:not(.sidebar--collapsed) .sidebar__item:hover:not(.sidebar__item--disabled):not(.sidebar__item--active) {
    scale: 1.03 1.05;
  }
  /* Collapsed: IconButton-style scale + icon spin */
  .sidebar--collapsed .sidebar__item:hover:not(.sidebar__item--disabled):not(.sidebar__item--active) {
    scale: 1.095;
  }
  .sidebar--collapsed .sidebar__item:hover .sidebar__item-icon {
    rotate: 360deg;
    transition: rotate 0.6s 0.05s cubic-bezier(0.19, 1, 0.22, 1);
  }
}

/* ---- Trigger ---- */

.sidebar__trigger {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 4px;
  border: none;
  background: #f5f5f5;
  color: #525252;
  cursor: pointer;
  border-radius: 4px;
  flex-shrink: 0;
  transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}

.sidebar__trigger:hover { background: #e5e5e5; color: #0a0a0a; }
.sidebar__trigger svg { width: 16px; height: 16px; }

.sidebar--collapsed .sidebar__trigger { background: #f5f5f5; color: #525252; box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); }
.sidebar--collapsed .sidebar__trigger:hover { background: #f5f5f5; color: #525252; }

/* ---- Tooltips (collapsed only) ---- */

.sidebar:not(.sidebar--collapsed) [data-tooltip]::before,
.sidebar:not(.sidebar--collapsed) [data-tooltip]::after { display: none !important; }

.sidebar--collapsed [data-tooltip]::before {
  bottom: auto !important; top: 50% !important;
  left: calc(100% + 10px) !important; right: auto !important;
  transform: translateY(-50%) translateX(-4px) !important;
  background: #fafafa !important; color: #0a0a0a !important;
  border: 1px solid #e5e5e5 !important;
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.1) !important;
}
.sidebar--collapsed [data-tooltip]:hover::before { transform: translateY(-50%) translateX(0) !important; }

.sidebar--collapsed [data-tooltip]::after {
  bottom: auto !important; top: 50% !important;
  left: calc(100% + 7px) !important; right: auto !important;
  transform: translateY(-50%) rotate(225deg) !important;
  background: #fafafa !important;
  border: 1px solid #e5e5e5 !important;
  border-top: none !important; border-left: none !important;
}

/* ---- Collapsible ---- */

.sidebar-collapsible__trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 8px;
  height: 36px;
  width: 100%;
  border: none;
  background: none;
  border-radius: 6px;
  font-family: 'inter tight', -apple-system, blinkmacsystemfont, 'segoe ui', roboto, helvetica, arial, sans-serif, ui-sans-serif, system-ui, sans-serif;
  font-size: 12.8px;
  font-weight: 500;
  line-height: 1;
  color: #0a0a0a;
  cursor: pointer;
  user-select: none;
  text-align: left;
  white-space: nowrap;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.sidebar-collapsible__trigger:hover { background: #f5f5f5; }
.sidebar-collapsible__label { flex: 1; overflow: hidden; text-overflow: ellipsis; }
.sidebar-collapsible__chevron { display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: #525252; transition: transform 0.2s ease; }

.sidebar-collapsible--open .sidebar-collapsible__chevron { transform: rotate(0deg); }
.sidebar-collapsible:not(.sidebar-collapsible--open) .sidebar-collapsible__chevron { transform: rotate(-90deg); }

/* Collapsible content: animated height */
.sidebar-collapsible__content { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 0.25s cubic-bezier(0.19, 1, 0.22, 1); }
.sidebar-collapsible:not(.sidebar-collapsible--open) .sidebar-collapsible__content { grid-template-rows: 0fr; }
.sidebar-collapsible__inner { overflow: hidden; min-height: 0; display: flex; flex-direction: column; gap: 2px; }
.sidebar-collapsible__inner .sidebar__item { padding-left: 34px; }

/* Collapsed: collapsible becomes icon-only */
.sidebar--collapsed .sidebar-collapsible__trigger { justify-content: center; padding: 0; width: 32px; height: 32px; margin: 0 auto; }
.sidebar--collapsed .sidebar-collapsible__label,
.sidebar--collapsed .sidebar-collapsible__chevron { display: none; }
.sidebar--collapsed .sidebar-collapsible__content { display: none; }

Codigo fuente

components-react
components/molecules/sidebar/Sidebar.tsx
import { type ReactNode } from 'react';
import { useSidebar } from './SidebarContext';

function cn(...classes: (string | false | undefined | null)[]) {
  return classes.filter(Boolean).join(' ');
}

export type SidebarProps = {
  side?: 'left' | 'right';
  children: ReactNode;
  className?: string;
};

export function Sidebar({ side = 'left', children, className }: SidebarProps) {
  const { collapsed } = useSidebar();
  return (
    <aside
      data-sidebar=""
      className={cn(
        'sidebar',
        collapsed && 'sidebar--collapsed',
        side === 'right' && 'sidebar--right',
        className,
      )}
    >
      {children}
    </aside>
  );
}

Componentes relacionados

On this page

Detalles

Publicado14 de mayo de 2026
Categorianavigation
Lectura...
Visitas...
Ayuda?Slack

Componente

Sidebar

Source

components-react / cssDisponible via MCP: atom_uikit_source("sidebar")
Abrir en Storybook