Hero

Mosaic Hero

Тёплый светлый hero, собранный мозаикой из пяти плиток: offer-плитка с заголовком и CTA, метрика, залитая акцентом плитка-утверждение, короткий чеклист и декоративный паттерн. Самодостаточен: не зависит от темы и стиля проекта.

  • light
  • warm
  • bento
  • editorial
  • landing
  • startup

Превью

1440pxHost theme

Использовать с AI

  1. 1. Скопируйте ссылку.
  2. 2. Напишите агенту своими словами и вставьте её в предложение.
  3. 3. Агент откроет ссылку и поставит компонент из registry.
Показать полную инструкцию

То, что лежит по ссылке в развёрнутом виде. Нужна, если агент не может открыть ссылку — тогда вставьте этот текст целиком.

# Установка и размещение «hero-003» (Mosaic Hero) из VibeUI

## 1. Сначала установка — не пропускай, не пересоздавай
Команда установки недоступна: URL реестра VibeUI не сконфигурирован.

Ставится в: components/vibeui/hero-003.tsx (точный путь следует алиасам components.json этого проекта).
npm-зависимости: нет.
Registry-зависимости: нет.

Ставь из реестра. Не пересоздавай по описанию, не подменяй похожим
компонентом из другой библиотеки и не переписывай под стиль проекта.

## 2. Что это
Тёплый светлый hero, собранный мозаикой из пяти плиток: offer-плитка с заголовком и CTA, метрика, залитая акцентом плитка-утверждение, короткий чеклист и декоративный паттерн. Самодостаточен: не зависит от темы и стиля проекта.

Тёплый светлый hero с мозаичной раскладкой из пяти плиток фиксированных ролей: крупная offer-плитка с eyebrow, serif-заголовком в две части, описанием и парой CTA; плитка метрики; плитка, залитая терракотой, с коротким утверждением и стрелкой; плитка с чеклистом из трёх пунктов; декоративная плитка со штриховкой. Тёплая нейтральная база и холодный серо-голубой слой в границах и второстепенных плитках, терракота — только акцент. Плоские плитки с тугими радиусами, без теней. Плитки проявляются последовательно. Ноль зависимостей, один файл, раскладка и типографика считаются от ширины блока через container-запросы и cqi.

## 3. Как использовать
Это целая секция страницы. Вставляй её одним куском — не вынимай из
неё фрагменты и не собирай заново из мелких компонентов.
Имя экспорта и пропсы смотри в установленном файле.

## 4. Куда поставить
Это полноширинная секция страницы. Поставь её прямым потомком
разметки страницы, в том порядке, который назвал пользователь. Не
вкладывай внутрь карточки, сайдбара, модального окна или другого
узкого контейнера: секция меряет собственную ширину и разложится
неправильно.

Куда поставить: ___
(Эту строку заполняет пользователь. Если она осталась пустой — спроси,
а не угадывай.)

## 5. Сохранить как установлено
- мозаичную раскладку из пяти плиток и их роли: lead, stat, accent, panel, pattern — не превращай её в одну колонку на широком экране, в центрированный блок или в две равные колонки
- offer-плитку lead как центр композиции: заголовок и CTA живут только в ней, остальные плитки её поддерживают
- залитую терракотой плитку accent — это визуальный акцент композиции, а не декоративная подложка; не убирай заливку и не превращай её в обычную карточку
- чеклист коротким: максимум три пункта без описаний и без иллюстративных иконок — это hero, а не features-секция
- палитру --vibeui-hero-003-*: тёплая нейтральная база, холодный серо-голубой слой в границах и второстепенных плитках, терракота точечно. Не заменять на токены темы проекта (bg-background, text-foreground, primary, border и т.п.) и не превращать блок в сплошную бежевую заливку
- пару шрифтов: системный serif для заголовка и числа метрики, системный sans для остального; не подключай веб-шрифты и не переводи заголовок на sans
- тугие радиусы плиток и CTA и отсутствие теней — этим блок отличается от hero-002 с его мягкими скруглениями и тенями
- тёмную заливку primary CTA: акцентный цвет в этом блоке принадлежит плитке, а не кнопке
- container-type: inline-size на корне блока и @container-правила в его <style> — блок меряет собственную ширину, а не ширину окна; не переписывай их на viewport-медиазапросы
- типографику в единицах cqi, clamp и tracking
- последовательное проявление плиток и отсутствие бесконечных анимаций
- блок <style> внутри компонента — в нём палитра, container-правила, keyframes и prefers-reduced-motion
- семантику: один h1, CTA как <a href>, чеклист как <ul>/<li>, декоративная плитка и фоновый слой aria-hidden

