Aspect Ratio

Video Frame

A 16:9 video thumbnail with a play button: a light frame instead of a heavy player before the click.

  • aspect ratio
  • video
  • thumbnail
  • performance

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/aspect-002?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 "aspect-002" (Video Frame) 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/aspect-002.json

Registry item: https://vibeui.ru/r/aspect-002.json
Installs to: components/vibeui/aspect-002.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 16:9 video thumbnail with a play button: a light frame instead of a heavy player before the click.

A 16:9 video preview: a round play button in the centre, the duration in the corner and the title over a gradient. The whole frame is a link, and the player loads only after the click. Zero dependencies, one file, its own palette, no client JS.

## 3. How to use it
import { Aspect002 } from "@/components/vibeui/aspect-002"

<Aspect002
  title="Build a landing page in an evening"
  duration="12:04"
  href="/video/landing"
/>

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-aspect-002-* palette — do not swap it for your theme tokens
- not loading the player before the click: an embedded iframe costs hundreds of kilobytes on every list page
- the triangle built from borders instead of an icon: no library for a single glyph
- the gradient under the title — without it the text disappears on a light frame
- the aria-label on the link carrying the video title: a link with no text says nothing
- the 16:9 ratio: anything else means cropping the video

## 6. You may change
- title and duration — the caption and length
- href — the page or player address
- the accent through the accent prop — the play button colour
- the thumbnail: put an <img> inside and it sits under the button

## 7. Rules
- The component plays nothing: on click you swap in a player or navigate.
- The duration is not computed — pass it with the video data.
- For vertical clips use aspect-004: 16:9 crops them to a strip.
- 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/aspect-002.json
https://vibeui.ru/r/aspect-002.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-aspect-002-*. Кадр держит 16:9 и по умолчанию рендерится ссылкой: iframe плеера весит сотни килобайт и подключается уже по клику. Треугольник воспроизведения нарисован границами, длительность лежит плашкой в углу, подпись — поверх градиента снизу.

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 Aspect002Props = Omit<
  ComponentPropsWithoutRef<"div">,
  "title" | "children"
> & {
  title?: string
  /** Длительность ролика: «12:04». Показывается плашкой в углу. */
  duration?: string
  /** Ссылка на видео. Кадр становится ссылкой целиком. */
  href?: string
  accent?: string
}

// Идея компонента: превью видео 16:9 с кнопкой воспроизведения по центру.
// Сам ролик не грузится: тяжёлый iframe плеера подключают по клику, а до
// клика на его месте стоит лёгкий кадр. Треугольник нарисован границами —
// иконочная библиотека ради одного знака не нужна.
const STYLES = `
:where([data-vibeui-block="aspect-002"]){
--vibeui-aspect-002-bg:oklch(0.28 0.02 265);
--vibeui-aspect-002-fg:oklch(0.98 0.003 265);
--vibeui-aspect-002-accent:oklch(0.62 0.2 25);
--vibeui-aspect-002-radius:0.875rem;
--vibeui-aspect-002-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="aspect-002"]{
position:relative;display:block;width:100%;box-sizing:border-box;overflow:hidden;
aspect-ratio:16 / 9;border-radius:var(--vibeui-aspect-002-radius);
background:
radial-gradient(120% 100% at 20% 0%,oklch(0.42 0.06 265),transparent 60%),
var(--vibeui-aspect-002-bg);
color:var(--vibeui-aspect-002-fg);font-family:var(--vibeui-aspect-002-font);
text-decoration:none;
}
[data-vibeui-block="aspect-002"] > img,
[data-vibeui-block="aspect-002"] > video,
[data-vibeui-block="aspect-002"] > iframe{
position:absolute;inset:0;width:100%;height:100%;border:0;object-fit:cover;
}
/* Кнопка воспроизведения: круг с треугольником из границ. */
[data-vibeui-block="aspect-002"] [data-part="play"]{
position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
display:flex;align-items:center;justify-content:center;
width:3.5rem;height:3.5rem;border-radius:9999px;
background:var(--vibeui-aspect-002-accent);
box-shadow:0 10px 30px -12px oklch(0.15 0.02 265 / 70%);
transition:transform .18s cubic-bezier(.32,.72,0,1);
}
[data-vibeui-block="aspect-002"]:hover [data-part="play"]{transform:translate(-50%,-50%) scale(1.06)}
[data-vibeui-block="aspect-002"] [data-part="play"]::before{
content:"";margin-left:0.25rem;
border-style:solid;border-width:0.5rem 0 0.5rem 0.8125rem;
border-color:transparent transparent transparent var(--vibeui-aspect-002-fg);
}
[data-vibeui-block="aspect-002"] [data-part="title"]{
position:absolute;left:0;right:0;bottom:0;padding:1.75rem 1rem 0.875rem;
font-size:0.875rem;font-weight:600;line-height:1.35;
background:linear-gradient(to top,oklch(0.15 0.02 265 / 75%),transparent);
}
[data-vibeui-block="aspect-002"] [data-part="duration"]{
position:absolute;right:0.625rem;top:0.625rem;
padding:0.125rem 0.375rem;border-radius:0.3125rem;
background:oklch(0.15 0.02 265 / 70%);
font-size:0.6875rem;font-weight:600;font-variant-numeric:tabular-nums;
}
[data-vibeui-block="aspect-002"]:focus-visible{outline:2px solid var(--vibeui-aspect-002-accent);outline-offset:3px}
@media (prefers-reduced-motion:reduce){
[data-vibeui-block="aspect-002"] *{animation:none!important;transition:none!important}
[data-vibeui-block="aspect-002"]:hover [data-part="play"]{transform:translate(-50%,-50%)}
}
`

/**
 * Превью видео 16:9: лёгкий кадр вместо тяжёлого плеера до клика.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Aspect002({
  title = "Как собрать лендинг за вечер",
  duration = "12:04",
  href = "#",
  accent,
  className,
  style,
  ...props
}: Aspect002Props) {
  const palette = {
    ...(accent ? { "--vibeui-aspect-002-accent": accent } : null),
    ...style,
  } as CSSProperties

  const content = (
    <>
      <span data-part="play" aria-hidden="true" />
      {duration ? <span data-part="duration">{duration}</span> : null}
      {title ? <span data-part="title">{title}</span> : null}
    </>
  )

  return (
    <>
      <style href="vibeui-aspect-002" precedence="medium">
        {STYLES}
      </style>
      {href ? (
        <a
          data-vibeui-block="aspect-002"
          href={href}
          className={className}
          style={palette}
          aria-label={title ? `Смотреть: ${title}` : "Смотреть видео"}
        >
          {content}
        </a>
      ) : (
        <div
          {...props}
          data-vibeui-block="aspect-002"
          className={className}
          style={palette}
        >
          {content}
        </div>
      )}
    </>
  )
}