Hero
Mosaic Hero
Тёплый светлый hero, собранный мозаикой из пяти плиток: offer-плитка с заголовком и CTA, метрика, залитая акцентом плитка-утверждение, короткий чеклист и декоративный паттерн. Самодостаточен: не зависит от темы и стиля проекта.
- light
- warm
- bento
- editorial
- landing
- startup
Превью
Использовать с AI
- 1. Скопируйте ссылку.
- 2. Напишите агенту своими словами и вставьте её в предложение.
- 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>
)
}