Code Block

Code Skeleton

A loading skeleton for code: the bars repeat the shape of a listing — nesting indents, an empty line and a short closing brace.

  • skeleton
  • loading
  • code
  • placeholder

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/codeblock-014?lang=en

Loading code
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 "codeblock-014" (Code Skeleton) 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/codeblock-014.json

Registry item: https://vibeui.ru/r/codeblock-014.json
Installs to: components/vibeui/codeblock-014.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 loading skeleton for code: the bars repeat the shape of a listing — nesting indents, an empty line and a short closing brace.

A code block skeleton: a header and rows that repeat the shape of a listing, with a sweeping shine. Zero dependencies, deterministic layout, no client JS.

## 3. How to use it
import { Codeblock014 } from "@/components/vibeui/codeblock-014"

<Codeblock014 rows={6} label="Loading code" />

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-codeblock-014-* palette — do not swap it for your theme tokens
- the deterministic width table: Math.random drifts between server and client and breaks hydration
- the listing shape — indents and the empty line: even identical bars read as a paragraph, not as code
- role=status with aria-busy and the hidden caption: without them loading is never announced
- the prefers-reduced-motion rule: an endless shine is an irritant for some people
- the skeleton row height matching real code leading: otherwise the swap jolts the layout

## 6. You may change
- the number of rows through rows
- the screen reader caption through label
- the shape table in the constant inside the file
- the bar colour and the shine speed

## 7. Rules
- A skeleton must match the real block in height, otherwise the page jumps when it loads.
- The shape table is shorter than the row list and repeats: with a large rows the pattern becomes visible.
- The bars are hidden from screen readers — they get the caption, not a pile of empty elements.
- 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/codeblock-014.json
https://vibeui.ru/r/codeblock-014.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-codeblock-014-*. Серверный: хуков нет. Ширины и отступы строк берутся из константной таблицы формы и повторяются по кругу — случайные значения разъезжались бы между сервером и клиентом и ломали гидратацию. Блик бежит по background-position, а не по transform, поэтому не нужен дополнительный слой; высота строки задана переменной и совпадает с межстрочным интервалом настоящего листинга. У корня role=status и aria-busy, подпись спрятана визуально, но доступна читалке экрана.

Component source

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

import type { CSSProperties } from "react"

export type Codeblock014Props = {
  rows?: number
  label?: string
  className?: string
  style?: CSSProperties
}

// Идея компонента: скелетон именно кода, а не абзаца. Ширины и отступы строк
// повторяют форму листинга — вложенность, пустая строка, короткая закрывающая
// скобка, — поэтому подмена на настоящий код не дёргает раскладку.
const STYLES = `
:where([data-vibeui-block="codeblock-014"]){
--vibeui-codeblock-014-bg:oklch(0.21 0.012 265);
--vibeui-codeblock-014-head:oklch(0.25 0.014 265);
--vibeui-codeblock-014-border:oklch(1 0 0 / 12%);
--vibeui-codeblock-014-bar:oklch(1 0 0 / 11%);
--vibeui-codeblock-014-shine:oklch(1 0 0 / 22%);
--vibeui-codeblock-014-row:1.375rem;
}
[data-vibeui-block="codeblock-014"]{
display:block;width:100%;max-width:30rem;box-sizing:border-box;overflow:hidden;
border:1px solid var(--vibeui-codeblock-014-border);border-radius:0.75rem;
background:var(--vibeui-codeblock-014-bg);
}
[data-vibeui-block="codeblock-014"] [data-part="head"]{
display:flex;align-items:center;gap:0.5rem;
padding:0.5625rem 0.875rem;
background:var(--vibeui-codeblock-014-head);
border-bottom:1px solid var(--vibeui-codeblock-014-border);
}
[data-vibeui-block="codeblock-014"] [data-part="body"]{
padding:0.75rem 0.875rem;
}
[data-vibeui-block="codeblock-014"] [data-part="row"]{
display:flex;align-items:center;height:var(--vibeui-codeblock-014-row);
}
[data-vibeui-block="codeblock-014"] [data-part="bar"]{
display:block;height:0.5rem;border-radius:999px;
background:var(--vibeui-codeblock-014-bar);
background-image:linear-gradient(90deg,transparent 0%,var(--vibeui-codeblock-014-shine) 50%,transparent 100%);
background-size:220% 100%;background-repeat:no-repeat;
animation:vibeui-codeblock-014-sweep 1.5s ease-in-out infinite;
}
[data-vibeui-block="codeblock-014"] [data-part="head"] [data-part="bar"]{height:0.625rem}
[data-vibeui-block="codeblock-014"] [data-part="gap"]{display:block;height:0.5rem;width:0}
[data-vibeui-block="codeblock-014"] [data-part="label"]{
position:absolute;width:1px;height:1px;margin:-1px;padding:0;
border:0;clip-path:inset(50%);overflow:hidden;white-space:nowrap;
}
@keyframes vibeui-codeblock-014-sweep{
from{background-position:120% 0}
to{background-position:-120% 0}
}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="codeblock-014"] *{animation:none!important;transition:none!important}}
`

// Форма листинга: отступ вложенности и доля ширины строки. Значения заданы
// таблицей, а не случайным числом: случайное разъезжается между сервером и
// клиентом и портит гидратацию.
const SHAPE = [
  { indent: 0, width: 62 },
  { indent: 1, width: 78 },
  { indent: 1, width: 45 },
  { indent: 2, width: 70 },
  { indent: 1, width: 0 },
  { indent: 1, width: 54 },
  { indent: 0, width: 18 },
]

/** Скелетон загрузки блока кода: шапка и строки формы листинга. */
export function Codeblock014({
  rows = 6,
  label = "Загрузка кода",
  className,
  style,
}: Codeblock014Props) {
  const count = Math.max(1, Math.round(rows))

  return (
    <>
      <style href="vibeui-codeblock-014" precedence="medium">
        {STYLES}
      </style>
      <div
        data-vibeui-block="codeblock-014"
        className={className}
        style={style}
        role="status"
        aria-busy="true"
      >
        <span data-part="label">{label}</span>
        <div data-part="head" aria-hidden="true">
          <span data-part="bar" style={{ width: "35%" }} />
          <span data-part="bar" style={{ width: "12%" }} />
        </div>
        <div data-part="body" aria-hidden="true">
          {Array.from({ length: count }, (_, index) => {
            const shape = SHAPE[index % SHAPE.length]

            return (
              <div data-part="row" key={index}>
                {shape.width === 0 ? (
                  <span data-part="gap" />
                ) : (
                  <span
                    data-part="bar"
                    style={{
                      marginInlineStart: `${shape.indent * 1.25}rem`,
                      width: `${shape.width}%`,
                    }}
                  />
                )}
              </div>
            )
          })}
        </div>
      </div>
    </>
  )
}