Flask Webアプリケーションのモバイルレスポンシブ改修事例
プロジェクト概要
案件名: RoutineNavi - タスク管理Webアプリケーションのモバイル対応改修
技術スタック: Flask, Bootstrap 5, HTML/CSS, JavaScript
改修期間: 1日
対応範囲: ダッシュボード画面のモバイルレスポンシブ対応
課題と問題点
初期状態の問題
クライアントから「モバイル版での表示がTOPページ(ダッシュボード)のレイアウトが大きく崩れる」という報告を受けました。
具体的な問題点
- 日付選択カレンダーの表示崩れ
- デスクトップでは正常に6日間が横並び表示
- モバイルでは右端の日付が画面外にはみ出し
-
横スクロールが機能していない
-
画面遷移時のレイアウト不安定
- TOPページでは正常表示
- 他の日付に遷移すると表示が崩れる
-
「今日に戻る」ボタンの動的表示がレイアウトを押し出す
-
タスクリストのボタン配置問題
- モバイルでボタンが重なり、タップしにくい
- 実行履歴情報が見切れる
解決アプローチ
1. モバイル専用カレンダーシステムの実装
<!-- デスクトップ版 -->
<div class="row text-center justify-content-center d-none d-md-flex">
<!-- 従来のBootstrapグリッド -->
</div>
<!-- モバイル版 -->
<div class="d-md-none">
<div class="mobile-calendar-container">
<div class="mobile-calendar-scroll">
{% for day in date_range %}
<div class="mobile-date-item">
<div class="mobile-date-box">
<!-- 日付情報 -->
</div>
</div>
{% endfor %}
</div>
</div>
</div>
2. Flexboxベースの横スクロール実装
.mobile-calendar-container {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
padding: 10px 0;
}
.mobile-calendar-scroll {
display: flex;
gap: 8px;
min-width: max-content;
padding: 0 5px;
}
.mobile-date-item {
flex: 0 0 auto;
min-width: 60px;
}
3. ヘッダーレイアウトの固定化
<div class="page-header-container mb-4">
<div class="d-flex flex-column flex-md-row justify-content-between">
<div class="header-title-section">
<h2>タスク名</h2>
</div>
<div class="header-button-section">
<div class="back-to-today-container">
{% if not is_today %}
<a href="{{ url_for('dashboard') }}" class="btn btn-sm btn-outline-primary">
🏠 今日に戻る
</a>
{% else %}
<!-- 透明プレースホルダー -->
<div class="btn btn-sm btn-outline-primary invisible">
🏠 今日に戻る
</div>
{% endif %}
</div>
</div>
</div>
</div>
4. レスポンシブタスクリストの実装
<!-- デスクトップ用ボタン -->
<div class="btn-group btn-group-sm d-none d-md-flex">
<!-- 横並びボタン -->
</div>
<!-- モバイル用ボタン -->
<div class="d-md-none w-100">
<div class="d-flex flex-wrap gap-1">
<button class="btn btn-outline-success btn-sm flex-fill">✅ 実行</button>
<button class="btn btn-outline-primary btn-sm flex-fill">📅 日付指定</button>
</div>
</div>
技術的な実装詳細
JavaScript初期化処理
function initMobileCalendar() {
const mobileCalendar = document.querySelector('.mobile-calendar-container');
if (mobileCalendar) {
// 選択された日付を中央に表示
const selectedItem = document.querySelector('.mobile-date-box.bg-primary');
if (selectedItem) {
setTimeout(() => {
const container = selectedItem.closest('.mobile-calendar-container');
const containerWidth = container.offsetWidth;
const itemLeft = selectedItem.closest('.mobile-date-item').offsetLeft;
const itemWidth = selectedItem.closest('.mobile-date-item').offsetWidth;
const scrollLeft = itemLeft - (containerWidth / 2) + (itemWidth / 2);
container.scrollLeft = Math.max(0, scrollLeft);
}, 100);
}
}
}
レスポンシブブレークポイント設計
/* タブレット以下 */
@media (max-width: 768px) {
.page-header-container {
min-height: 80px;
}
.mobile-calendar-container {
margin: 0 -0.75rem;
padding: 10px 0.75rem;
}
}
/* スマートフォン */
@media (max-width: 576px) {
.task-item .d-flex {
flex-direction: column;
gap: 0.5rem;
}
}
/* 小型スマートフォン */
@media (max-width: 480px) {
.page-header-container {
min-height: 90px;
}
.mobile-date-item {
min-width: 55px;
}
}
成果と改善効果
Before / After比較
項目 | Before | After |
---|---|---|
カレンダー表示 | 右端切れ・スクロール不可 | 完全横スクロール対応 |
画面遷移安定性 | レイアウト崩れ頻発 | 安定した固定レイアウト |
ボタン操作性 | 重なり・タップ困難 | 最適化された配置 |
UX一貫性 | デバイス間でバラバラ | 統一されたエクスペリエンス |
技術的な改善点
- パフォーマンス向上
- CSS Grid から Flexbox への変更で描画速度向上
-
不要なJS処理の削減
-
保守性の向上
- デスクトップ/モバイルの明確な分離
-
再利用可能なCSSコンポーネント設計
-
アクセシビリティ改善
- タッチターゲットサイズの最適化
- スクリーンリーダー対応の向上
技術的な学びとポイント
1. モバイルファーストの重要性
従来のBootstrapグリッドシステムでは限界があるケースでは、モバイル専用の実装を検討することが重要。
2. レイアウト安定化のテクニック
動的に表示/非表示が切り替わる要素には「透明プレースホルダー」を使用し、レイアウトの一貫性を保つ。
3. デバイス別最適化
画面サイズに応じた細かなブレークポイント設定で、すべてのデバイスで最適な体験を提供。
使用技術詳細
- Frontend: HTML5, CSS3, Bootstrap 5.1.3, Vanilla JavaScript
- Backend: Python Flask, Jinja2 Templates
- Tools: Chrome DevTools, レスポンシブデザインモード
- Testing: 複数デバイス実機検証
今後の展開
この改修をベースに、他のページ(統計画面、タスク管理画面)への同様のモバイル対応を展開予定。また、PWA化も検討中。
この事例が示すのは、既存のWebアプリケーションでも適切な設計と実装により、モバイルユーザビリティを大幅に改善できるということです。特に、レスポンシブデザインは単なる画面サイズ対応ではなく、デバイス特性を理解した専用実装が重要である点が学びとなりました。