Button
Buttonは、押下することで特定の操作を実行するコンポーネントです。
ボタン内部の構造はテキストやアイコン等に柔軟に変更できます。
<script lang="ts">
import Button from '$lib/components/ui/atoms/Button.svelte';
</script>
<Button>ボタン</Button>
プロパティ
Buttonは、以下のプロパティをサポートしています。
| 名前 | 型 | デフォルト値 | 説明 |
|---|---|---|---|
variant |
string |
primary |
ボタンのスタイルを指定します。primary, secondary, success, danger のいずれかを選択できます。 |
size |
string |
medium |
ボタンのサイズを指定します。small, medium, large のいずれかを選択できます。 |
tone |
string |
solid |
ボタンのトーンを指定します。solid, ghost のいずれかを選択できます。 |
isSquare |
boolean |
false |
ボタンを正方形にします。 |
disabled |
boolean |
false |
ボタンを無効化します。無効化されたボタンはクリックできません。 |
インストールの手順
以下のコンポーネントのコードを、使いたいプロジェクトにコピー&ペーストします。 パスは実際のプロジェクトの構成にあわせて更新します。
atoms/Button.svelte
<!--
@component
## 概要
- buttonタグのように、クリックなどのユーザー操作に対応する汎用的なボタンコンポーネントです
- 色(variant)、サイズ(size)、塗り(tone)を組み合わせて見た目を柔軟に変更できます
## 機能
- buttonタグと同様に、クリックやフォーカスなどのインタラクションに対応します
- 見た目を変更するためのいくつかのスタイル用Propsが追加されています(詳細はPropsセクションを参照)
## Props
- variant: ボタンの意味を指定すると、それに合わせたスタイルになります
- tone: 塗りのスタイルを指定します
- isSquare: trueにすると正方形のボタンになります
- size: サイズを指定します
- disabled: 指定するとグレーアウトされ、クリック不可になります
## Usage
```svelte
<Button variant="primary" tone="solid" size="medium">ボタン</Button>
```
-->
<script module lang="ts">
import type { Snippet } from 'svelte';
import type { ClassValue, HTMLButtonAttributes } from 'svelte/elements';
import { cva, type VariantProps } from 'class-variance-authority';
export const buttonVariants = cva('inline-flex items-center justify-center gap-2 rounded-md leading-tight text-sm outline-primary transition-colors cursor-pointer focus-visible:outline-[0.125rem] focus-visible:outline-offset-[0.125rem] focus-visible:outline-primary', {
variants: {
/** ボタンの使用用途 */
variant: {
primary: [],
secondary: [],
success: [],
danger: [],
},
/** ボタンのサイズ */
size: {
small: ['min-h-9 px-3 py-2'],
medium: ['min-h-10 px-4 py-2.5'],
large: ['min-h-11 px-8 py-3'],
},
/** ボタンの塗りの値 */
tone: {
solid: [],
ghost: [],
},
/** ボタンを正方形にするか */
isSquare: {
true: ['aspect-square'],
false: [],
},
/** 操作できるかどうか */
disabled: {
true: ['opacity-50 !text-base-foreground-default pointer-events-none'],
false: [],
},
},
compoundVariants: [
{
variant: 'primary',
tone: 'solid',
class:
'bg-primary border border-primary text-primary-on-fill active:bg-primary/80 hover:bg-primary/90 hover:border-primary/10 active:border-primary/80',
},
{
variant: 'primary',
tone: 'ghost',
class:
'border border-transparent text-primary active:bg-base-container-accent/80 hover:bg-base-container-accent/90 active:border-base-container-accent/80 hover:border-base-container-accent/90',
},
{
variant: 'secondary',
tone: 'solid',
class:
'border border-base-stroke-default text-base-foreground-default active:bg-base-container-accent/80 hover:bg-base-container-accent/90',
},
{
variant: 'secondary',
tone: 'ghost',
class:
'border border-transparent text-base-foreground-default active:bg-base-container-accent/80 hover:bg-base-container-accent/90 active:border-base-container-accent/80 hover:border-base-container-accent/90',
},
{
variant: 'success',
tone: 'solid',
class:
'bg-success border border-success text-success-on-fill active:bg-success/80 hover:bg-success/90 hover:border-success/10 active:border-success/80',
},
{
variant: 'success',
tone: 'ghost',
class:
'border border-transparent text-success active:bg-base-container-accent/80 hover:bg-base-container-accent/90 active:border-base-container-accent/80 hover:border-base-container-accent/90',
},
{
variant: 'danger',
tone: 'solid',
class:
'bg-destructive border border-destructive text-destructive-on-fill active:bg-destructive/80 hover:bg-destructive/90 hover:border-destructive/10 active:border-destructive/80',
},
{
variant: 'danger',
tone: 'ghost',
class:
'border border-transparent text-destructive active:bg-base-container-accent/80 hover:bg-base-container-accent/90 active:border-base-container-accent/80 hover:border-base-container-accent/90',
},
{
variant: 'primary',
disabled: true,
tone: 'solid',
class: '!bg-base-container-muted !border-base-container-muted !text-base-foreground-default',
},
{
variant: 'success',
disabled: true,
tone: 'solid',
class: '!bg-base-container-muted !border-base-container-muted !text-base-foreground-default',
},
{
variant: 'danger',
disabled: true,
tone: 'solid',
class: '!bg-base-container-muted !border-base-container-muted !text-base-foreground-default',
},
{
isSquare: true,
size: 'small',
class: '!p-2.25',
},
{
isSquare: true,
size: 'medium',
class: '!p-2.75',
},
{
isSquare: true,
size: 'large',
class: '!p-3.25',
},
],
defaultVariants: {
variant: 'primary',
tone: 'solid',
size: 'medium',
},
});
export type ButtonVariants = VariantProps<typeof buttonVariants>;
export interface ButtonProps extends ButtonVariants, HTMLButtonAttributes {
/** クラス */
class?: ClassValue;
children?: Snippet<[]>;
}
</script>
<script lang="ts">
let { tone, isSquare, variant, size, class: className, children, ...buttonAttributes }: ButtonProps = $props();
let buttonVariantClass = $derived(buttonVariants({ class: className, tone, isSquare, variant, disabled: buttonAttributes.disabled, size }));
</script>
<button class={buttonVariantClass} {...buttonAttributes}>
{@render children?.()}
</button>
使い方
<script lang="ts">
import Button from '$lib/components/ui/atoms/Button.svelte';
</script>
<Button>ボタン</Button>
サンプル
Default
最も優先度の高いアクションに使うコンポーネントです。
基本的には1画面に1つのみ使用します。
<script lang="ts">
import Button from '$lib/components/ui/atoms/Button.svelte';
</script>
<Button>ボタン</Button>
Disabled
利用不可の状態です。
<script lang="ts">
import Button from '$lib/components/ui/atoms/Button.svelte';
</script>
<Button disabled>ボタン</Button>
Variants
ボタンのタイプを変更することもできます。
<script lang="ts">
import Button from '$lib/components/ui/atoms/Button.svelte';
</script>
<div class="flex items-center justify-center flex-wrap gap-4 max-sm:flex-col">
<Button variant="primary">primary</Button>
<Button variant="secondary">secondary</Button>
<Button variant="success">success</Button>
<Button variant="danger">danger</Button>
</div>
Sizes
ボタンのサイズを変更することもできます。
<script lang="ts">
import Button from '$lib/components/ui/atoms/Button.svelte';
</script>
<div class="flex items-center justify-center flex-wrap gap-4 max-sm:flex-col">
<Button size="small">small</Button>
<Button size="medium">medium</Button>
<Button size="large">large</Button>
</div>
Tones
ボタンのトーンを変更することもできます。
<script lang="ts">
import Button from '$lib/components/ui/atoms/Button.svelte';
</script>
<div class="flex gap-4 items-center">
<Button tone="solid">solid</Button>
<Button tone="ghost">ghost</Button>
</div>
Square
ボタンを正方形にすることもできます。
<script lang="ts">
import Button from '$lib/components/ui/atoms/Button.svelte';
import { Plus } from '@lucide/svelte';
</script>
<div class="flex gap-4 items-center">
<Button size="small" isSquare>
<Plus size="1rem" />
</Button>
<Button size="medium" isSquare>
<Plus size="1rem" />
</Button>
<Button size="large" isSquare>
<Plus size="1rem" />
</Button>
</div>
Click
一般的な<button>と同様に、クリック時のイベントを登録できます。
<script lang="ts">
import Button from '$lib/components/ui/atoms/Button.svelte';
function onClick() {
alert('ボタンがクリックされました');
};
</script>
<Button onclick={onClick}>ボタン</Button>
With Icon
アイコンを追加することもできます。
<script lang="ts">
import Button from '$lib/components/ui/atoms/Button.svelte';
import { Download, Search, Trash } from '@lucide/svelte';
</script>
<div class="flex items-center justify-center flex-wrap gap-4 max-sm:flex-col">
<!-- ダウンロードボタン -->
<Button>
<Download size="1rem" />
ダウンロード
</Button>
<!-- 検索ボタン -->
<Button variant="secondary">
<Search size="1rem" />
検索する
</Button>
<!-- 削除ボタン -->
<Button variant="danger">
<Trash size="1rem" />
削除する
</Button>
</div>
Loading
ローディング状態です。
<script lang="ts">
import Button from '$lib/components/ui/atoms/Button.svelte';
import { Loader } from '@lucide/svelte';
</script>
<div class="flex items-center justify-center flex-wrap gap-4 max-sm:flex-col">
<Button variant="primary" tone="solid" disabled>
<div class="animate-spin [animation-duration:2.2s]">
<Loader size="1rem" />
</div>
ボタン
</Button>
<Button variant="secondary" tone="solid" disabled>
<div class="animate-spin [animation-duration:2.2s]">
<Loader size="1rem" />
</div>
ボタン
</Button>
</div>
Only Style
<a> タグ等に Buttonのクラスやスタイルのみを適用することで、同じ見た目を再現できます。
<script lang="ts">
import { buttonVariants } from '$lib/components/ui/atoms/Button.svelte';
</script>
<a href="#" class={buttonVariants({ variant: 'secondary' })}>a タグ</a>
Toggle Button
フラグで variant 属性や中身のコンテンツを切り替えることで Toggle Button を実現することができます。
<script lang="ts">
import Button from '$lib/components/ui/atoms/Button.svelte';
import { MoonStar, Sun, UserRoundPlus } from '@lucide/svelte';
type Mode = 'light' | 'dark';
let isFollowed = $state(true);
let mode = $state<Mode>('light');
</script>
<div class="flex items-center justify-center flex-wrap gap-4 max-sm:flex-col">
<Button variant={isFollowed ? 'primary' : 'secondary'} onclick={() => (isFollowed = !isFollowed)}>
{#if isFollowed}
<UserRoundPlus size="1rem" />
フォロー
{:else}
フォロー中
{/if}
</Button>
<Button isSquare tone="ghost" variant="secondary" onclick={() => (mode = mode === 'light' ? 'dark' : 'light')}>
{#if mode === 'light'}
<Sun size="1rem" />
{:else}
<MoonStar size="1rem" />
{/if}
</Button>
</div>