## 6. Можно менять
- тексты eyebrow, title, titleAccent, description, CTA, accentNote и подписи метрики
- href у CTA
- value и label у метрики
- до трёх пунктов чеклиста
- акцентный цвет — проп accent (и accentForeground для контраста текста на акцентной плитке)
- внешние отступы через className

## 7. Правила
- Не добавляй motion-библиотеки: анимации уже на CSS.
- Не выноси CSS-переменные в globals.css: блок должен оставаться одним файлом.
- Не добавляй в мозаику новые плитки и не меняй их порядок: состав фиксирован, это часть композиции.
- Не расширяй чеклист до четвёртого пункта и не добавляй к пунктам описания — иначе блок превращается в features-секцию.
- Не вставляй в плитки картинки, скриншоты и логотипы — блок собран без внешних ассетов.
- Если меняешь accent, проверь контраст текста на залитой плитке: нужно не менее 4.5:1.
- Сохраняй семантику ссылок. Для внутренней навигации можно использовать компонент роутера проекта, если сохраняются визуальные стили, доступность и поведение href.

## 8. Проверка
- рендерится без ошибок в консоли;
- выглядит так же, как превью на странице VibeUI, откуда взята ссылка;
- если нет — ты изменил что-то из раздела 5, верни как было.

Для разработчика

Команда установки недоступна: переменная окруженияREGISTRY_BASE_URL

Блок самодостаточен: один файл, без зависимостей, всегда светлый независимо от темы проекта. Цвета живут в локальных переменных --vibeui-hero-003-*; акцент меняется пропом accent. Раскладка переключается container-запросами от ширины самого блока, поэтому мозаика видна и на странице, и в миниатюре каталога. Заголовок использует системный serif, интерфейсный текст — системный sans; веб-шрифты не подключаются.

Исходник компонента

Тот же файл, который поставит агент. Нужен, если вы предпочитаете скопировать код руками.

import type { CSSProperties } from "react"

type Hero003Action = {
  label: string
  href: string
}

type Hero003Stat = {
  value: string
  label: string
}

export type Hero003Props = {
  eyebrow?: string
  title?: string
  titleAccent?: string
  description?: string
  primaryAction?: Hero003Action
  secondaryAction?: Hero003Action
  stat?: Hero003Stat
  highlights?: string[]
  accentNote?: string
  accent?: string
  accentForeground?: string
  className?: string
}

// Весь CSS блока живёт здесь, а не в globals.css проекта: палитра, раскладка
// мозаики, декоративный слой и keyframes. Переменные объявлены в :where()
// с нулевой специфичностью, поэтому пользователь переопределяет их чем угодно.
//
// Палитра: тёплая нейтральная база (низкая цветность, не песочная заливка),
// глубокие espresso-нейтральные чернила, холодный серо-голубой слой в границах
// и во второстепенных плитках — он не даёт композиции слипнуться в одну тёплую
// массу. Терракота работает точечно: одна залитая плитка и акценты, не фон.
//
// container-type делает блок собственным query-контейнером: раскладка и размер
// шрифта считаются от ширины блока, а не от ширины окна. Поэтому мозаика видна
// и в миниатюре каталога, где блок рендерится в 1280px внутри узкого окна.
// Правила раскладки — плоским CSS, а не Tailwind-вариантами, чтобы блок не
// зависел от версии Tailwind в чужом проекте; специфичность (0,2,0) выше утилит.
const STYLES = `
:where([data-vibeui-block="hero-003"]){
--vibeui-hero-003-bg:oklch(0.968 0.004 70);
--vibeui-hero-003-card:oklch(0.995 0.002 70);
--vibeui-hero-003-card-alt:oklch(0.953 0.005 250);
--vibeui-hero-003-ink:oklch(0.21 0.012 55);
--vibeui-hero-003-muted:oklch(0.5 0.012 55);
--vibeui-hero-003-border:oklch(0.881 0.005 250);
--vibeui-hero-003-accent:oklch(0.5 0.145 42);
--vibeui-hero-003-accent-fg:oklch(0.99 0.004 70);
--vibeui-hero-003-ring:color-mix(in oklab, var(--vibeui-hero-003-accent) 70%, transparent);
--vibeui-hero-003-serif:ui-serif,Georgia,"Times New Roman",Times,serif;
--vibeui-hero-003-sans:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="hero-003"]{container-type:inline-size}
[data-vibeui-block="hero-003"] [data-part="hatch"]{position:absolute;inset:0;pointer-events:none;background-image:repeating-linear-gradient(135deg,var(--vibeui-hero-003-border) 0 1px,transparent 1px 11px);opacity:.32;-webkit-mask-image:radial-gradient(70% 60% at 50% 45%,transparent,black);mask-image:radial-gradient(70% 60% at 50% 45%,transparent,black)}
[data-vibeui-block="hero-003"] [data-part="pattern"]{background-image:repeating-linear-gradient(135deg,var(--vibeui-hero-003-border) 0 1px,transparent 1px 10px)}
[data-vibeui-block="hero-003"] [data-part="lead"]{grid-area:lead}
[data-vibeui-block="hero-003"] [data-part="stat"]{grid-area:stat}
[data-vibeui-block="hero-003"] [data-part="accent"]{grid-area:accent}
[data-vibeui-block="hero-003"] [data-part="panel"]{grid-area:panel}
[data-vibeui-block="hero-003"] [data-part="pattern"]{grid-area:pattern;display:none}
[data-vibeui-block="hero-003"] [data-part="grid"]{grid-template-columns:minmax(0,1fr);grid-template-areas:"lead" "stat" "panel" "accent"}
@container (min-width:40rem){
[data-vibeui-block="hero-003"] [data-part="frame"]{min-height:600px;padding-left:2rem;padding-right:2rem}
[data-vibeui-block="hero-003"] [data-part="actions"]{width:auto;flex-direction:row}
[data-vibeui-block="hero-003"] [data-part="grid"]{grid-template-columns:repeat(2,minmax(0,1fr));grid-template-areas:"lead lead" "stat accent" "panel panel"}
}
@container (min-width:64rem){
[data-vibeui-block="hero-003"] [data-part="frame"]{min-height:680px;padding-top:4rem;padding-bottom:4rem}
[data-vibeui-block="hero-003"] [data-part="grid"]{grid-template-columns:repeat(4,minmax(0,1fr));grid-template-areas:"lead lead lead stat" "accent panel panel pattern"}
[data-vibeui-block="hero-003"] [data-part="pattern"]{display:block}
}
@container (min-width:80rem){
[data-vibeui-block="hero-003"] [data-part="frame"]{min-height:720px;padding-left:4rem;padding-right:4rem}
[data-vibeui-block="hero-003"] [data-part="grid"]{max-width:1200px;margin-inline:auto;gap:1.25rem}
}
@keyframes vibeui-hero-003-tile-in{from{opacity:0;transform:translate3d(0,10px,0) scale(0.99)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="hero-003"] *{animation:none!important;transition:none!important}}
`

