Display

Markup State

A changelog where the expanded section is declared with the open attribute in the markup, so the server ships it already open.

  • collapsible
  • changelog
  • ssr
  • no-js

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/collapsible-009?lang=en

Release history

0.6.012 августаразвернутьсвернуть

Реестр отдаёт исходник компонента по /f/<name>.tsx — теперь агент забирает файл байт в байт.

0.5.229 июляразвернутьсвернуть

Каталог фильтруется по тегам, а карточки считают ширину от собственного контейнера.

0.5.014 июляразвернутьсвернуть

Инструкция Copy for AI собирается из metadata, а не пишется руками под каждый компонент.

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 "collapsible-009" (Markup State) 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/collapsible-009.json

Registry item: https://vibeui.ru/r/collapsible-009.json
Installs to: components/vibeui/collapsible-009.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 changelog where the expanded section is declared with the open attribute in the markup, so the server ships it already open.

A changelog on details where the initial state lives in markup: the open section arrives from the server and the toggle caption swaps in pure CSS. Zero dependencies, one file.

## 3. How to use it
import { Collapsible009 } from "@/components/vibeui/collapsible-009"

<Collapsible009 releases={[{ version: "0.6.0", date: "12 August", text: "The registry serves sources", open: true }]} />

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-collapsible-009-* palette — do not swap it for your theme tokens
- the open attribute as the state: moving it into useState brings back the pre-hydration flash
- the caption swap through details[open] and the data-when nodes — React is not needed for that
- the monospaced version number: versions get compared character by character
- the visible focus ring on summary: sections get tabbed through
- the light card surface — dark text is invisible on a dark showcase without it

## 6. You may change
- the heading through the title prop
- the releases array and its open flags
- the date format and toggle captions
- the accent through the accent prop

## 7. Rules
- State does not persist across navigations: persisting it needs an outer layer, which costs pre-hydration behaviour.
- Several sections may stay open at once — there is deliberately no mutual exclusion here.
- Both toggle captions live in the DOM: a screen reader could read the hidden one if display:none is removed.
- 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/collapsible-009.json
https://vibeui.ru/r/collapsible-009.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-collapsible-009-*. Серверный: хуков нет, флаг open каждого релиза уходит прямо в атрибут details. Это не «начальное значение стейта», а само состояние: до гидратации ничего не мигает, печать и поиск по странице видят содержимое. Подпись переключателя меняется без React — в разметке лежат два узла data-when, а показывает нужный селектор details[open].

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 Collapsible009Release = {
  version: string
  date: string
  text: string
  open?: boolean
}

export type Collapsible009Props = Omit<
  ComponentPropsWithoutRef<"section">,
  "children" | "title"
> & {
  title?: string
  releases?: Collapsible009Release[]
  accent?: string
}

