mcpField
F
frontend-design

frontend-design

github.com/vaadinデザイン・UI790 回インストール1 日前に更新
リポジトリ

frontend-design について

Java と CSS で洗練された Vaadin インターフェースを構築するためのスキルです。デザイン方針やテーマによるスタイル設定を案内し、タイポグラフィ、色、間隔、コンポーネントの細部を扱います。

主な機能

  1. 01一貫したビジュアル方針の策定
  2. 02Aura と Lumo のテーマ調整
  3. 03文字、色、間隔の調整
  4. 04CSS とバリアントによるコンポーネント装飾
  5. 05モーションと操作効果の追加

活用シーン

  1. 01Vaadin 管理ダッシュボードの設計
  2. 02顧客向けポータルの構築
  3. 03データ密度の高い画面のスタイル統一

MCP 対応

ドキュメント検索には Vaadin MCP ツール、Java API のシグネチャやソース確認には Javadoc MCP の利用を推奨します。

インストール

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

$npx skills add https://github.com/vaadin/agent-skills --skill frontend-design

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

同梱ファイルreferences/design-patterns.md

詳細

個性が際立つ Vaadin インターフェースの作成

特定の API の詳細が不明な場合は、Vaadin MCP ツール(search_vaadin_docs、get_component_styling、get_component_java_api)を使って最新のドキュメントを確認してください。vaadin_version は常に "25" に、ui_language は "java" に設定してください。

正確な Java API シグネチャを確認したり、ソースを読んだりする場合は、~/.m2 から JAR を展開するのではなく、javadoc MCP(mcp__javadoc__* — 未ロードの場合は ToolSearch で javadoc を検索)を使って Maven Central の Javadoc とソースを参照してください。

このスキルでは、デフォルトのテーマスタイルにとどまらない、個性が際立つ洗練された Vaadin インターフェースの作成方法を説明します。目指すのは、細部まで美しさにこだわった、本番環境に適したコードです。ありきたりなデフォルト設定ではありません。

デザインの考え方

コードを書く前に背景を理解し、明確な美的方向性を定めてください。

  • 目的:このインターフェースはどのような問題を解決しますか?誰が使いますか?管理ダッシュボードと、顧客向けポータルとでは、デザインのニーズが異なります。
  • トーン:方向性を選びます。すっきりとプロフェッショナル、温かみがあり親しみやすい、大胆でデータ密度が高い、明るく開放的、暗く集中できる、エディトリアル、遊び心がある、または洗練されている、といった方向性があります。方向性は、ユーザーとコンテンツに合ったものにしてください。
  • 制約:Vaadin コンポーネントライブラリ、Vaadin テーマシステム(Aura または Lumo)、サーバーサイドレンダリングモデル、アクセシビリティ要件。
  • 差別化:このインターフェースを、デフォルトではなく、丁寧に作り込まれたものと感じさせる要素は何ですか?ユーザーが気づく細部は何ですか?

重要:明確な方向性を選び、一貫して実現してください。色、余白、タイポグラフィの選択はすべて、同じ美的方向性を強めるものにしてください。一貫性のあるシンプルなデザインは、まとまりのない複雑なデザインに常に勝ります。

次に、以下の条件を満たす動作するコード(Java + CSS)を実装してください。

  • 本番環境に適した、機能するコードであること
  • 明確な審美眼に基づいた、視覚的に一貫性のあるデザインであること
  • Vaadin のコンポーネントライブラリとテーマシステムを活用していること
  • 余白、色、タイポグラフィ、視覚的な階層が洗練されていること

Vaadin テーマシステム

視覚的なカスタマイズは、選択したテーマ(Aura または Lumo)から始めます。どちらも以下を提供します。

  1. CSS カスタムプロパティ — 色、タイポグラフィ、余白、サイズ、境界線、影の基盤です。これらをオーバーライドして、アプリケーション全体の外観を変更します。
  2. コンポーネントのテーマバリアント — あらかじめ用意された視覚的なバリエーション(プライマリー、ターシャリ、コンパクトなど)です。カスタム CSS を書く前に使用してください。
  3. カスタム CSS — テーマで対応できない部分に使用します。ビューにスコープを限定した CSS ファイルには @StyleSheet を使用します。

Lumo では、さらに以下も利用できます。

  1. ユーティリティクラス — レイアウト、余白、色に使える Tailwind のようなクラス(LumoUtility.*)です。Java からすばやく適用できます。Aura では使用できません。

