Inputs

Currency Attach

Amount and currency in one border: the number is pushed right against the currency edge, so the pair reads as a single line.

  • inputgroup
  • currency
  • amount
  • select

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/inputgroup-004?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 "inputgroup-004" (Currency Attach) 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/inputgroup-004.json

Registry item: https://vibeui.ru/r/inputgroup-004.json
Installs to: components/vibeui/inputgroup-004.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
Amount and currency in one border: the number is pushed right against the currency edge, so the pair reads as a single line.

A server-rendered amount-and-currency group: one border on the group, the inner line drawn with a shadow, the number right against it. Zero dependencies, one file.

## 3. How to use it
import { Inputgroup004 } from "@/components/vibeui/inputgroup-004"

<Inputgroup004
  name="amount"
  label="Subscription price"
  defaultValue={1290}
/>

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-inputgroup-004-* palette — do not swap it for your theme tokens
- the border on the group instead of on the halves: otherwise two more appear inside the shared one
- the inner line as an inset box-shadow rather than a border: a collapsed border doubles in width
- overflow:hidden on the group — it clips the halves to the border radius
- right-aligning the number: amount and currency have to read as one line
- the :focus-within highlight on the whole group: focus can sit on the number or on the currency

## 6. You may change
- the label copy and the currencies array
- the starting defaultValue and the field step
- the name prop — both form field names are built from it
- the accent through the accent prop

## 7. Rules
- Currency conversion is the server's job: the client sends a number and a currency code.
- Decimals in type="number" behave differently across locales — money is often easier as a masked text field.
- Long currency codes stretch the select and eat the number's room: keep the list short.
- 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/inputgroup-004.json
https://vibeui.ru/r/inputgroup-004.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-inputgroup-004-*. Серверный: состояния нет. Здесь другой приём сцепки: рамка живёт на группе, у input и select её нет, а внутренняя линия нарисована inset box-shadow — так толщина не удваивается при схлопывании. overflow:hidden обрезает подложки по радиусу. Число выровнено вправо, стрелки number-поля скрыты. Значение и валюта уходят двумя полями формы.

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 Inputgroup004Props = Omit<
  ComponentPropsWithoutRef<"div">,
  "children" | "defaultValue"
> & {
  name?: string
  label?: string
  currencies?: string[]
  defaultValue?: number
  hint?: string
  accent?: string
}

