Hero
Aurora Hero
Тёмный SaaS hero с ambient-свечением, grid-подложкой, badge и парой CTA. Самодостаточен: не зависит от темы и стиля проекта.
- dark
- saas
- gradient
- animated
- centered
- landing
Превью
1440pxHost theme
Использовать с AI
- 1. Скопируйте ссылку.
- 2. Напишите агенту своими словами и вставьте её в предложение.
- 3. Агент откроет ссылку и поставит компонент из registry.
Показать полную инструкцию
То, что лежит по ссылке в развёрнутом виде. Нужна, если агент не может открыть ссылку — тогда вставьте этот текст целиком.
# Установка и размещение «hero-001» (Aurora Hero) из VibeUI ## 1. Сначала установка — не пропускай, не пересоздавай Команда установки недоступна: URL реестра VibeUI не сконфигурирован. Ставится в: components/vibeui/hero-001.tsx (точный путь следует алиасам components.json этого проекта). npm-зависимости: нет. Registry-зависимости: нет. Ставь из реестра. Не пересоздавай по описанию, не подменяй похожим компонентом из другой библиотеки и не переписывай под стиль проекта. ## 2. Что это Тёмный SaaS hero с ambient-свечением, grid-подложкой, badge и парой CTA. Самодостаточен: не зависит от темы и стиля проекта. Тёмный центрированный SaaS-hero: badge, крупный заголовок в две части, подзаголовок, две CTA-ссылки, строка фактов. Ambient-свечение и grid-подложка на чистом CSS, ноль зависимостей, один файл. ## 3. Как использовать Это целая секция страницы. Вставляй её одним куском — не вынимай из неё фрагменты и не собирай заново из мелких компонентов. Имя экспорта и пропсы смотри в установленном файле. ## 4. Куда поставить Это полноширинная секция страницы. Поставь её прямым потомком разметки страницы, в том порядке, который назвал пользователь. Не вкладывай внутрь карточки, сайдбара, модального окна или другого узкого контейнера: секция меряет собственную ширину и разложится неправильно. Куда поставить: ___ (Эту строку заполняет пользователь. Если она осталась пустой — спроси, а не угадывай.) ## 5. Сохранить как установлено - тёмный фон блока независимо от темы проекта - локальную палитру --vibeui-hero-* — не заменять её на токены темы проекта (bg-background, text-foreground, primary и т.п.) - ambient glow и grid-подложку - clamp-типографику заголовка и tracking в единицах cqi - entrance-анимации и стагер через animation-delay - container-type: inline-size на корне блока и @container-правила в его <style> — блок меряет собственную ширину, а не ширину окна; не переписывай их на viewport-медиазапросы - responsive-поведение CTA: столбик на узком блоке, строка от 40rem ширины блока - семантику: один h1, CTA как <a href>, декоративные слои aria-hidden - блок <style> внутри компонента — в нём keyframes и prefers-reduced-motion ## 6. Можно менять - тексты badge, title, titleAccent, description, CTA и highlights - href у CTA - акцентный цвет — проп accent (и accentForeground для контраста текста на кнопке) - внешние отступы через className ## 7. Правила - Сохраняй семантику ссылок. Для внутренней навигации можно использовать компонент роутера проекта, если сохраняются визуальные стили, доступность и поведение href. - Не добавляй motion-библиотеки: анимации уже на CSS. - Не выноси CSS-переменные в globals.css: блок должен оставаться одним файлом. ## 8. Проверка - рендерится без ошибок в консоли; - выглядит так же, как превью на странице VibeUI, откуда взята ссылка; - если нет — ты изменил что-то из раздела 5, верни как было.
Для разработчика
Команда установки недоступна: переменная окруженияREGISTRY_BASE_URL
Блок самодостаточен: один файл, без зависимостей, всегда тёмный независимо от темы проекта. Цвета живут в локальных переменных --vibeui-hero-*; акцент меняется пропом accent. Keyframes и prefers-reduced-motion лежат внутри компонента, globals.css править не нужно.
Исходник компонента
Тот же файл, который поставит агент. Нужен, если вы предпочитаете скопировать код руками.
import type { CSSProperties } from "react"
type Hero001Action = {
label: string
href: string
}
export type Hero001Props = {
badge?: string
title?: string
titleAccent?: string
description?: string
primaryAction?: Hero001Action
secondaryAction?: Hero001Action
highlights?: string[]
accent?: string
accentForeground?: string
className?: string
}
// Блок везде выглядит одинаково, поэтому палитра, шрифт и keyframes живут здесь,
// а не в globals.css проекта. Селектор в :where() — нулевая специфичность,
// так что любой класс или inline style пользователя переопределяет значение.
//
// container-type делает блок собственным query-контейнером: раскладка и размер
// шрифта считаются от ширины блока, а не от ширины окна. Поэтому блок выглядит
// одинаково и на странице, и в масштабированной миниатюре каталога, где он
// рендерится в 1280px внутри узкого окна. Правила раскладки живут здесь, а не
// в Tailwind-вариантах, чтобы блок не зависел от версии Tailwind в чужом
// проекте; специфичность (0,2,0) — выше утилит, поэтому они переопределяются.
const STYLES = `
:where([data-vibeui-block="hero-001"]){
--vibeui-hero-bg:oklch(0.16 0.014 266);
--vibeui-hero-fg:oklch(0.98 0.003 266);
--vibeui-hero-muted:oklch(0.75 0.019 266);
--vibeui-hero-border:oklch(1 0 0 / 14%);
--vibeui-hero-accent:oklch(0.72 0.163 264);
--vibeui-hero-accent-fg:oklch(0.17 0.02 266);
--vibeui-hero-ring:color-mix(in oklab, var(--vibeui-hero-accent) 75%, transparent);
--vibeui-hero-glow:color-mix(in oklab, var(--vibeui-hero-accent) 38%, transparent);
--vibeui-hero-grid:oklch(1 0 0 / 6%);
--vibeui-hero-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="hero-001"]{container-type:inline-size}
@container (min-width:40rem){
[data-vibeui-block="hero-001"] [data-part="frame"]{padding-left:2.5rem;padding-right:2.5rem}
[data-vibeui-block="hero-001"] [data-part="actions"]{width:auto;flex-direction:row}
}
@container (min-width:48rem){
[data-vibeui-block="hero-001"] [data-part="frame"]{min-height:680px;padding-top:8rem;padding-bottom:8rem}
}
@keyframes vibeui-hero-001-fade-up{from{opacity:0;transform:translate3d(0,14px,0)}to{opacity:1;transform:none}}
@keyframes vibeui-hero-001-glow{0%,100%{opacity:.68}50%{opacity:1}}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="hero-001"] *{animation:none!important;transition:none!important}}
`
const ENTER =
"animate-[vibeui-hero-001-fade-up_0.6s_cubic-bezier(0.16,1,0.3,1)_both]"
const ACTION_BASE =
"group inline-flex h-12 items-center justify-center gap-2 rounded-full px-7 text-[0.95rem] font-medium focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--vibeui-hero-ring)]"
function cx(...classes: (string | false | undefined)[]) {
return classes.filter(Boolean).join(" ")
}
export function Hero001({
badge = "Now in public beta",
title = "Ship your product",
titleAccent = "twice as fast",
description = "Everything your team needs to design, build and launch — in one workspace that stays out of the way.",
primaryAction = { label: "Start building", href: "#" },
secondaryAction = { label: "Book a demo", href: "#" },
highlights = ["No credit card", "14-day trial", "SOC 2 compliant"],
accent,
accentForeground,
className,
}: Hero001Props) {
const style = {
...(accent ? { "--vibeui-hero-accent": accent } : {}),
...(accentForeground
? { "--vibeui-hero-accent-fg": accentForeground }
: {}),
} as CSSProperties
return (
<section
data-vibeui-block="hero-001"
style={style}
className={cx(
"relative isolate overflow-hidden bg-[var(--vibeui-hero-bg)] font-[family-name:var(--vibeui-hero-font)] text-[var(--vibeui-hero-fg)] antialiased",
className,
)}
>
<style href="vibeui-hero-001" precedence="medium">
{STYLES}
</style>
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 -top-[35%] h-[85%] animate-[vibeui-hero-001-glow_12s_ease-in-out_infinite]"
style={{
background:
"radial-gradient(50% 50% at 50% 55%, var(--vibeui-hero-glow), transparent 72%)",
}}
/>
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0"
style={{
backgroundImage:
"linear-gradient(to right, var(--vibeui-hero-grid) 1px, transparent 1px), linear-gradient(to bottom, var(--vibeui-hero-grid) 1px, transparent 1px)",
backgroundSize: "64px 64px",
maskImage:
"radial-gradient(60% 55% at 50% 45%, black, transparent 100%)",
WebkitMaskImage:
"radial-gradient(60% 55% at 50% 45%, black, transparent 100%)",
}}
/>
{/* Отступы и высота живут на внутреннем слое: container-запросы читают
ширину секции, а сама секция своим контейнером быть не может. */}
<div
data-part="frame"
className="relative flex min-h-[560px] items-center px-6 py-24"
>
<div className="mx-auto flex w-full max-w-3xl flex-col items-center text-center">
{badge ? (
<span
className={cx(
ENTER,
"inline-flex items-center gap-2 rounded-full border border-[var(--vibeui-hero-border)] px-3.5 py-1.5 text-xs font-medium tracking-wide text-[var(--vibeui-hero-muted)]",
)}
>
<span
aria-hidden="true"
className="size-1.5 rounded-full bg-[var(--vibeui-hero-accent)]"
/>
{badge}
</span>
) : null}
<h1
className={cx(
ENTER,
"mt-7 text-[clamp(2.25rem,6cqi,4.75rem)] leading-[1.04] font-semibold tracking-tight text-balance break-words [animation-delay:60ms]",
)}
>
{title}
{titleAccent ? (
<>
{" "}
<span className="text-[var(--vibeui-hero-muted)]">
{titleAccent}
</span>
</>
) : null}
</h1>
{description ? (
<p
className={cx(
ENTER,
"mt-6 max-w-xl text-[clamp(1rem,1.6cqi,1.15rem)] leading-relaxed text-pretty break-words text-[var(--vibeui-hero-muted)] [animation-delay:120ms]",
)}
>
{description}
</p>
) : null}
<div
data-part="actions"
className={cx(
ENTER,
"mt-10 flex w-full max-w-full flex-col gap-3 [animation-delay:180ms]",
)}
>
{primaryAction ? (
<a
href={primaryAction.href}
className={cx(
ACTION_BASE,
"bg-[var(--vibeui-hero-accent)] text-[var(--vibeui-hero-accent-fg)] transition-[filter,transform] duration-150 hover:-translate-y-px hover:brightness-110",
)}
style={{
boxShadow:
"0 14px 40px -14px color-mix(in oklab, var(--vibeui-hero-accent) 70%, transparent)",
}}
>
{primaryAction.label}
</a>
) : null}
{secondaryAction ? (
<a
href={secondaryAction.href}
className={cx(
ACTION_BASE,
"border border-[var(--vibeui-hero-border)] text-[var(--vibeui-hero-fg)] transition-colors duration-150 hover:bg-[color-mix(in_oklab,var(--vibeui-hero-fg)_10%,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>
{highlights.length > 0 ? (
<ul
className={cx(
ENTER,
"mt-10 flex flex-wrap items-center justify-center gap-x-3 gap-y-2 text-sm text-[var(--vibeui-hero-muted)] [animation-delay:240ms]",
)}
>
{highlights.map((highlight, index) => (
<li key={highlight} className="flex items-center gap-3">
{index > 0 ? (
<span
aria-hidden="true"
className="size-1 rounded-full bg-[var(--vibeui-hero-border)]"
/>
) : null}
{highlight}
</li>
))}
</ul>
) : null}
</div>
</div>
</section>
)
}