常に上位の設定から順に作業してください。コンポーネント固有の CSS を書く前にテーマプロパティを変更し、カスタムスタイルを書く前にテーマバリアントを使います。

テーマの選択、読み込み、すべてのデザイントークンの詳細については、theming スキルを参照してください。

タイポグラフィ

タイポグラフィはインターフェース全体のトーンを決定します。

フォントファミリーのカスタマイズ(どちらのテーマでも使用できます):

/* In styles.css */
@font-face {
    font-family: 'Your Font';
    src: url('./fonts/your-font.woff2') format('woff2');
    font-display: swap;
}

html {
    /* Aura */
    --aura-font-family: 'Your Font', sans-serif;
    /* Lumo */
    --lumo-font-family: 'Your Font', sans-serif;
}

文字サイズのスケール調整:

Aura — 1 つの値を調整すると、スケール全体が計算されます。

html {
    --aura-base-font-size: 15;  /* unitless number, represents px */
}

Lumo — 個々のトークンをオーバーライドします。

html {
    --lumo-font-size-xxxl: 2.5rem;
    --lumo-font-size-xxl: 1.75rem;
    --lumo-font-size-xl: 1.375rem;
    --lumo-font-size-l: 1.125rem;
    --lumo-font-size-m: 1rem;
    --lumo-font-size-s: 0.875rem;
    --lumo-font-size-xs: 0.8125rem;
    --lumo-font-size-xxs: 0.75rem;
}

Java からタイポグラフィの階層を作成する(Lumo ユーティリティクラス):

// Lumo theme only — requires Lumo.UTILITY_STYLESHEET
H2 title = new H2("Dashboard");
title.addClassNames(
    LumoUtility.FontSize.XXLARGE,
    LumoUtility.FontWeight.BOLD,
    LumoUtility.TextColor.HEADER
);

Span subtitle = new Span("Weekly performance overview");
subtitle.addClassNames(
    LumoUtility.FontSize.MEDIUM,
    LumoUtility.TextColor.SECONDARY
);

Aura では、LumoUtility の代わりに CSS クラスまたはインラインスタイルを使用してください。

重要な原則:明確な階層を作ります。主となる見出しスタイル、本文スタイル、補助・キャプション用スタイルをそれぞれ 1 つずつ用意してください。フォントサイズ、太さ、色を組み合わせて、階層ごとに差を付けます。1 つのビューで、異なるテキストスタイルを 3~4 種類より多く使わないでください。

色とテーマ設定

色は、Vaadin アプリに独自のアイデンティティを与える最も手早い方法です。

Aura — アクセントとパレットのカスタマイズ:

html {
    --aura-accent-color-light: hsl(220, 80%, 50%);
    --aura-accent-color-dark: hsl(220, 85%, 65%);
    --aura-background-color-light: hsl(220, 20%, 98%);
    --aura-background-color-dark: hsl(220, 20%, 12%);
    --aura-blue: hsl(220, 80%, 50%);
    --aura-green: hsl(150, 60%, 40%);
    --aura-red: hsl(0, 75%, 55%);
}

Lumo — カラーパレットの上書き:

html {
    /* Primary color — used for buttons, links, focus rings */
    --lumo-primary-color: hsl(220, 80%, 50%);
    --lumo-primary-color-50pct: hsla(220, 80%, 50%, 0.5);
    --lumo-primary-color-10pct: hsla(220, 80%, 50%, 0.1);
    --lumo-primary-text-color: hsl(220, 80%, 45%);
    --lumo-primary-contrast-color: #fff;

    /* Surface colors — backgrounds, cards, dialogs */
    --lumo-base-color: hsl(220, 20%, 98%);

    /* Error, success, warning */
    --lumo-error-color: hsl(0, 75%, 55%);
    --lumo-success-color: hsl(150, 60%, 40%);
    --lumo-warning-color: hsl(40, 95%, 50%);
}

ダークモード:

どちらのテーマも @ColorScheme アノテーションをサポートしています。カスタムのダークモード用カラーについては、テーマ固有のセレクター(Lumo の [theme~="dark"]、Aura の -light/-dark サフィックス付きプロパティ)について、テーマ設定スキルを参照してください。

Java からのアクセントカラーとセマンティックカラーの適用(Lumo ユーティリティクラス):

// Lumo theme only
badge.addClassNames(
    LumoUtility.Background.PRIMARY,
    LumoUtility.TextColor.PRIMARY_CONTRAST
);

