Radio Card
RadioCardは、ラジオボタンとカードUIを組み合わせた選択UI向けコンポーネントです。
radio_card/default is coming soon.
<script lang="ts">
import RadioCard from '$lib/components/ui/modules/RadioCard.svelte';
</script>
<div class="flex items-center justify-center size-full min-h-screen">
<RadioCard id="option-1" class="w-80" value="choice1">
<div>
<div class="font-normal text-sm/tight mb-1.5">選択肢1</div>
<div class="font-normal text-base-foreground-muted text-sm">ここに補足文が入ります。</div>
</div>
</RadioCard>
</div>
プロパティ
RadioCardは、以下のプロパティをサポートしています。
| 名前 | 型 | デフォルト値 | 説明 |
|---|---|---|---|
children |
Snippet<[]> |
カード内に表示するコンテンツ | |
group |
any |
選択値(bind:group で双方向バインド) |
|
isError |
boolean |
false |
true の場合はエラースタイルを適用 |
インストールの手順
以下のコンポーネントのコードを、使いたいプロジェクトにコピー&ペーストします。
パスは実際のプロジェクトの構成にあわせて更新します。
modules/RadioCard.svelte
<!--
@component
## 概要
- ラジオボタンとカードUIを組み合わせた選択用コンポーネントです。
## 機能
- カード全体をクリックしてラジオ選択できます
- groupバインディングで選択状態を外部と同期できます
- disabled / isError に応じて状態別スタイルを適用します
## Props
- children: カード内に表示する Snippet
- group: 選択値(bind:groupで双方向バインド)
- isError: trueの場合はエラースタイルを適用します
- class: 追加クラス
## Usage
```svelte
<RadioCard value="option-a" bind:group={selected}>
<div>選択肢A</div>
</RadioCard>
```
-->
<script lang="ts" module>
import type { Snippet } from 'svelte';
import type { ClassValue, HTMLInputAttributes } from 'svelte/elements';
import { cva } from 'class-variance-authority';
export const radioCardVariants = cva('flex size-full gap-2 p-4 border border-base-stroke-default rounded-md cursor-pointer hover:bg-base-container-accent/90 has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-primary', {
variants: {
/** 操作できるかどうか */
disabled: {
true: ['opacity-50 pointer-events-none'],
false: ['cursor-pointer'],
},
/** エラーかどうか */
isError: {
true: ['border-destructive'],
false: ['has-checked:border-primary'],
},
},
});
export interface RadioCardProps extends HTMLInputAttributes {
/** カードの中身 */
children: Snippet<[]>;
/** 選択されている値を保持する変数(双方向バインド用) */
group?: any;
/** エラー状態(スタイル制御用) */
isError?: boolean;
/** 外部から注入するクラス */
class?: ClassValue;
}
</script>
<script lang="ts">
import Label from '$lib/components/ui/atoms/Label.svelte';
import Radio from '$lib/components/ui/atoms/Radio.svelte';
let { children, group = $bindable(), isError = false, class: className, ...inputAttributes }: RadioCardProps = $props();
let radioCardVariantClass = $derived(radioCardVariants({ class: className, disabled: inputAttributes.disabled, isError }));
</script>
<Label for={inputAttributes.id}>
<div class={['group', radioCardVariantClass]}>
<!-- focus時にRadioのoutlineを消す -->
<Radio class="*:outline-none" {...inputAttributes} bind:group {isError} />
{@render children()}
</div>
</Label>
依存コンポーネント
RadioCardを使うときは、以下のコンポーネントもダウンロードが必要です。
使い方
radio_card/default is coming soon.
<script lang="ts">
import RadioCard from '$lib/components/ui/modules/RadioCard.svelte';
</script>
<div class="flex items-center justify-center size-full min-h-screen">
<RadioCard id="option-1" class="w-80" value="choice1">
<div>
<div class="font-normal text-sm/tight mb-1.5">選択肢1</div>
<div class="font-normal text-base-foreground-muted text-sm">ここに補足文が入ります。</div>
</div>
</RadioCard>
</div>
サンプル
Default
特に操作が行われていない、デフォルトの状態です。
radio_card/default is coming soon.
<script lang="ts">
import RadioCard from '$lib/components/ui/modules/RadioCard.svelte';
</script>
<div class="flex items-center justify-center size-full min-h-screen">
<RadioCard id="option-1" class="w-80" value="choice1">
<div>
<div class="font-normal text-sm/tight mb-1.5">選択肢1</div>
<div class="font-normal text-base-foreground-muted text-sm">ここに補足文が入ります。</div>
</div>
</RadioCard>
</div>
Error
入力内容に問題があり、エラーが表示されている状態です。
radio_card/error is coming soon.
<script lang="ts">
import RadioCard from '$lib/components/ui/modules/RadioCard.svelte';
</script>
<div class="flex items-center justify-center size-full min-h-screen">
<RadioCard id="option-1" class="w-80" isError value="choice1">
<div>
<div class="font-normal text-sm/tight mb-1.5">選択肢1</div>
<div class="font-normal text-base-foreground-muted text-sm">ここに補足文が入ります。</div>
</div>
</RadioCard>
</div>
Disabled
利用不可の状態です。
radio_card/disabled is coming soon.
<script lang="ts">
import RadioCard from '$lib/components/ui/modules/RadioCard.svelte';
</script>
<div class="flex items-center justify-center size-full min-h-screen">
<RadioCard class="w-80" disabled>
<div>
<div class="font-normal text-sm/tight mb-1.5">選択肢1</div>
<div class="font-normal text-base-foreground-muted text-sm">ここに補足文が入ります。</div>
</div>
</RadioCard>
</div>
GroupText
bind:groupを使うことで、複数のラジオボタンをグループ化し、同じ値を共有できます。
radio_card/group_text is coming soon.
<script lang="ts">
import Label from '$lib/components/ui/atoms/Label.svelte';
import RadioCard from '$lib/components/ui/modules/RadioCard.svelte';
const textList = [
{ id: 'option-1', name: 'selection-group-1', value: 'choice1', text: '選択肢1' },
{ id: 'option-2', name: 'selection-group-2', value: 'choice2', text: '選択肢2' },
{ id: 'option-3', name: 'selection-group-3', value: 'choice3', text: '選択肢3' },
];
let selectedValue = $state('choice1');
</script>
<div class="flex flex-col items-center justify-center size-full min-h-screen py-12">
<div class="flex flex-col">
<Label class="mb-1.5">ラベル</Label>
<div class="font-normal text-base-foreground-muted text-sm mb-4">ここに補足文が入ります。</div>
<div class="flex flex-col gap-2">
{#each textList as text}
<RadioCard id={text.id} class="w-80" bind:group={selectedValue} value={text.value} name={text.name}>
<div class="flex flex-col gap-1.5">
<div class="font-normal text-sm/tight">{text.text}</div>
<div class="font-normal text-base-foreground-muted text-sm">ここに補足文が入ります。</div>
</div>
</RadioCard>
{/each}
</div>
</div>
</div>
GroupImage
bind:groupを使うことで、複数のラジオボタンをグループ化し、同じ値を共有できます。
radio_card/group_image is coming soon.
<script lang="ts">
import Label from '$lib/components/ui/atoms/Label.svelte';
import RadioCard from '$lib/components/ui/modules/RadioCard.svelte';
const cardList = [
{ id: 'option-1', name: 'selection-group-1', value: 'choice1', text: '選択肢1', image: '/images/sample-rectangle.png' },
{ id: 'option-2', name: 'selection-group-2', value: 'choice2', text: '選択肢2', image: '/images/sample-rectangle.png' },
{ id: 'option-3', name: 'selection-group-3', value: 'choice3', text: '選択肢3', image: '/images/sample-rectangle.png' },
];
let selectedValue = $state('choice1');
</script>
<div class="flex flex-col items-center justify-center size-full min-h-screen py-12">
<div class="flex flex-col w-xs">
<Label class="mb-1.5">ラベル</Label>
<div class="font-normal text-base-foreground-muted text-sm mb-4">ここに補足文が入ります。</div>
<div class="flex flex-col gap-2">
{#each cardList as list}
<RadioCard id={list.id} name={list.name} bind:group={selectedValue} value={list.value}>
<div class="flex flex-col gap-1.5">
<div class="font-normal text-sm/tight">{list.text}</div>
<img class="w-full rounded-md object-contain" src={list.image} alt="">
</div>
</RadioCard>
{/each}
</div>
</div>
</div>