const TILE_IN =
  "animate-[vibeui-hero-003-tile-in_0.5s_cubic-bezier(0.16,1,0.3,1)_both]"

const TILE =
  "rounded-md border border-[var(--vibeui-hero-003-border)] bg-[var(--vibeui-hero-003-card)]"

const ACTION_BASE =
  "group inline-flex h-11 items-center justify-center gap-2 rounded-sm px-5 text-[0.9375rem] font-medium focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--vibeui-hero-003-ring)]"

function cx(...classes: (string | false | undefined)[]) {
  return classes.filter(Boolean).join(" ")
}

function CheckIcon() {
  return (
    <svg
      aria-hidden="true"
      viewBox="0 0 16 16"
      fill="none"
      className="mt-0.5 size-4 shrink-0 text-[var(--vibeui-hero-003-accent)]"
    >
      <path
        d="M3.5 8.5 6.5 11.5 12.5 5"
        stroke="currentColor"
        strokeWidth="1.75"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  )
}

export function Hero003({
  eyebrow = "Design system · v3",
  title = "Everything you ship",
  titleAccent = "deserves a shape",
  description = "A calm, opinionated foundation for teams that would rather design once and reuse forever.",
  primaryAction = { label: "Get started", href: "#" },
  secondaryAction = { label: "Read the docs", href: "#" },
  stat = { value: "12k+", label: "components shipped" },
  highlights = [
    "Accessible by default",
    "Zero runtime deps",
    "Works in any theme",
  ],
  accentNote = "Built to be copied, not configured",
  accent,
  accentForeground,
  className,
}: Hero003Props) {
  const style = {
    ...(accent ? { "--vibeui-hero-003-accent": accent } : {}),
    ...(accentForeground
      ? { "--vibeui-hero-003-accent-fg": accentForeground }
      : {}),
  } as CSSProperties

  return (
    <section
      data-vibeui-block="hero-003"
      style={style}
      className={cx(
        "relative isolate overflow-hidden bg-[var(--vibeui-hero-003-bg)] font-[family-name:var(--vibeui-hero-003-sans)] text-[var(--vibeui-hero-003-ink)] antialiased",
        className,
      )}
    >
      <style href="vibeui-hero-003" precedence="medium">
        {STYLES}
      </style>

      <div aria-hidden="true" data-part="hatch" />

      {/* Отступы и высота живут на внутреннем слое: container-запросы читают
          ширину секции, а сама секция своим контейнером быть не может. */}
      <div
        data-part="frame"
        className="relative flex min-h-[560px] items-center px-5 py-14"
      >
        <div data-part="grid" className="grid w-full gap-4">
          {/* Главная offer-плитка: центр композиции, остальные её поддерживают */}
          <div
            data-part="lead"
            className={cx(TILE, TILE_IN, "flex flex-col justify-center p-7")}
          >
            {eyebrow ? (
              <p className="text-[0.75rem] font-medium tracking-[0.08em] text-[var(--vibeui-hero-003-muted)] uppercase">
                {eyebrow}
              </p>
            ) : null}

            <h1 className="mt-5 font-[family-name:var(--vibeui-hero-003-serif)] text-[clamp(2.125rem,4.4cqi,3.5rem)] leading-[1.06] font-semibold tracking-[-0.01em] text-balance break-words">
              {title}
              {titleAccent ? (
                <>
                  {" "}
                  <span className="text-[var(--vibeui-hero-003-accent)]">
                    {titleAccent}
                  </span>
                </>
              ) : null}
            </h1>

            {description ? (
              <p className="mt-4 max-w-[30rem] text-[clamp(1rem,1.3cqi,1.0625rem)] leading-relaxed text-pretty break-words text-[var(--vibeui-hero-003-muted)]">
                {description}
              </p>
            ) : null}

            <div
              data-part="actions"
              className="mt-7 flex w-full max-w-full flex-col gap-3"
            >
              {primaryAction ? (
                <a
                  href={primaryAction.href}
                  className={cx(
                    ACTION_BASE,
                    "bg-[var(--vibeui-hero-003-ink)] text-[var(--vibeui-hero-003-bg)] transition-opacity duration-150 hover:opacity-90",
                  )}
                >
                  {primaryAction.label}
                </a>
              ) : null}

              {secondaryAction ? (
                <a
                  href={secondaryAction.href}
                  className={cx(
                    ACTION_BASE,
                    "border border-[var(--vibeui-hero-003-ink)] transition-colors duration-150 hover:bg-[color-mix(in_oklab,var(--vibeui-hero-003-ink)_8%,transparent)]",
                  )}
                >
                  {secondaryAction.label}
                  <svg
                    aria-hidden="true"
                    viewBox="0 0 16 16"
                    fill="none"
                    className="size-4 transition-transform duration-150 group-hover:translate-x-0.5"
                  >
                    <path
                      d="M3 8h9m0 0L8.5 4.5M12 8l-3.5 3.5"
                      stroke="currentColor"
                      strokeWidth="1.5"
                      strokeLinecap="round"
                      strokeLinejoin="round"
                    />
                  </svg>
                </a>
              ) : null}
            </div>
          </div>

          {stat ? (
            <div
              data-part="stat"
              className={cx(
                TILE_IN,
                "flex flex-col justify-end rounded-md border border-[var(--vibeui-hero-003-border)] bg-[var(--vibeui-hero-003-card-alt)] p-6 [animation-delay:70ms]",
              )}
            >
              <p className="font-[family-name:var(--vibeui-hero-003-serif)] text-[clamp(2rem,3.4cqi,2.75rem)] leading-none font-semibold tabular-nums">
                {stat.value}
              </p>
              <p className="mt-3 text-[0.8125rem] text-[var(--vibeui-hero-003-muted)]">
                {stat.label}
              </p>
            </div>
          ) : null}

          {accentNote ? (
            <div
              data-part="accent"
              className={cx(
                TILE_IN,
                "flex flex-col justify-between gap-6 rounded-md bg-[var(--vibeui-hero-003-accent)] p-6 text-[var(--vibeui-hero-003-accent-fg)] [animation-delay:140ms]",
              )}
            >
              <p className="text-[1rem] leading-snug font-medium text-balance">
                {accentNote}
              </p>
              <svg
                aria-hidden="true"
                viewBox="0 0 16 16"
                fill="none"
                className="size-5"
              >
                <path
                  d="M3 13 13 3m0 0H5.5M13 3v7.5"
                  stroke="currentColor"
                  strokeWidth="1.5"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                />
              </svg>
            </div>
          ) : null}

          {highlights.length > 0 ? (
            <ul
              data-part="panel"
              className={cx(
                TILE,
                TILE_IN,
                "flex flex-col justify-center gap-3 p-6 [animation-delay:210ms]",
              )}
            >
              {highlights.slice(0, 3).map((highlight) => (
                <li key={highlight} className="flex gap-3 text-[0.875rem]">
                  <CheckIcon />
                  {highlight}
                </li>
              ))}
            </ul>
          ) : null}

          <div
            aria-hidden="true"
            data-part="pattern"
            className={cx(
              TILE_IN,
              "rounded-md border border-[var(--vibeui-hero-003-border)] bg-[var(--vibeui-hero-003-card-alt)] [animation-delay:280ms]",
            )}
          />
        </div>
      </div>
    </section>
  )
}