warningCard.addClassNames(
    LumoUtility.Background.WARNING_10PCT,
    LumoUtility.TextColor.WARNING
);

重要な原則:統一感のあるパレットを選びましょう。強いプライマリカラーまたはアクセントカラーを 1 つ選び、テーマのバリアントシステム(Lumo の不透明度バリアント、Aura の計算済みバリアント)を二次的な用途に使用します。均等に配置された虹色よりも、プライマリカラーを主役にし、アクセントカラーを控えめに使う方が効果的です。

余白と密度

一貫した余白は、視覚的なリズムとプロフェッショナルな印象を生み出します。

Aura — 基本サイズの調整:

html {
    --aura-base-size: 18;     /* unitless, range 12–24 */
    --aura-base-radius: 8;    /* unitless, range 0–10 */
}

Lumo — 個別の余白トークンの上書き:

html {
    --lumo-space-xs: 0.25rem;
    --lumo-space-s: 0.5rem;
    --lumo-space-m: 1rem;
    --lumo-space-l: 1.5rem;
    --lumo-space-xl: 2.5rem;
}

Java からの余白の適用(Lumo ユーティリティクラス):

// Lumo theme only
card.addClassNames(
    LumoUtility.Padding.LARGE,
    LumoUtility.Gap.MEDIUM
);

section.addClassNames(
    LumoUtility.Padding.Horizontal.LARGE,
    LumoUtility.Padding.Vertical.XLARGE
);

コンパクト/高密度のバリアント:

// Cross-theme variants — work in both Aura and Lumo
grid.addThemeVariants(GridVariant.NO_BORDER);
textField.addThemeVariants(TextFieldVariant.SMALL);
button.addThemeVariants(ButtonVariant.SMALL);

// Lumo-only: compact grid
grid.addThemeVariants(GridVariant.LUMO_COMPACT);

重要な原則:密度を決めたら、一貫して維持しましょう。データ密度の高いダッシュボードは、全体をコンパクトにします。ゆとりのあるマーケティング向け画面は、全体に余裕を持たせます。密度を混在させると、意図しない印象になります。

影、境界線、エレベーション

エレベーションを使って奥行きと視覚的な階層を作りましょう。

Aura — サーフェスレベルシステム:

.elevated-card {
    background: var(--aura-surface-color);
    --aura-surface-level: 2;
}

.sunken-area {
    background: var(--aura-surface-color);
    --aura-surface-level: -1;
}

Lumo — 明示的なシャドウトークン:

html {
    --lumo-box-shadow-xs: 0 1px 2px 0 rgba(0,0,0,0.05);
    --lumo-box-shadow-s: 0 2px 4px -1px rgba(0,0,0,0.1);
    --lumo-box-shadow-m: 0 4px 8px -2px rgba(0,0,0,0.1);
    --lumo-box-shadow-l: 0 8px 16px -4px rgba(0,0,0,0.15);
    --lumo-box-shadow-xl: 0 16px 32px -8px rgba(0,0,0,0.2);
}
// Lumo theme only
card.addClassNames(
    LumoUtility.BoxShadow.SMALL,
    LumoUtility.BorderRadius.MEDIUM
);

角丸のカスタマイズ:

/* Lumo */
html {
    --lumo-border-radius-s: 4px;
    --lumo-border-radius-m: 8px;
    --lumo-border-radius-l: 16px;
}

/* Aura — adjust the base radius instead */
html {
    --aura-base-radius: 8;  /* computes all radius values */
}

区切り用の控えめな境界線(Lumo ユーティリティクラス):

// Lumo theme only
section.addClassNames(
    LumoUtility.Border.BOTTOM,
    LumoUtility.BorderColor.CONTRAST_10
);

重要な原則:階層を伝えるために、エレベーションを一貫して使いましょう。カードはサーフェスより浮かび上がり、ダイアログはカードより浮かび上がります。平面的な要素に濃い影を付けたり、浮かび上がった要素に平面的な影を付けたりしないでください。

モーションとアニメーション

Vaadin のサーバーサイドモデルでは、アニメーションは CSS で実装する必要があります。控えめに、目的を持たせましょう。

CSS によるビューのトランジション:

/* Fade-in for view content */
.fade-in {
    animation: fadeIn 0.3s ease-out;
}

