design-shiny-ui
About
This skill helps developers design modern Shiny UI interfaces using bslib for theming, layout_columns for responsive grids, and custom CSS/SCSS. It's useful for building new apps from scratch, modernizing legacy fluidPage apps, or applying brand themes while ensuring accessibility and responsive design. The skill handles page layout, accessibility improvements, and brand consistency across different screen sizes.
Quick Install
Claude Code
Recommendednpx skills add pjt222/agent-almanac -a claude-code/plugin add https://github.com/pjt222/agent-almanacgit clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/design-shiny-uiCopy and paste this command in Claude Code to install this skill
Documentation
name: design-shiny-ui description: > テーマ設定にbslib、レスポンシブグリッドにlayout_columns、バリューボックス、 カード、カスタムCSS/SCSSを使ってShinyアプリケーションUIを設計します。 ページレイアウト、アクセシビリティ、ブランドの一貫性を扱います。新しいShinyアプリ UIをゼロから構築するとき、既存のアプリをfluidPageからbslibにモダナイズするとき、 ブランドのテーマを適用するとき、Shinyアプリを様々な画面サイズでレスポンシブにするとき、 またはShinyアプリのアクセシビリティを改善するときに使用します。 locale: ja source_locale: en source_commit: 6f65f316 translator: claude-opus-4-6 translation_date: 2026-03-16 license: MIT allowed-tools: Read Write Edit Bash Grep Glob metadata: author: Philipp Thoss version: "1.0" domain: shiny complexity: intermediate language: R tags: shiny, bslib, ui, theming, layout, css, accessibility, responsive
Shiny UIの設計
bslibのテーマ設定、モダンなレイアウトプリミティブ、カスタムCSSを使ってレスポンシブでアクセシブルなShinyアプリケーションインターフェースを設計します。
使用タイミング
- 新しいShinyアプリUIをゼロから構築するとき
- 既存のShinyアプリをfluidPageからbslibにモダナイズするとき
- Shinyアプリにブランドのテーマ(色、フォント)を適用するとき
- Shinyアプリを様々な画面サイズでレスポンシブにするとき
- Shinyアプリケーションのアクセシビリティを改善するとき
入力
- 必須: アプリケーションの目的とターゲットオーディエンス
- 必須: レイアウトタイプ(sidebar、navbar、fillable、dashboard)
- オプション: ブランドの色とフォント
- オプション: カスタムCSS/SCSSを使用するか(デフォルト:bslibのみ)
- オプション: アクセシビリティ要件(WCAGレベル)
手順
ステップ1: ページレイアウトの選択
bslibはいくつかのページコンストラクターを提供します:
# サイドバーレイアウト — データアプリに最も一般的
ui <- page_sidebar(
title = "My App",
sidebar = sidebar("Controls here"),
"Main content here"
)
# Navbarレイアウト — 複数ページアプリに
ui <- page_navbar(
title = "My App",
nav_panel("Tab 1", "Content 1"),
nav_panel("Tab 2", "Content 2"),
nav_spacer(),
nav_item(actionButton("help", "Help"))
)
# Fillableレイアウト — コンテンツが利用可能なスペースを埋める
ui <- page_fillable(
card(
full_screen = TRUE,
plotOutput("plot")
)
)
# ダッシュボードレイアウト — バリューボックスとカードのグリッド
ui <- page_sidebar(
title = "Dashboard",
sidebar = sidebar(open = "closed", "Filters"),
layout_columns(
fill = FALSE,
value_box("Revenue", "$1.2M", theme = "primary"),
value_box("Users", "4,521", theme = "success"),
value_box("Uptime", "99.9%", theme = "info")
),
layout_columns(
card(plotOutput("chart1")),
card(plotOutput("chart2"))
)
)
期待結果: ページレイアウトがアプリケーションのナビゲーションとコンテンツのニーズと一致します。
失敗時: レイアウトが正しく見えない場合は、fluidPage()/navbarPage()(ベースshiny)ではなくpage_sidebar()/page_navbar()(bslib)を使用しているか確認してください。bslibバージョンはデフォルトとテーマサポートが優れています。
ステップ2: bslibテーマの設定
my_theme <- bslib::bs_theme(
version = 5, # Bootstrap 5
bootswatch = "flatly", # オプションのプリセットテーマ
bg = "#ffffff", # 背景色
fg = "#2c3e50", # 前景(テキスト)色
primary = "#2c3e50", # プライマリブランドカラー
secondary = "#95a5a6", # セカンダリカラー
success = "#18bc9c",
info = "#3498db",
warning = "#f39c12",
danger = "#e74c3c",
base_font = bslib::font_google("Source Sans Pro"),
heading_font = bslib::font_google("Source Sans Pro", wght = 600),
code_font = bslib::font_google("Fira Code"),
"navbar-bg" = "#2c3e50"
)
ui <- page_sidebar(
theme = my_theme,
title = "Themed App",
# ...
)
開発中はインタラクティブなテーマエディタを使用します:
bslib::bs_theme_preview(my_theme)
期待結果: アプリが一貫したブランドの色、フォント、Bootstrap 5コンポーネントでレンダリングされます。
失敗時: フォントが読み込まれない場合は、インターネットアクセスを確認してください(Google Fontsには必要)またはシステムフォントに切り替えてください:font_collection("system-ui", "-apple-system", "Segoe UI")。テーマ変数が適用されない場合は、themeをページ関数に渡しているか確認してください。
ステップ3: カードとカラムを使ったレイアウトの構築
ui <- page_sidebar(
theme = my_theme,
title = "Analysis Dashboard",
sidebar = sidebar(
width = 300,
title = "Filters",
selectInput("dataset", "Dataset", choices = c("iris", "mtcars")),
sliderInput("sample", "Sample %", 10, 100, 100, step = 10),
hr(),
actionButton("refresh", "Refresh", class = "btn-primary w-100")
),
# KPI行 — 非フィリング
layout_columns(
fill = FALSE,
col_widths = c(4, 4, 4),
value_box(
title = "Observations",
value = textOutput("n_obs"),
showcase = bsicons::bs_icon("table"),
theme = "primary"
),
value_box(
title = "Variables",
value = textOutput("n_vars"),
showcase = bsicons::bs_icon("columns-gap"),
theme = "info"
),
value_box(
title = "Missing",
value = textOutput("n_missing"),
showcase = bsicons::bs_icon("exclamation-triangle"),
theme = "warning"
)
),
# メインコンテンツ行
layout_columns(
col_widths = c(8, 4),
card(
card_header("Distribution"),
full_screen = TRUE,
plotOutput("main_plot")
),
card(
card_header("Summary"),
tableOutput("summary_table")
)
)
)
主要なレイアウトプリミティブ:
layout_columns()—col_widthsを持つレスポンシブグリッドcard()— オプションのヘッダー/フッターを持つコンテンツコンテナvalue_box()— アイコンとテーマを持つKPI表示layout_sidebar()— カード内のネストしたサイドバーnavset_card_tab()— タブ付きカード
期待結果: 画面サイズに適応するレスポンシブグリッドレイアウト。
失敗時: 広い画面で列が予期せずスタックする場合は、col_widthsの合計が12(Bootstrapグリッド)になるか確認してください。カードが重なる場合は、非フィリング行にfill = FALSEを設定してください。
ステップ4: ダイナミックUI要素の追加
server <- function(input, output, session) {
output$dynamic_filters <- renderUI({
data <- current_data()
tagList(
selectInput("col", "Column", choices = names(data)),
if (is.numeric(data[[input$col]])) {
sliderInput("range", "Range",
min = min(data[[input$col]], na.rm = TRUE),
max = max(data[[input$col]], na.rm = TRUE),
value = range(data[[input$col]], na.rm = TRUE)
)
} else {
selectInput("values", "Values",
choices = unique(data[[input$col]]),
multiple = TRUE
)
}
)
})
# 条件付きパネル(サーバーラウンドトリップなし)
# UI内:
# conditionalPanel(
# condition = "input.show_advanced == true",
# numericInput("alpha", "Alpha", 0.05)
# )
}
期待結果: UI要素がユーザーの選択とデータに基づいてダイナミックに更新されます。
失敗時: ダイナミックUIがちらつく場合は、可能であればrenderUI()の代わりにconditionalPanel()(CSSベース)を使用してください。再レンダリング時にダイナミックな入力が値を失う場合は、状態を復元するためにsession$sendInputMessage()を追加してください。
ステップ5: カスタムCSS/SCSSの追加(オプション)
bslibのテーマ変数を超えたスタイルのために:
# インラインCSS
ui <- page_sidebar(
theme = my_theme,
tags$head(tags$style(HTML("
.sidebar { border-right: 2px solid var(--bs-primary); }
.card-header { font-weight: 600; }
.value-box .value { font-size: 2.5rem; }
"))),
# ...
)
# 外部CSSファイル(www/ディレクトリに配置)
ui <- page_sidebar(
theme = my_theme,
tags$head(tags$link(rel = "stylesheet", href = "custom.css")),
# ...
)
bslibとのSCSS統合:
my_theme <- bslib::bs_theme(version = 5) |>
bslib::bs_add_rules(sass::sass_file("www/custom.scss"))
期待結果: bslibのテーマを壊さずにカスタムスタイルが適用されます。
失敗時: カスタムCSSがbslibと競合する場合は、ハードコードされた色の代わりにBootstrap CSS変数(var(--bs-primary))を使用してください。これにより、テーマ変更がカスタムスタイルに伝播します。
ステップ6: アクセシビリティの確保
# 入力にARIAラベルを追加
selectInput("category", "Category",
choices = c("A", "B", "C")
) |> tagAppendAttributes(`aria-describedby` = "category-help")
# プロットにaltテキストを追加
output$plot <- renderPlot({
plot(data(), main = "Distribution of Values")
}, alt = "Histogram showing the distribution of selected values")
# テーマで十分な色のコントラストを確保
my_theme <- bslib::bs_theme(
version = 5,
bg = "#ffffff", # 白い背景
fg = "#212529" # 暗いテキスト — 15.4:1のコントラスト比
)
# セマンティックHTMLを使用
tags$main(
role = "main",
tags$h1("Dashboard"),
tags$section(
`aria-label` = "Key Performance Indicators",
layout_columns(
# バリューボックス...
)
)
)
期待結果: アプリが色のコントラスト、キーボードナビゲーション、スクリーンリーダーの互換性についてWCAG 2.1 AA基準を満たします。
失敗時: ブラウザのデベロッパーツールのアクセシビリティ監査(Lighthouse)でテストしてください。WebAIMのコントラストチェッカーで色のコントラスト比を確認してください。すべてのインタラクティブ要素がキーボードでフォーカスできることを確認してください。
バリデーション
- ページレイアウトがデスクトップとモバイルの幅で正しくレンダリングされる
- bslibのテーマがすべてのコンポーネントに一貫して適用される
- バリューボックスが正しいテーマとアイコンで表示される
- カードがレスポンシブグリッドで適切にリサイズされる
- カスタムCSSがハードコードされた値ではなくBootstrap変数を使用する
- すべてのプロットがスクリーンリーダー用のaltテキストを持つ
- 色のコントラストがWCAG AA(テキストに対して4.5:1)を満たす
- インタラクティブ要素がキーボードでアクセス可能
よくある落とし穴
- 古いShiny UIと新しいShiny UIの混在:
fluidPage()とbslibコンポーネントを混在させないでください。page_sidebar()、page_navbar()、またはpage_fillable()だけを使用してください。 - CSSでのハードコードされた色:
#2c3e50の代わりにvar(--bs-primary)を使用してください。テーマが変更されるとハードコードされた色は壊れます。 - 非フィリング行に
fill = FALSEがない: バリューボックス行とサマリー行は通常、利用可能なスペースを埋めるべきではありません。fill = FALSEを設定してください。 - オフライン環境でのGoogle Fonts: アプリがエアギャップネットワークにデプロイされる場合は、
font_google()の代わりにシステムフォントまたはセルフホストのフォントファイルを使用してください。 - モバイルの無視: ブラウザのレスポンシブモードでテストしてください。
layout_columnsは狭い画面で自動的にスタックしますが、カスタムCSSはそうでない場合があります。
関連スキル
scaffold-shiny-app— テーマ設定を含む初期アプリセットアップbuild-shiny-module— モジュラーUIコンポーネントの作成optimize-shiny-performance— パフォーマンスを意識したレンダリングreview-web-design— レイアウト、タイポグラフィ、色のビジュアルデザインレビューreview-ux-ui— ユーザビリティとアクセシビリティのレビュー
GitHub Repository
Related Skills
executing-plans
DesignUse the executing-plans skill when you have a complete implementation plan to execute in controlled batches with review checkpoints. It loads and critically reviews the plan, then executes tasks in small batches (default 3 tasks) while reporting progress between each batch for architect review. This ensures systematic implementation with built-in quality control checkpoints.
requesting-code-review
DesignThis skill dispatches a code-reviewer subagent to analyze code changes against requirements before proceeding. It should be used after completing tasks, implementing major features, or before merging to main. The review helps catch issues early by comparing the current implementation with the original plan.
connect-mcp-server
DesignThis skill provides a comprehensive guide for developers to connect MCP servers to Claude Code using HTTP, stdio, or SSE transports. It covers installation, configuration, authentication, and security for integrating external services like GitHub, Notion, and custom APIs. Use it when setting up MCP integrations, configuring external tools, or working with Claude's Model Context Protocol.
web-cli-teleport
DesignThis skill helps developers choose between Claude Code Web and CLI interfaces based on task analysis, then enables seamless session teleportation between these environments. It optimizes workflow by managing session state and context when switching between web, CLI, or mobile. Use it for complex projects requiring different tools at various stages.
