Navigation

Profile Footer Nav

A menu with a profile card pinned to the bottom: settings and sign-out open upwards instead of sitting in the list of sections.

  • sidebar
  • profile
  • account
  • footer

Preview

1440pxHost theme

Use it with AI

  1. 1. Copy the link.
  2. 2. Write to your agent in your own words and drop the link into the sentence.
  3. 3. The agent opens the link and installs the component from the registry.

put this in the header: https://vibeui.ru/c/sidebar-006?lang=en

Show the full instructions

What the link says, spelled out. Use it when your agent cannot open links — paste this text instead.

# Install and place "sidebar-006" (Profile Footer Nav) from VibeUI

## 1. Install first — do not skip, do not recreate
Run this exact command before writing any code:
npx shadcn@latest add https://vibeui.ru/r/sidebar-006.json

Registry item: https://vibeui.ru/r/sidebar-006.json
Installs to: components/vibeui/sidebar-006.tsx (the exact path follows this project's components.json aliases).
npm dependencies: none.
Registry dependencies: none.

Install it from the registry. Do not recreate it from the description,
do not substitute a similar component from another library, and do not
rewrite it to match the project's existing style.

## 2. What it is
A menu with a profile card pinned to the bottom: settings and sign-out open upwards instead of sitting in the list of sections.

A sidebar with a profile at the bottom and an account menu opening upwards on <details>. Zero dependencies, one file, no client JS.

## 3. How to use it
import { Sidebar006 } from "@/components/vibeui/sidebar-006"

<Sidebar006
  userName="Olga Dorn"
  userMeta="olga@studio.ru"
  activeLabel="Projects"
  items={[{ label: "Feed", href: "/" }, { label: "Projects", href: "/projects" }]}
/>

Read the installed file for the full prop list.

## 4. Where to place it
This is a small inline component. Put it exactly where the user asked,
inside the existing markup. Do not create a new page, section or
wrapper for it. If a similar control already sits in that spot,
replace it instead of adding a second one.

Placement: ___
(The user fills this line in. If it is still blank, ask where to put it
instead of guessing.)

## 5. Keep exactly as installed
- the local --vibeui-sidebar-006-* palette — do not swap it for your theme tokens
- the flex:1 spacer instead of a margin: the profile must sit at the bottom at any height
- the menu opening upwards — downwards it hits the column edge and gets clipped
- sign-out and settings inside the profile menu, not in the section list: they cannot be hit by accident
- <details> instead of hand-rolled disclosure: the keyboard works without a line of JS
- the ellipsis on long names and e-mails — the column is narrow and wrapping breaks the card

## 6. You may change
- the sections through items and the current one through activeLabel
- the name through userName and the second line through userMeta
- the account menu entries through menu
- the accent colour through accent

## 7. Rules
- The avatar is initials from the name: for a photo replace the block with an <img> and an alt.
- The profile menu does not close on an outside click: that is the price of having no client JS.
- The column's minimum height is set in CSS: without it the spacer does nothing.
- Do not lift the CSS variables into globals.css: the component has to stay a single file.

## 8. Verify
- it renders with no console errors;
- it looks like the preview on the VibeUI page you copied this from;
- if it does not, you changed something listed in section 5 — put it back.

For developers

npx shadcn@latest add https://vibeui.ru/r/sidebar-006.json
https://vibeui.ru/r/sidebar-006.json

Компонент самодостаточен: один файл, без зависимостей, палитра в локальных переменных --vibeui-sidebar-006-*. Серверный: хуков нет, меню профиля собрано на <details>, состояние держит браузер. Профиль прижат к низу распоркой flex:1, а не отступом: высота колонки бывает разной. Список меню позиционирован абсолютно и раскрывается вверх — внизу колонки места нет, а обрезанное меню бесполезно. Инициалы считаются из имени на рендере. Выход и настройки живут рядом с именем, поэтому их невозможно нажать по ошибке при выборе раздела.

Component source

The same file your agent installs. Here in case you would rather copy it by hand.

import type { ComponentPropsWithoutRef, CSSProperties } from "react"

export type Sidebar006Item = {
  label: string
  href?: string
}

export type Sidebar006Props = Omit<
  ComponentPropsWithoutRef<"div">,
  "children"
> & {
  items?: Sidebar006Item[]
  activeLabel?: string
  userName?: string
  userMeta?: string
  menu?: Sidebar006Item[]
  accent?: string
}

// Идея компонента: профиль стоит внизу колонки и раскрывается вверх. Это
// не украшение: выход из аккаунта и настройки живут рядом с именем, а не в
// общем списке разделов, поэтому их невозможно нажать по ошибке при выборе
// раздела. Меню профиля собрано на <details> — состояние держит браузер,
// клиентского JS в компоненте нет вообще.
const STYLES = `
:where([data-vibeui-block="sidebar-006"]){
--vibeui-sidebar-006-bg:oklch(1 0 0);
--vibeui-sidebar-006-fg:oklch(0.25 0.016 265);
--vibeui-sidebar-006-muted:oklch(0.55 0.014 265);
--vibeui-sidebar-006-border:oklch(0.91 0.006 265);
--vibeui-sidebar-006-accent:oklch(0.55 0.16 200);
--vibeui-sidebar-006-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="sidebar-006"]{
display:flex;flex-direction:column;gap:0.75rem;
width:100%;max-width:15rem;min-height:17rem;box-sizing:border-box;padding:0.625rem;
background:var(--vibeui-sidebar-006-bg);color:var(--vibeui-sidebar-006-fg);
border:1px solid var(--vibeui-sidebar-006-border);border-radius:0.875rem;
font-family:var(--vibeui-sidebar-006-font);
}
[data-vibeui-block="sidebar-006"] [data-part="nav"] ul{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:0.125rem}
[data-vibeui-block="sidebar-006"] [data-part="nav"] a{
display:block;padding:0.4375rem 0.5rem;border-radius:0.5rem;
color:var(--vibeui-sidebar-006-muted);text-decoration:none;font-size:0.875rem;line-height:1.3;
}
[data-vibeui-block="sidebar-006"] [data-part="nav"] a:hover{background:oklch(0.55 0.02 265 / 7%);color:var(--vibeui-sidebar-006-fg)}
[data-vibeui-block="sidebar-006"] [data-part="nav"] a:focus-visible{outline:2px solid var(--vibeui-sidebar-006-accent);outline-offset:-2px}
[data-vibeui-block="sidebar-006"] [data-part="nav"] a[aria-current="page"]{
background:color-mix(in oklab,var(--vibeui-sidebar-006-accent) 14%,transparent);
color:var(--vibeui-sidebar-006-fg);font-weight:600;
}
/* Профиль прижат к низу распоркой, а не отступом: высота колонки бывает разной. */
[data-vibeui-block="sidebar-006"] [data-part="spacer"]{flex:1}
[data-vibeui-block="sidebar-006"] [data-part="profile"]{position:relative;border-top:1px solid var(--vibeui-sidebar-006-border);padding-top:0.5rem}
[data-vibeui-block="sidebar-006"] summary{
display:flex;align-items:center;gap:0.5rem;cursor:pointer;list-style:none;
padding:0.375rem 0.5rem;border-radius:0.5rem;
}
[data-vibeui-block="sidebar-006"] summary::-webkit-details-marker{display:none}
[data-vibeui-block="sidebar-006"] summary:hover{background:oklch(0.55 0.02 265 / 7%)}
[data-vibeui-block="sidebar-006"] summary:focus-visible{outline:2px solid var(--vibeui-sidebar-006-accent);outline-offset:-2px}
[data-vibeui-block="sidebar-006"] [data-part="avatar"]{
display:flex;align-items:center;justify-content:center;flex:none;
width:1.75rem;height:1.75rem;border-radius:9999px;
background:color-mix(in oklab,var(--vibeui-sidebar-006-accent) 18%,transparent);
color:var(--vibeui-sidebar-006-fg);font-size:0.6875rem;font-weight:700;
}
[data-vibeui-block="sidebar-006"] [data-part="who"]{display:flex;flex-direction:column;min-width:0}
[data-vibeui-block="sidebar-006"] [data-part="name"]{font-size:0.8125rem;font-weight:650;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
[data-vibeui-block="sidebar-006"] [data-part="meta"]{font-size:0.6875rem;color:var(--vibeui-sidebar-006-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
[data-vibeui-block="sidebar-006"] [data-part="dots"]{
margin-left:auto;flex:none;color:var(--vibeui-sidebar-006-muted);font-size:0.875rem;line-height:1;
}
/* Меню раскрывается вверх: внизу колонки места нет, а обрезанное меню бесполезно. */
[data-vibeui-block="sidebar-006"] [data-part="menu"]{
position:absolute;left:0;right:0;bottom:calc(100% - 0.25rem);z-index:2;
margin:0;padding:0.25rem;list-style:none;
background:var(--vibeui-sidebar-006-bg);
border:1px solid var(--vibeui-sidebar-006-border);border-radius:0.625rem;
box-shadow:0 8px 24px oklch(0.2 0.02 265 / 14%);
}
[data-vibeui-block="sidebar-006"] [data-part="menu"] a{
display:block;padding:0.375rem 0.5rem;border-radius:0.375rem;
color:var(--vibeui-sidebar-006-fg);text-decoration:none;font-size:0.8125rem;
}
[data-vibeui-block="sidebar-006"] [data-part="menu"] a:hover{background:oklch(0.55 0.02 265 / 8%)}
[data-vibeui-block="sidebar-006"] [data-part="menu"] a:focus-visible{outline:2px solid var(--vibeui-sidebar-006-accent);outline-offset:-2px}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="sidebar-006"] *{animation:none!important;transition:none!important}}
`

const DEFAULT_ITEMS: Sidebar006Item[] = [
  { label: "Лента", href: "#" },
  { label: "Проекты", href: "#" },
  { label: "Задачи", href: "#" },
  { label: "Файлы", href: "#" },
]

const DEFAULT_MENU: Sidebar006Item[] = [
  { label: "Настройки профиля", href: "#" },
  { label: "Сменить тему", href: "#" },
  { label: "Выйти", href: "#" },
]

/**
 * Меню с карточкой профиля внизу и меню аккаунта, раскрывающимся вверх.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Sidebar006({
  items = DEFAULT_ITEMS,
  activeLabel = "Проекты",
  userName = "Ольга Дорн",
  userMeta = "olga@studio.ru",
  menu = DEFAULT_MENU,
  accent,
  className,
  style,
  ...props
}: Sidebar006Props) {
  const palette = {
    ...(accent ? { "--vibeui-sidebar-006-accent": accent } : null),
    ...style,
  } as CSSProperties

  const short = userName
    .split(" ")
    .slice(0, 2)
    .map((part) => part.charAt(0).toUpperCase())
    .join("")

  return (
    <>
      <style href="vibeui-sidebar-006" precedence="medium">
        {STYLES}
      </style>
      <div
        {...props}
        data-vibeui-block="sidebar-006"
        className={className}
        style={palette}
      >
        <nav data-part="nav" aria-label="Разделы">
          <ul>
            {items.map((item) => (
              <li key={item.label}>
                <a
                  href={item.href}
                  aria-current={item.label === activeLabel ? "page" : undefined}
                >
                  {item.label}
                </a>
              </li>
            ))}
          </ul>
        </nav>
        <div data-part="spacer" />
        <div data-part="profile">
          <details>
            <summary aria-label={`Меню профиля: ${userName}`}>
              <span data-part="avatar" aria-hidden="true">
                {short}
              </span>
              <span data-part="who">
                <span data-part="name">{userName}</span>
                <span data-part="meta">{userMeta}</span>
              </span>
              <span data-part="dots" aria-hidden="true">
                •••
              </span>
            </summary>
            <ul data-part="menu">
              {menu.map((entry) => (
                <li key={entry.label}>
                  <a href={entry.href}>{entry.label}</a>
                </li>
              ))}
            </ul>
          </details>
        </div>
      </div>
    </>
  )
}