1 звезда на GitHub = 1 новый компонент.

Поставить звезду
Формыradiogroup

Custom Radio Group

Группа радиокнопок в виде карточек с иконками и активной подсветкой границ.

Живое превью

Использование

Скопируйте и вставьте этот код в ваш React-компонент:

tsx
import { CustomRadioGroup } from 'react-component-kit-ui/forms';

<CustomRadioGroup label="Select your billing cycle" name="billing" options={[
    { label: "Monthly", value: "monthly" },
    { label: "Yearly (Save 20%)", value: "yearly", checked: true },
    { label: "Lifetime", value: "life" }
  ]} />

Ручная установка

Если вы не используете пакет npm, создайте файл и вставьте следующий код: src/components/CustomRadioGroup.tsx + CustomRadioGroup.css:

CustomRadioGroup.tsx
'use client';

import './CustomRadioGroup.css';

/** class:list → className */
function cx(...args: unknown[]): string {
  const out: string[] = [];
  const walk = (v: unknown) => {
    if (!v) return;
    if (typeof v === 'string' || typeof v === 'number') out.push(String(v));
    else if (Array.isArray(v)) v.forEach(walk);
    else if (typeof v === 'object') for (const [k, on] of Object.entries(v as Record<string, unknown>)) if (on) out.push(k);
  };
  args.forEach(walk);
  return out.join(' ');
}

/**
 * CustomRadioGroup — A group of styled radio buttons for vertical or horizontal options.
 * 
 * @param {string} label - Optional title for the fieldset.
 * @param {string} name - Common HTML name for the radio group.
 * @param {Array<{label: string, value: string, checked?: boolean}>} options - List of choices.
 * @param {boolean} horizontal - Layout orientation. Default is false (vertical).
 */
export interface CustomRadioGroupProps {
  label?: string;
  name: string;
  options: Array<{ label: string; value: string; checked?: boolean }>;
  horizontal?: boolean;
}

export function CustomRadioGroup({ label, name, options = [], horizontal = false }: CustomRadioGroupProps) {
  return (
    <>
      <fieldset className={cx(["radio-group", { "radio-group--horizontal": horizontal }])} data-rk-01df88="">
        {label && <legend className="radio-group-legend" data-rk-01df88="">{label}</legend>}
        <div className="radio-items" data-rk-01df88="">
          {options.map((opt, i) => (
            <label key={i} className="radio-item" data-rk-01df88="">
              <input type="radio" name={name} defaultValue={opt.value} defaultChecked={opt.checked} data-rk-01df88="" />
              <span className="radio-circle" aria-hidden="true" data-rk-01df88=""></span>
              <span className="radio-label" data-rk-01df88="">{opt.label}</span>
            </label>
          ))}
        </div>
      </fieldset>
    </>
  );
}

export default CustomRadioGroup;
CustomRadioGroup.css
/* CustomRadioGroup — ported from astro-component-kit (forms/CustomRadioGroup.astro) */

.radio-group:where([data-rk-01df88]) { border: none; padding: 0; margin: 0; width: 100%; }
.radio-group-legend:where([data-rk-01df88]) { font-size: 0.85rem; font-weight: 600; color: var(--c-text-2, #94a3b8); margin-bottom: var(--sp-3, 0.75rem); margin-left: 0.5rem; }

.radio-items:where([data-rk-01df88]) { display: flex; flex-direction: column; gap: var(--sp-3, 0.8rem); }
.radio-group--horizontal:where([data-rk-01df88]) .radio-items:where([data-rk-01df88]) { flex-direction: row; flex-wrap: wrap; gap: var(--sp-6, 1.5rem); }

.radio-item:where([data-rk-01df88]) { display: flex; align-items: center; gap: 0.75rem; cursor: pointer; color: var(--c-text-2, #94a3b8); transition: 0.2s; user-select: none; }

.radio-item:where([data-rk-01df88]) input:where([data-rk-01df88]) { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }
.radio-item:where([data-rk-01df88]) { position: relative; }
.radio-item:where([data-rk-01df88]) input:focus-visible:where([data-rk-01df88]) + .radio-circle:where([data-rk-01df88]) { outline: 2px solid var(--c-primary-light, #818cf8); outline-offset: 2px; }

.radio-circle:where([data-rk-01df88]) { 
  width: 20px; height: 20px; 
  border: 2px solid var(--c-border, #334155); 
  border-radius: var(--r-full, 50%); 
  position: relative; cursor: pointer; transition: 0.2s;
  background: var(--c-bg-elev, rgba(255,255,255,0.02));
}

.radio-item:hover:where([data-rk-01df88]) .radio-circle:where([data-rk-01df88]) { border-color: var(--c-primary-light, #818cf8); }

.radio-circle:where([data-rk-01df88])::after { 
  content: ''; position: absolute; inset: 4px; 
  background: var(--c-primary, #6366f1); 
  border-radius: 50%; opacity: 0; transform: scale(0.5); transition: 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

.radio-item:where([data-rk-01df88]) input:checked:where([data-rk-01df88]) + .radio-circle:where([data-rk-01df88]) { border-color: var(--c-primary, #6366f1); box-shadow: 0 0 10px rgba(99, 102, 241, 0.2); }
.radio-item:where([data-rk-01df88]) input:checked:where([data-rk-01df88]) + .radio-circle:where([data-rk-01df88])::after { opacity: 1; transform: scale(1); }

.radio-label:where([data-rk-01df88]) { font-size: 0.95rem; }
.radio-item:where([data-rk-01df88]) input:checked:where([data-rk-01df88]) ~ .radio-label:where([data-rk-01df88]) { color: var(--c-text-1, #fff); font-weight: 600; }

Краткая информация

Категория
Формы
Имя файла
CustomRadioGroup.tsx
Зависимости
Нет — чистый React + CSS
Теги
radiogroup