Inputs

Emoji Faces

A rating in faces: stars ask "how many", emoji ask "how was it", and an impression is answered faster that way.

  • rating
  • emoji
  • feedback
  • css-only

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/rating-004?lang=en

How was your order?

Ужасно

Не понравилось

Нормально

Хорошо

Восторг

Ответ анонимный, его видит только служба поддержки

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 "rating-004" (Emoji Faces) 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/rating-004.json

Registry item: https://vibeui.ru/r/rating-004.json
Installs to: components/vibeui/rating-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
A rating in faces: stars ask "how many", emoji ask "how was it", and an impression is answered faster that way.

A five-emoji rating on radios: the picked face is coloured and larger, the rest are desaturated. Server component, zero dependencies, one file.

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

<Rating004 legend="How was your order?" name="mood" defaultValue={4} />

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-rating-004-* palette — do not swap it for your theme tokens
- its own light surface: the rating gets shown over any background
- desaturating the unpicked faces: a full-colour row of emoji does not show what is selected
- the aria-label with text on every option: screen readers announce emoji inconsistently
- the radios under the faces: keyboard support and form submission come for free
- aria-hidden on the emoji itself: otherwise the caption is announced twice
- clear:both on the face row: a legend with float:left otherwise makes it wrap around

## 6. You may change
- legend — the question above the row
- faces — the faces themselves and their captions
- name — the radio group name
- defaultValue — the pre-selected face
- the accent through the accent prop

## 7. Rules
- The :has rules are written for five values: another row length needs its own rules.
- Emoji are drawn by the system font: the faces look different across platforms.
- Five faces is the readable limit: seven shades of mood cannot be told apart.
- 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/rating-004.json
https://vibeui.ru/r/rating-004.json

Компонент самодостаточен: один файл, ноль зависимостей, палитра в локальных переменных --vibeui-rating-004-*. Серверный: всё держится на радиокнопках и :has(). Невыбранные лица обесцвечены фильтром grayscale и приглушены прозрачностью — цветной ряд из пяти рожиц выглядит нарядно, но не показывает выбор. Подпись под рядом переключается правилами :has по значению отмеченной радиокнопки, поэтому текста в JS нет вовсе.

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 Rating004Props = Omit<
  ComponentPropsWithoutRef<"fieldset">,
  "children" | "defaultValue"
> & {
  legend?: string
  name?: string
  faces?: { face: string; text: string }[]
  defaultValue?: number
  accent?: string
}

