mcpField
S
svelte-components

svelte-components

github.com/exceptionless開発2.5k43 回インストール1 日前に更新
リポジトリ

svelte-components について

Svelte開発者向けのコンポーネント規約をまとめたスキルです。構成、Svelte runes、プロパティ、操作方法に加え、UIの視覚確認とキーボードアクセシビリティの検証を扱います。

主な機能

  1. 01コンポーネントの命名と配置
  2. 02Svelte runesとプロパティの指針
  3. 03shadcn-svelteのコンポーネント規約
  4. 04イベントとスニペットのパターン
  5. 05視覚とキーボード操作の確認

活用シーン

  1. 01一貫性のあるSvelteコンポーネントの構築
  2. 02コンポーネント構成と状態の確認
  3. 03UIの見た目と操作の検証

MCP 対応

このスキルは、UI開発時にChrome MCPでスナップショットやスクリーンショットを取得し、表示、操作状態、レスポンシブレイアウトを確認するよう案内します。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/exceptionless/exceptionless --skill svelte-components

インストール後、Claude Code で/svelte-componentsを入力して読み込まれたか確認します。

詳細

Svelte コンポーネント

ドキュメント: svelte.dev | API リファレンスには context7 を使用してください

Chrome MCP による視覚的な検証

UI を変更した場合は、必ず Chrome MCP を使って視覚的に検証してください。

  1. コンポーネントを変更した後、Chrome MCP でスナップショットまたはスクリーンショットを撮影します
  2. コンポーネントが正しくレンダリングされ、想定されたデザインと一致していることを確認します
  3. 該当する場合は、インタラクティブな状態(ホバー、フォーカス、無効)をテストします
  4. さまざまなビューポートサイズでレスポンシブな動作を確認します
  5. 検証には /next サイトパスを使用してください

この視覚的な検証ループにより、自動テストでは見逃す可能性のあるスタイルの問題、レイアウトの問題、アクセシビリティの後退を検出できます。

ファイルの整理

命名規則
  • すべてのコンポーネントファイルに kebab-case を使用します。:stack-status-badge.svelte、user-profile-card.svelte
  • API コントローラーに合わせて、機能スライスと同じ場所に配置します
ディレクトリー構成
src/lib/features/
├── organizations/           # Matches OrganizationController
│   ├── components/
│   │   ├── organization-card.svelte
│   │   └── organization-switcher.svelte
│   ├── api.svelte.ts
│   ├── models.ts
│   └── schemas.ts
├── stacks/                  # Matches StackController
│   └── components/
│       └── stack-status-badge.svelte
└── shared/                  # Shared across features
    └── components/
        ├── data-table/
        ├── navigation/
        └── typography/

shadcn-svelte コンポーネントを必ず使用する

ボタン、入力欄、フォーム要素にはネイティブ HTML を使用しないでください。

<script lang="ts">
    import { Button } from '$comp/ui/button';
    import { Input } from '$comp/ui/input';
    import * as Card from '$comp/ui/card';
</script>


<Card.Root>
    <Card.Header>
        <Card.Title>Settings</Card.Title>
    </Card.Header>
    <Card.Content>
        <Input placeholder="Enter value" />
    </Card.Content>
    <Card.Footer>
        <Button>Save</Button>
    </Card.Footer>
</Card.Root>


<button class="...">Save</button>
<input type="text" />

Runes

$state — リアクティブな状態
<script lang="ts">
    let count = $state(0);
    let user = $state<User | null>(null);
    let items = $state<string[]>([]);
</script>
$derived — 計算値
<script lang="ts">
    let count = $state(0);
    let doubled = $derived(count * 2);
    let isEven = $derived(count % 2 === 0);

    // Complex derived
    let summary = $derived.by(() => {
        return items.filter(i => i.active).map(i => i.name).join(', ');
    });
</script>
$effect — 副作用
<script lang="ts">
    let searchTerm = $state('');

    $effect(() => {
        console.log('Search term changed:', searchTerm);
        return () => console.log('Cleaning up');
    });
</script>

Props

<script lang="ts">
    interface Props {
        name: string;
        count?: number;
        onUpdate?: (value: number) => void;
        children?: import('svelte').Snippet;
    }

    let { name, count = 0, onUpdate, children }: Props = $props();
</script>

イベント処理

on:click ではなく onclick を使用します。

<Button onclick={() => handleClick()}>Click me</Button>
<Input oninput={(e) => (value = e.currentTarget.value)} />

Snippets(コンテンツの投影)

<slot> を snippets に置き換えます。src/Exceptionless.Web/ClientApp/src/routes/(auth)/login/+page.svelte より:

<form.Subscribe selector={(state) => state.errors}>
    {#snippet children(errors)}
        <ErrorMessage message={getFormErrorMessages(errors)}></ErrorMessage>
    {/snippet}
</form.Subscribe>

<form.Field name="email">
    {#snippet children(field)}
        <Field.Field data-invalid={ariaInvalid(field)}>
            <Field.Label for={field.name}>Email</Field.Label>
            <Input
                id={field.name}
                value={field.state.value}
                oninput={(e) => field.handleChange(e.currentTarget.value)}
            />
            <Field.Error errors={mapFieldErrors(field.state.meta.errors)} />
        </Field.Field>
    {/snippet}
</form.Field>

クラスのマージ

条件付きクラスには配列構文を使用します。

<div class={['flex items-center', expanded && 'bg-muted', className]}>
    Content
</div>

<Button class={['w-full', isActive && 'bg-primary']}>Save</Button>

キーボードアクセシビリティ

すべてのインタラクティブなコンポーネントは、キーボードで操作できる必要があります。

  • Button コンポーネントを使用します(フォーカス処理が自動的に行われます)
  • カスタム操作に tabindex とキーボードハンドラーが設定されていることを確認します
  • キーボードのみでのナビゲーションをテストします

WCAG のガイドラインについては、アクセシビリティを参照してください。

インポート

<script lang="ts">
    // Use $app/state instead of $app/stores
    import { page } from '$app/state';

    // Access page data
    let currentPath = $derived(page.url.pathname);
</script>

参考資料

コメント