// Идея компонента: число прижато вправо, к самой границе с валютой, — так
// сумма и её валюта читаются как одна строка «12 500 ₽», а не как два поля.
// Рамка живёт на группе, у input и select её нет; разделяет половины
// внутренняя линия box-shadow'ом, чтобы схлопывание границ не давало
// двойной толщины. Стрелка select нарисована градиентами: своя разметка
// внутри option невозможна, а картинку в registry не положишь.
const STYLES = `
:where([data-vibeui-block="inputgroup-004"]){
--vibeui-inputgroup-004-surface:oklch(1 0 0);
--vibeui-inputgroup-004-shell:oklch(0.91 0.006 265);
--vibeui-inputgroup-004-fg:oklch(0.21 0.014 265);
--vibeui-inputgroup-004-muted:oklch(0.55 0.014 265);
--vibeui-inputgroup-004-field:oklch(0.99 0.002 265);
--vibeui-inputgroup-004-fixed:oklch(0.96 0.004 265);
--vibeui-inputgroup-004-border:oklch(0.86 0.008 265);
--vibeui-inputgroup-004-accent:oklch(0.5 0.15 150);
--vibeui-inputgroup-004-radius:0.75rem;
--vibeui-inputgroup-004-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="inputgroup-004"]{
display:flex;flex-direction:column;gap:0.4375rem;
width:100%;max-width:20rem;box-sizing:border-box;padding:0.875rem;
background:var(--vibeui-inputgroup-004-surface);
border:1px solid var(--vibeui-inputgroup-004-shell);border-radius:0.875rem;
font-family:var(--vibeui-inputgroup-004-font);color:var(--vibeui-inputgroup-004-fg);
}
[data-vibeui-block="inputgroup-004"] *{box-sizing:border-box}
[data-vibeui-block="inputgroup-004"] label{font-size:0.8125rem;font-weight:600}
/* Рамка одна на всю сцепку, overflow обрезает половины по её радиусу. */
[data-vibeui-block="inputgroup-004"] [data-part="group"]{
display:flex;align-items:stretch;overflow:hidden;
height:3rem;background:var(--vibeui-inputgroup-004-field);
border:1px solid var(--vibeui-inputgroup-004-border);
border-radius:var(--vibeui-inputgroup-004-radius);
transition:border-color .16s ease,box-shadow .16s ease;
}
[data-vibeui-block="inputgroup-004"] [data-part="group"]:focus-within{
border-color:var(--vibeui-inputgroup-004-accent);
box-shadow:0 0 0 3px color-mix(in oklab,var(--vibeui-inputgroup-004-accent) 18%,transparent);
}
[data-vibeui-block="inputgroup-004"] [data-part="group"] > *{
border:0;background:none;font:inherit;color:inherit;
}
[data-vibeui-block="inputgroup-004"] [data-part="group"] > *:focus{outline:none}
[data-vibeui-block="inputgroup-004"] input{
flex:1;min-width:0;padding:0 0.75rem;text-align:right;
font-size:1.25rem;font-weight:700;font-variant-numeric:tabular-nums;
}
[data-vibeui-block="inputgroup-004"] input::-webkit-outer-spin-button,
[data-vibeui-block="inputgroup-004"] input::-webkit-inner-spin-button{appearance:none;margin:0}
/* Внутренняя линия тенью, а не border: толщина не удваивается. */
[data-vibeui-block="inputgroup-004"] select{
appearance:none;flex:none;width:6.5ch;padding:0 1.25rem 0 0.625rem;cursor:pointer;
background:var(--vibeui-inputgroup-004-fixed);
box-shadow:inset 1px 0 0 var(--vibeui-inputgroup-004-border);
font-size:0.9375rem;font-weight:650;color:var(--vibeui-inputgroup-004-muted);
background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,transparent 50%);
background-position:calc(100% - 0.8rem) 50%,calc(100% - 0.5rem) 50%;
background-size:0.3rem 0.3rem,0.3rem 0.3rem;
background-repeat:no-repeat;
}
[data-vibeui-block="inputgroup-004"] [data-part="group"]:focus-within select:focus{
color:var(--vibeui-inputgroup-004-fg);
}
[data-vibeui-block="inputgroup-004"] [data-part="hint"]{
margin:0;font-size:0.75rem;line-height:1.4;color:var(--vibeui-inputgroup-004-muted);
}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="inputgroup-004"] *{animation:none!important;transition:none!important}}
`

/**
 * Сцепка «сумма + валюта» в одной рамке: число справа, валюта у самой границы.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Inputgroup004({
  name = "amount",
  label = "Стоимость подписки",
  currencies = ["₽", "$", "€", "₸"],
  defaultValue = 1290,
  hint = "Валюта уходит отдельным полем формы, пересчёт делает сервер.",
  accent,
  className,
  style,
  ...props
}: Inputgroup004Props) {
  const palette = {
    ...(accent ? { "--vibeui-inputgroup-004-accent": accent } : null),
    ...style,
  } as CSSProperties

  return (
    <>
      <style href="vibeui-inputgroup-004" precedence="medium">
        {STYLES}
      </style>
      <div
        {...props}
        data-vibeui-block="inputgroup-004"
        className={className}
        style={palette}
      >
        <label htmlFor={`${name}-value`}>{label}</label>
        <div data-part="group">
          <input
            id={`${name}-value`}
            name={name}
            type="number"
            inputMode="decimal"
            min={0}
            step={10}
            defaultValue={defaultValue}
            aria-describedby={`${name}-hint`}
          />
          <select
            name={`${name}-currency`}
            aria-label="Валюта"
            defaultValue={currencies[0]}
          >
            {currencies.map((currency) => (
              <option key={currency} value={currency}>
                {currency}
              </option>
            ))}
          </select>
        </div>
        <p data-part="hint" id={`${name}-hint`}>
          {hint}
        </p>
      </div>
    </>
  )
}