// Идея компонента: оценка лицами вместо звёзд. Звёзды спрашивают «сколько», а
// эмодзи — «как это было», и на вопрос о впечатлении так отвечают быстрее.
// Невыбранные лица обесцвечены фильтром: цветной ряд из пяти рожиц выглядит
// нарядно, но не показывает выбор. Всё держится на радиокнопках и :has(),
// поэтому клавиатура, отправка формы и подпись под рядом работают без JS,
// а компонент остаётся серверным.
const STYLES = `
:where([data-vibeui-block="rating-004"]){
--vibeui-rating-004-surface:oklch(1 0 0);
--vibeui-rating-004-shell:oklch(0.9 0.006 265);
--vibeui-rating-004-fg:oklch(0.23 0.014 265);
--vibeui-rating-004-muted:oklch(0.55 0.014 265);
--vibeui-rating-004-hover:oklch(0.96 0.004 265);
--vibeui-rating-004-accent:oklch(0.58 0.16 300);
--vibeui-rating-004-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
/* Своя светлая подложка: оценку показывают поверх любого фона. */
[data-vibeui-block="rating-004"]{
display:flex;flex-direction:column;gap:0.5rem;
width:100%;max-width:19rem;box-sizing:border-box;margin:0;padding:0.875rem;
background:var(--vibeui-rating-004-surface);
border:1px solid var(--vibeui-rating-004-shell);border-radius:0.875rem;
font-family:var(--vibeui-rating-004-font);color:var(--vibeui-rating-004-fg);
text-align:center;
}
/* legend во float даёт обтекание: clear возвращает нормальный поток. */
[data-vibeui-block="rating-004"] legend{
float:left;width:100%;padding:0;margin-bottom:0.5rem;font-size:0.875rem;font-weight:650;
}
[data-vibeui-block="rating-004"] [data-part="faces"]{
clear:both;display:flex;justify-content:space-between;gap:0.25rem;
}
[data-vibeui-block="rating-004"] input{position:absolute;opacity:0;pointer-events:none}
[data-vibeui-block="rating-004"] label{
flex:1 1 0;display:grid;place-items:center;cursor:pointer;
height:3rem;border-radius:0.625rem;
}
[data-vibeui-block="rating-004"] label:hover{background:var(--vibeui-rating-004-hover)}
[data-vibeui-block="rating-004"] [data-part="face"]{
font-size:1.75rem;line-height:1;
filter:grayscale(1);opacity:.45;
transition:filter .16s ease,opacity .16s ease,transform .16s ease;
}
/* Обесцвечены все, кроме выбранного: иначе нарядный ряд не показывает выбор. */
[data-vibeui-block="rating-004"] label:has(input:checked) [data-part="face"]{
filter:none;opacity:1;transform:scale(1.2);
}
[data-vibeui-block="rating-004"] label:hover [data-part="face"]{filter:none;opacity:.8}
[data-vibeui-block="rating-004"] label:has(input:focus-visible){outline:2px solid var(--vibeui-rating-004-accent);outline-offset:2px}
/* Подпись без JS: видна та строка, чей value отмечен. */
[data-vibeui-block="rating-004"] [data-part="captions"]{min-height:1.125rem}
[data-vibeui-block="rating-004"] [data-part="caption"]{
display:none;margin:0;font-size:0.8125rem;font-weight:650;color:var(--vibeui-rating-004-accent);
}
[data-vibeui-block="rating-004"]:has(input[value="1"]:checked) [data-part="caption"][data-value="1"],
[data-vibeui-block="rating-004"]:has(input[value="2"]:checked) [data-part="caption"][data-value="2"],
[data-vibeui-block="rating-004"]:has(input[value="3"]:checked) [data-part="caption"][data-value="3"],
[data-vibeui-block="rating-004"]:has(input[value="4"]:checked) [data-part="caption"][data-value="4"],
[data-vibeui-block="rating-004"]:has(input[value="5"]:checked) [data-part="caption"][data-value="5"]{display:block}
[data-vibeui-block="rating-004"] [data-part="hint"]{margin:0;font-size:0.6875rem;color:var(--vibeui-rating-004-muted)}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="rating-004"] *{animation:none!important;transition:none!important}}
`

const DEFAULT_FACES = [
  { face: "😠", text: "Ужасно" },
  { face: "🙁", text: "Не понравилось" },
  { face: "😐", text: "Нормально" },
  { face: "🙂", text: "Хорошо" },
  { face: "😍", text: "Восторг" },
]

/**
 * Оценка эмодзи на радиокнопках: выбранное лицо цветное, остальные обесцвечены.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Rating004({
  legend = "Как вам заказ?",
  name = "vibeui-rating-004",
  faces = DEFAULT_FACES,
  defaultValue = 4,
  accent,
  className,
  style,
  ...props
}: Rating004Props) {
  const palette = {
    ...(accent ? { "--vibeui-rating-004-accent": accent } : null),
    ...style,
  } as CSSProperties

  return (
    <>
      <style href="vibeui-rating-004" precedence="medium">
        {STYLES}
      </style>
      <fieldset
        {...props}
        data-vibeui-block="rating-004"
        className={className}
        style={palette}
      >
        <legend>{legend}</legend>
        <div data-part="faces">
          {faces.map((item, index) => (
            <label key={item.text} aria-label={item.text}>
              <input
                type="radio"
                name={name}
                value={index + 1}
                defaultChecked={index + 1 === defaultValue}
              />
              <span data-part="face" aria-hidden="true">
                {item.face}
              </span>
            </label>
          ))}
        </div>
        <div data-part="captions" aria-live="polite">
          {faces.map((item, index) => (
            <p key={item.text} data-part="caption" data-value={index + 1}>
              {item.text}
            </p>
          ))}
        </div>
        <p data-part="hint">
          Ответ анонимный, его видит только служба поддержки
        </p>
      </fieldset>
    </>
  )
}