// Идея компонента: список изменений, где стартовое состояние объявлено прямо
// в разметке атрибутом open. Это не «начальное значение стейта», а само
// состояние: сервер отдаёт нужный раздел уже раскрытым, до гидратации ничего
// не мигает, а печать и поиск по странице видят содержимое. Версия и дата
// стоят в одной строке заголовка слева, чтобы список читался колонкой.
const STYLES = `
:where([data-vibeui-block="collapsible-009"]){
--vibeui-collapsible-009-bg:oklch(1 0 0);
--vibeui-collapsible-009-fg:oklch(0.24 0.014 265);
--vibeui-collapsible-009-muted:oklch(0.56 0.014 265);
--vibeui-collapsible-009-border:oklch(0.9 0.006 265);
--vibeui-collapsible-009-accent:oklch(0.56 0.15 165);
--vibeui-collapsible-009-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
--vibeui-collapsible-009-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="collapsible-009"]{
display:block;box-sizing:border-box;width:100%;max-width:26rem;padding:0.875rem;
background:var(--vibeui-collapsible-009-bg);color:var(--vibeui-collapsible-009-fg);
border:1px solid var(--vibeui-collapsible-009-border);border-radius:1rem;
font-family:var(--vibeui-collapsible-009-font);
}
[data-vibeui-block="collapsible-009"] [data-part="title"]{
margin:0 0 0.625rem;font-size:0.75rem;font-weight:700;
letter-spacing:0.06em;text-transform:uppercase;color:var(--vibeui-collapsible-009-muted);
}
[data-vibeui-block="collapsible-009"] details{border-top:1px solid var(--vibeui-collapsible-009-border)}
[data-vibeui-block="collapsible-009"] summary{
display:flex;align-items:baseline;gap:0.625rem;
padding:0.625rem 0.125rem;cursor:pointer;list-style:none;
font-size:0.8125rem;
}
[data-vibeui-block="collapsible-009"] summary::-webkit-details-marker{display:none}
[data-vibeui-block="collapsible-009"] summary:focus-visible{outline:2px solid var(--vibeui-collapsible-009-accent);outline-offset:2px;border-radius:0.375rem}
[data-vibeui-block="collapsible-009"] [data-part="version"]{
flex:none;padding:0.0625rem 0.4375rem;border-radius:0.375rem;
background:color-mix(in oklab,var(--vibeui-collapsible-009-accent) 15%,transparent);
color:var(--vibeui-collapsible-009-accent);
font-family:var(--vibeui-collapsible-009-mono);font-size:0.6875rem;font-weight:700;
}
[data-vibeui-block="collapsible-009"] [data-part="date"]{font-weight:640}
[data-vibeui-block="collapsible-009"] [data-part="state"]{
margin-left:auto;font-size:0.6875rem;color:var(--vibeui-collapsible-009-muted);
}
/* Подпись переключателя меняется на CSS: два узла вместо React-условия. */
[data-vibeui-block="collapsible-009"] [data-part="state"] [data-when="open"]{display:none}
[data-vibeui-block="collapsible-009"] details[open] [data-part="state"] [data-when="open"]{display:inline}
[data-vibeui-block="collapsible-009"] details[open] [data-part="state"] [data-when="closed"]{display:none}
[data-vibeui-block="collapsible-009"] [data-part="body"]{
margin:0;padding:0 0.125rem 0.75rem;
font-size:0.8125rem;line-height:1.55;color:var(--vibeui-collapsible-009-muted);
}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="collapsible-009"] *{animation:none!important;transition:none!important}}
`

const DEFAULT_RELEASES: Collapsible009Release[] = [
  {
    version: "0.6.0",
    date: "12 августа",
    text: "Реестр отдаёт исходник компонента по /f/<name>.tsx — теперь агент забирает файл байт в байт.",
    open: true,
  },
  {
    version: "0.5.2",
    date: "29 июля",
    text: "Каталог фильтруется по тегам, а карточки считают ширину от собственного контейнера.",
  },
  {
    version: "0.5.0",
    date: "14 июля",
    text: "Инструкция Copy for AI собирается из metadata, а не пишется руками под каждый компонент.",
  },
]

/**
 * Список изменений, где раскрытый раздел объявлен атрибутом open прямо в
 * разметке. Один файл, ноль зависимостей, клиентского кода нет.
 */
export function Collapsible009({
  title = "История версий",
  releases = DEFAULT_RELEASES,
  accent,
  className,
  style,
  ...props
}: Collapsible009Props) {
  const palette = {
    ...(accent ? { "--vibeui-collapsible-009-accent": accent } : null),
    ...style,
  } as CSSProperties

  return (
    <>
      <style href="vibeui-collapsible-009" precedence="medium">
        {STYLES}
      </style>
      <section
        {...props}
        data-vibeui-block="collapsible-009"
        className={className}
        style={palette}
      >
        <h3 data-part="title">{title}</h3>
        {releases.map((release) => (
          <details key={release.version} open={release.open}>
            <summary>
              <span data-part="version">{release.version}</span>
              <span data-part="date">{release.date}</span>
              <span data-part="state">
                <span data-when="closed">развернуть</span>
                <span data-when="open">свернуть</span>
              </span>
            </summary>
            <p data-part="body">{release.text}</p>
          </details>
        ))}
      </section>
    </>
  )
}