Buttons

Full Width Button

A full-width column button with two layouts: a centred label for short strings, and a split pair of label-with-caveat and arrow.

  • button
  • full-width
  • form
  • cta

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/button-040?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 "button-040" (Full Width Button) 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/button-040.json

Registry item: https://vibeui.ru/r/button-040.json
Installs to: components/vibeui/button-040.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 full-width column button with two layouts: a centred label for short strings, and a split pair of label-with-caveat and arrow.

A full-width button with something to put in the middle: a second line carrying the caveat sits under the label, and an arrow sits on the right. For short labels the layout switches to centred and the arrow leaves the markup. Zero dependencies, one file.

## 3. How to use it
import { Button040 } from "@/components/vibeui/button-040"

<Button040 align="between" hint="Free for 14 days, no card needed" onClick={subscribe}>
  Start subscription
</Button040>

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
- width:100% with a max-width cap: the width has to come from the column, not the window
- the second hint line at a smaller size and lowered opacity — it qualifies the label rather than competing with it
- tabular-nums on the second line: prices and deadlines must not shift the width
- the two layouts as different cases rather than decoration: the centred one has no arrow, and that is deliberate
- the arrow sliding forward and the slight squeeze on :active — a wide button needs that feedback most
- the 3.25rem minimum height: a short full-width button reads as a bar, not as an action

## 6. You may change
- the label and the second line through the hint prop
- the layout through the align prop (center or between)
- the accent colour through the accent prop
- the onClick handler and the rest of the native button props
- the maximum width through className

## 7. Rules
- Do not drop the max-width: on a wide screen a button spanning the whole container reads as a banner.
- Do not put a critical condition entirely into the second line — it is set small and at lowered contrast.
- Always keep the arrow in the between layout: without it the right edge goes empty.
- 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/button-040.json
https://vibeui.ru/r/button-040.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-button-040-*. Серверный компонент, корень — сам <button> с width:100% и max-width, поэтому ширину задаёт родитель, а не вьюпорт. Раскладка переключается data-атрибутом align: center центрирует содержимое, between разносит текстовый блок и стрелку. Вторая строка hint набрана tabular-nums — в ней обычно цена или срок.

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 Button040Props = ComponentPropsWithoutRef<"button"> & {
  /** Вторая строка под подписью: условие, срок, цена. */
  hint?: string
  /** Раскладка: подпись по центру или подпись слева, стрелка справа. */
  align?: "center" | "between"
  accent?: string
}

// Идея компонента: кнопка во всю ширину колонки. Проблема таких кнопок —
// пустота посередине, поэтому здесь две раскладки: центрированная для
// коротких подписей и разнесённая, где подпись с уточнением прижата влево,
// а стрелка — вправо. Ширину задаёт родитель, кнопка её только занимает.
const STYLES = `
:where([data-vibeui-block="button-040"]){
--vibeui-button-040-accent:oklch(0.48 0.15 258);
--vibeui-button-040-fg:oklch(0.99 0.01 258);
--vibeui-button-040-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="button-040"]{
appearance:none;border:0;cursor:pointer;box-sizing:border-box;
display:flex;align-items:center;gap:0.75rem;
width:100%;max-width:26rem;min-height:3.25rem;
padding:0.625rem 1.125rem;border-radius:0.875rem;
background:var(--vibeui-button-040-accent);color:var(--vibeui-button-040-fg);
font-family:var(--vibeui-button-040-font);font-size:0.9375rem;font-weight:650;line-height:1.25;
text-align:left;
transition:filter .16s ease,transform .12s ease;
}
[data-vibeui-block="button-040"][data-align="center"]{justify-content:center;text-align:center}
[data-vibeui-block="button-040"][data-align="between"]{justify-content:space-between}
[data-vibeui-block="button-040"]:hover:not(:disabled){filter:brightness(1.07)}
[data-vibeui-block="button-040"]:active:not(:disabled){transform:scale(.995)}
[data-vibeui-block="button-040"]:focus-visible{outline:2px solid var(--vibeui-button-040-accent);outline-offset:3px}
[data-vibeui-block="button-040"]:disabled{cursor:not-allowed;opacity:.55}
[data-vibeui-block="button-040"] [data-part="text"]{display:flex;flex-direction:column;gap:0.1875rem;min-width:0}
[data-vibeui-block="button-040"] [data-part="hint"]{
font-size:0.75rem;font-weight:500;opacity:.78;
font-variant-numeric:tabular-nums;
}
[data-vibeui-block="button-040"] [data-part="arrow"]{position:relative;flex:none;width:1rem;height:1rem}
[data-vibeui-block="button-040"] [data-part="arrow"]::before{
content:"";position:absolute;left:0;top:50%;width:0.875rem;height:1.75px;
margin-top:-0.875px;background:currentColor;border-radius:1px;
}
[data-vibeui-block="button-040"] [data-part="arrow"]::after{
content:"";position:absolute;right:0.125rem;top:50%;width:0.4375rem;height:0.4375rem;
box-sizing:border-box;border:1.75px solid currentColor;border-left:0;border-bottom:0;
transform:translateY(-50%) rotate(45deg);
}
[data-vibeui-block="button-040"] [data-part="arrow"]{transition:transform .18s cubic-bezier(0.16,1,0.3,1)}
[data-vibeui-block="button-040"]:hover:not(:disabled) [data-part="arrow"]{transform:translateX(3px)}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="button-040"] *{animation:none!important;transition:none!important}}
`

/**
 * Кнопка во всю ширину колонки с двумя раскладками и второй строкой.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Button040({
  hint = "Бесплатно 14 дней, карта не нужна",
  align = "between",
  accent,
  type = "button",
  className,
  style,
  children = "Оформить подписку",
  ...props
}: Button040Props) {
  const palette = {
    ...(accent ? { "--vibeui-button-040-accent": accent } : null),
    ...style,
  } as CSSProperties

  return (
    <>
      <style href="vibeui-button-040" precedence="medium">
        {STYLES}
      </style>
      <button
        {...props}
        type={type}
        data-vibeui-block="button-040"
        data-align={align}
        className={className}
        style={palette}
      >
        <span data-part="text">
          <span data-part="label">{children}</span>
          {hint ? <span data-part="hint">{hint}</span> : null}
        </span>
        {align === "between" ? (
          <span data-part="arrow" aria-hidden="true" />
        ) : null}
      </button>
    </>
  )
}