@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(8px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Staggered reveal for list items */
.stagger-item {
    animation: fadeIn 0.3s ease-out backwards;
}

.stagger-item:nth-child(1) { animation-delay: 0.05s; }
.stagger-item:nth-child(2) { animation-delay: 0.1s; }
.stagger-item:nth-child(3) { animation-delay: 0.15s; }
.stagger-item:nth-child(4) { animation-delay: 0.2s; }
.stagger-item:nth-child(5) { animation-delay: 0.25s; }
content.addClassName("fade-in");

// Staggered card reveal
for (Component card : cards) {
    card.addClassName("stagger-item");
}

ホバーとインタラクションの効果:

.interactive-card {
    transition: box-shadow 0.2s ease, transform 0.2s ease;
}

.interactive-card:hover {
    box-shadow: 0 4px 8px -2px rgba(0,0,0,0.1);  /* or use theme token */
    transform: translateY(-2px);
}

重要な原則:ページ読み込み時に段階的に表示する演出など、丁寧に作り込んだアニメーションを 1 つか 2 つ使う方が、あちこちに細かなインタラクションを散りばめるよりも効果的です。複雑なキーフレームアニメーションより、ホバー/フォーカス時の CSS トランジションを優先してください。再生時間は 400ms 未満に保ちましょう。

洗練されたコンポーネントの作成

スタイル付きカード(Lumo ユーティリティクラス)
// Lumo theme only — uses LumoUtility classes
public static Div createCard(String title, String value, String description) {
    Div card = new Div();
    card.addClassNames(
        LumoUtility.Background.BASE,
        LumoUtility.BorderRadius.MEDIUM,
        LumoUtility.BoxShadow.SMALL,
        LumoUtility.Padding.LARGE,
        LumoUtility.Display.FLEX,
        LumoUtility.FlexDirection.COLUMN,
        LumoUtility.Gap.SMALL
    );

    Span titleSpan = new Span(title);
    titleSpan.addClassNames(
        LumoUtility.FontSize.SMALL,
        LumoUtility.TextColor.SECONDARY,
        LumoUtility.FontWeight.MEDIUM
    );

    Span valueSpan = new Span(value);
    valueSpan.addClassNames(
        LumoUtility.FontSize.XXLARGE,
        LumoUtility.FontWeight.BOLD,
        LumoUtility.TextColor.HEADER
    );

    Span descSpan = new Span(description);
    descSpan.addClassNames(
        LumoUtility.FontSize.SMALL,
        LumoUtility.TextColor.TERTIARY
    );

    card.add(titleSpan, valueSpan, descSpan);
    return card;
}

Aura では、LumoUtility の代わりに Aura のサーフェスシステムを使う CSS クラスを使用してください。

.metric-card {
    background: var(--aura-surface-color);
    --aura-surface-level: 2;
    border-radius: var(--vaadin-border-radius-m);
    padding: var(--vaadin-padding);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
ステータスバッジ

Badge は Vaadin 25.1 のプレビュー機能です。badgeComponent 機能フラグを有効にして使用してください。利用可能なバリアント:SUCCESS、WARNING、ERROR、FILLED、ICON_ONLY、NUMBER_ONLY。

// Badge component (Vaadin 25.1+ preview — requires badgeComponent feature flag)
Badge pending = new Badge("Pending");

Badge confirmed = new Badge("Confirmed");
confirmed.addThemeVariants(BadgeVariant.SUCCESS);

Badge warning = new Badge("Warning");
warning.addThemeVariants(BadgeVariant.WARNING);

Badge denied = new Badge("Denied");
denied.addThemeVariants(BadgeVariant.ERROR);

// With icon
Badge iconBadge = new Badge("Confirmed", VaadinIcon.CHECK.create());
iconBadge.addThemeVariants(BadgeVariant.SUCCESS);

// With number
Badge counter = new Badge("Inbox", 12);
counter.addThemeVariants(BadgeVariant.FILLED);

Vaadin 25.1 より前のバージョンでは、theme="badge" 属性を使用した Span 要素でバッジを作成できますが、この方法は非推奨です。

データ密度の高いダッシュボードレイアウト(Lumo ユーティリティクラス)
// Lumo theme only — uses LumoUtility classes
VerticalLayout dashboard = new VerticalLayout();
dashboard.setPadding(true);
dashboard.setSpacing(false);
dashboard.addClassNames(
    LumoUtility.Gap.LARGE,
    LumoUtility.Background.CONTRAST_5
);

// Metric cards grid — uses CSS Grid for responsive layout
Div metrics = new Div(
    createCard("Revenue", "$48,200", "+12% from last month"),
    createCard("Users", "1,420", "+5% from last month"),
    createCard("Orders", "384", "+8% from last month")
);
metrics.addClassName("metrics-grid");
// Companion CSS:
// .metrics-grid {
//     display: grid;
//     grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
//     gap: var(--lumo-space-m);
//     width: 100%;
// }

dashboard.add(createSectionHeader("Overview"), metrics);

CSS による Vaadin コンポーネントのスタイル設定

テーマプロパティやテーマバリアントでは不十分な場合は、CSS を使用してください。

Shadow DOM と ::part() セレクター — Vaadin コンポーネントは Shadow DOM を使用します。::part() で内部パーツをスタイル設定します。

/* Style Grid header cells */
vaadin-grid::part(header-cell) {
    background-color: var(--lumo-contrast-5pct);  /* Lumo */
    /* or for Aura: --aura-surface-level: 0; */
    font-weight: 600;
}

/* Style Dialog overlay */
vaadin-dialog-overlay::part(overlay) {
    border-radius: 16px;
}

/* Style TextField input */
vaadin-text-field::part(input-field) {
    border-radius: 8px;
}

コンポーネントスコープのスタイル — 特定のビューに CSS を適用します。

@StyleSheet("styles/views/dashboard-view.css")
@Route("dashboard")
public class DashboardView extends VerticalLayout {
    // ...
}

重要な原則:CSS にハードコードした値を使うより、テーマのカスタムプロパティを常に優先してください。色、余白、サイズには、使用中のテーマのトークンを使います。これにより、テーマとの一貫性が保たれ、ダークモードや密度の変更、将来のデザイン変更にも簡単に対応できます。

よくあるアンチパターン

  1. 色やサイズのハードコード — 常にテーマのカスタムプロパティを使用してください。値をハードコードすると、ダークモードが機能しなくなり、テーマの変更もできなくなります。
  2. テーマプロパティではなくコンポーネント内部を上書きする — 色がおかしい場合は、個々のコンポーネントの CSS ではなく、テーマの色トークンを上書きしてください。プロパティを 1 つ変更するだけで、アプリ全体を更新できます。
  3. 視覚スタイルが多すぎる — カードのスタイルを 2~3 種類、テキストの階層を 2~3 種類、主要なアクションのスタイルを 1 種類に絞ってください。一貫性がプロフェッショナルな印象を生みます。
  4. テーマのバリアントを無視する — カスタム CSS を書く前に、ButtonVariant、GridVariant、TextFieldVariant などを確認してください。必要なバリアントは、すでに用意されている可能性があります。
  5. サーバー描画による更新に派手なアニメーションを使う — Vaadin はサーバーから再描画されます。サーバープッシュのたびに複雑な登場アニメーションを使うと、ぎこちなく見えます。アニメーションは初回のページ読み込みやユーザー操作による画面遷移に使い、データ更新のたびには使わないでください。
  6. 視覚的な階層がない — すべてが同じように見えると、何も目立ちません。サイズ、太さ、色、エレベーションを使って、重要な情報に視線を誘導してください。

ベストプラクティス

  1. テーマプロパティから始める — まずテーマのコアプロパティをカスタマイズしてください。それだけで見た目を大きく変えられます。
  2. コンポーネントのテーマバリアントを使う — プライマリー、テリシャリー、コンパクト、バッジのテーマを使ってください。組み込みのものを活用しましょう。
  3. 一貫した配色方針を保つ — プライマリーカラーまたはアクセントカラーを 1 色、追加の色を 1~2 色、ステータス用のセマンティックカラーを使ってください。
  4. エレベーションを意識してデザインする — カード、ダイアログ、メニューには重なりを感じさせる必要があります。テーマのエレベーションシステムを一貫して使ってください。
  5. モーションは控えめに加える — ホバー時の CSS トランジション、ページ読み込み時のフェードイン、カードが順に表示される演出などを使ってください。400ms 未満に収めてください。
  6. ダークモードでテストする — 色をカスタマイズした場合は、ライトテーマとダークテーマの両方で確認してください。テーマプロパティを使用すれば、この確認を自動化できます。

詳細リファレンス

コンポーネントの ::part() セレクター、CSS アニメーションのレシピ、カラーパレットのレシピについては、references/design-patterns.md を参照してください。テーマトークンの完全な表とバリアントの比較については、theming スキルの references/theming-patterns.md を参照してください。

コメント