Bootstrap 5.2でFlask版ToDoリストの雛形を作成するには【CSS, Bootstrap】
ここでは「 Bootstrap 5.2」のフロントエンドフレームワークを使用して「ToDoリスト」のWebページを作成する手順を解説します。
Bootstrapは人気のあるフロントエンドフレームワークで世界のWebサイトの約25%がこのプレームワークを利用していると言われています。
ここではHTMLとBootstrapのCSSを使用して静的なWebページを作成します。
「Python + Flask + SQLite3」を使用してデータベースと連動するデータドリブン型の動的(ダイナミック)なWebページを作成する方法については
「記事(Article107)」で解説しています。
なお、この記事ではMicrosoftのVisual Studio Code (VS Code)を使用しますが、他のIDEツールを使用することも可能です。
Bootstrap5.2で「ToDoリスト」のWebページを作成する
-
新規HTMLファイルを作成してBootstrap 5.2のCSSとJavaScriptを取り込む
Visual Studio Code (VS Code)を起動したら新規HTMLフィアルを作成して行1-29を入力(コピペ)します。
VS CodeからHTMLファイルのテンプレートを作成するには「!」を入力して[Tab]を押します。
BootstrapのCSSとJavaScriptは、Booststrapトップページの
「Include via CDN」
からコピペします。
行9ではBootstrapのCSSを取り込んでいます。
BootstrapのCSSはHeadセクションに挿入します。
行26ではBootstrapのJavaScriptを取り込んでいます。
BootstrapのJavaScriptはBodyセクションの最後に挿入します。
Bootstrapの「Version 5」からはjQueryが不要になっています。
行13-23ではBodyセクションに「header, main, footer」セクションを配置しています。
ここでは「style」属性を追加して「border」を表示しています。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flask ToDo App #0</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
</head>
<body>
<header class="mb-2 " style="height: 100px; border: 1px solid orangered">
header
</header>
<main class="mb-2" style="height: 500px; border: 1px solid orangered">
main
</main>
<footer style="height: 100px; border: 1px solid orangered">
foooter
</footer>
<!-- Bootstrap JavaScipt (No jQuery) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
</body>
</html>
図1はWebページをブラウザに表示したときの画像です。
ここでは「VS Code」の拡張機能「Live Server」を利用してブラウザに表示しています。
「Live Server」のインストール手順については
「記事(Article073)」で解説しています。
注:
ここではWebページをGoogleのChromeに表示しています。
Chromeには拡張機能「Dark Reader」を組み込んで「ダークテーマ」を適用しています。
この場合は全てのWebページに目に優しい「ダークテーマ」が適用されます。
-
Webページのヘッダーとフッターを固定する
行12-14では、headerセクションにnavタグを追加しています。
navタグにはclass属性を追加してBootstrapのCSSクラス「fixed-top」を追加してheaderセクションを固定させています。
この場合、ブラウザ上でWebページを上下にスクロールしてもheaderセクションが常に画面の先頭に表示されます。
行29ではfooterのclass属性にBootstrapのCSSクラス「fixed-bottom」を追加してfooterセクションを固定させています。
この場合、ブラウザ上でWebページを上下にスクロールしてもfooterセクションは常に画面の終端に表示されます。
行25-27ではdivタグを追加してブラウザ上で画面を上下にスクロールしたときの不具合を回避しています。
ここではdivタグにstyle属性を追加してCSSの「height: 100px;」を追加しています。
heightの値はfooterセクションの高さ(height)と同じにします。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flask ToDo App #1</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
</head>
<body>
<header class="mb-2 text-bg-success " style="height: 100px; border: 1px solid orangered">
<nav class="navbar fixed-top">
Home Todo Goal Philosophy (fixed-top)
</nav>
</header>
<main class="position-relative mb-2" style="height: 500px; border: 1px solid orangered">
<div class="position-absolute top-0 start-0 bg-danger">main (top)</div>
<div class="position-absolute bottom-0 end-0 bg-danger">main (bottom)</div>
</main>
<!-- change to visible to invisible -->
<div class="visible" style="height: 100px; border: 1px solid orangered">
margin to fix fixed-bottom
</div>
<footer class="page-footer fixed-bottom bg-warning" style="height: 100px; border: 1px solid orangered">
foooter (fixed-bottom)
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
</body>
</html>
図2-1はWebページをブラウザに表示した画面です。
mainセクションの上下に「main(top)」と「main(bottom)」が表示されています。
これはブラウザ上で画面を上下にスクロールしたときに表示されるか確認するために使います。
図2-2はブラウザの画面の高さを縮めて画面を上下にスクロールさせたときの画面です。
画面を最下位にスクロールするとmainセクションの「main(bottom)」が表示されています。
ちみに、行25-27のdivタグをコメントにしてブラウザに表示すると最下位にスクロールしても「main(bottom)」が表示されません。
理由はmainセクションの最下位がfooterセクションと重なっているからです。
-
Webページのヘッダーにメニューを配置する
ここではWebページのheaderセクションにメニューを配置します。
Bootstrapのheaderのサンプルは
「headers」
に掲載されています。
また、サンプルのソースコードは
「Download source code」からダウンロードすることができます。
行12-50ではheaderセクションにメニューを追加しています。
このメニューは「レスポンシブデザイン」になっているのでスマホなどに表示するとメニューが自動的に変化します。
詳細は図3-1~図3-4で説明します。
行14ではanchorタグで絵文字「📝」を表示しています。
絵文字をクリックすると「Home」ページが表示されるようにします。
行15-17ではハンバーガーボタン(hamburger button)を配置しています。
スマホなどに表示すると右端にハンバーガーボタンが表示されます。
行19-43ではメニュー「Home, Todo, Goal, Philosophy, GO」を配置しています。
行36-41では「GO」をクリックしたときのドロップダウンメニューを配置しています。
行44-47では検索用のテキストボックスとボタンを配置しています。
TIP1:
HTMLのタグとclass属性のCSSクラスを素早く入力するには[tab]キーを使用します。
たとえば「<nav class="navbar fixed-top bg-dark">」を入力するには、
「nav.navbar.fixed-top.bg-dark」を入力して[tab]キーを押します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flask ToDo App #2</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
</head>
<body>
<header>
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">📝</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Todo</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Goal</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Philosophy</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
GO
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Action 1</a></li>
<li><a class="dropdown-item" href="#">Action 2</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Action 9</a></li>
</ul>
</li>
</ul>
<form class="d-flex" role="search">
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
</div>
</nav>
</header>
<main class="mt-5" style="height: 500px; border: 1px solid orangered">
<hr>
<p>Main section content</p>
</main>
<div class="invisible" style="height: 100px; border: 1px solid orangered">
margin to fix fixed-bottom
</div>
<footer class="page-footer fixed-bottom bg-warning" style="height: 100px; border: 1px solid orangered">
foooter (fixed-bottom)
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
</body>
</html>
図3-1はWebページをブラウザに表示した画面です。
ここでは全てのメニューと検索ボタンが表示されています。
図3-2はブラウザの横幅を狭めてスマホに表示させたようにしています。
メニューのアイテム「Home, Todo, Goal, Philosophy, GO」と検索ボタンが消えて代わりにハンバーガーボタンが表示されています。
図3-3はハンバーガーボタンをクリックしたときの画面です。
メニューアイテムが縦型で表示されています。
図3-4は[GO]ボタンをクリックしてドロップダウンメニューを表示させた画面です。
-
Webページのフッターに著作権を配置する
ここではfooterセクションに著作権等の表示を追加します。
Bootstrapのfooterのサンプルは
「footer」に掲載されています。
また、サンプルのソースコードは
「Download source code」からダウンロードすることができます。
行20-26ではfooterセクションに著作権の表示を追加しています。
行20のfooterタグのclass属性にはBootstrapのCSSクラス「page-footer fixed-bottom font-small bg-dark」等を追加しています。
行23のCopyrightの[(C)」は「©」のように記述します。
anchorタグのclass属性にはBootstrapのCSSクラス「text-reset text-decoration-none」を追加しています。
「text-reset」はanchorのカラーをデフォルトに戻します。
「text-decoration-non」はリンクの下線を非表示にします。
行16ではfooterセクションの高さに合わせてstyle属性の「hight」を「40px」に変更しています。
class属性にはBootstrapのCSSクラス「invisible」を追加して不可視にしています。
TIP2:
Bootstrapのクラス名は「{property}{sides}-{size}」または「{property}{sides}-{breakpoint}-{size}」
のようなフォーマットになっています。
「property」には「m - margin」「p - padding」を指定します。
「sides」には「t - top」「b - bottom」「s - left」「e - right」「x - left & right」「y - top & bottom」を指定します。
「size」には「0 - 5」の数値または「auto」を指定します。
<!DOCTYPE html>
<html lang="en">
<head>
:::
</head>
<body>
<header>
:::
</header>
<main class="mt-5" style="height: 500px; border: 1px solid orangered">
<hr>
<p>Main section content</p>
</main>
<div class="invisible" style="height: 40px; border: 1px solid orangered">
margin to fix fixed-bottom
</div>
<footer class="page-footer fixed-bottom font-small bg-dark py-1">
<div class="container text-center">
<span class="text-muted">
Copyright © <a class="text-reset text-decoration-none" href="https://money-or-ikigai.com/" target="_blank">Akio Kasai</a>
</span>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
</body>
</html>
図4はWebページをブラウザに表示させた画面です。
footerセクションに著作権が表示されています。
ここでは「Akio Kasai」をクリックすると著者のWebサイトに移動するようにしています。
-
WebページのメインにToDoリストを配置する
ここではWebページのmainセクションにToDoリストを配置します。
mainセクションのサンプルは
「Examples」に掲載されています。
サンプルのソースコードもダウンロードすることができます。
行12-69ではmainセクションにToDoリストを配置しています。
行13-14ではWebサイトのタイトルを配置しています。
行16-19ではToDoリストを入力するテキストボックスとボタンを配置しています。
行21-68ではtableタグを配置してToDoリストを表示させています。
TIP3:
mainセクションにダミーテキストを表示するにはLorem ipsum(ロレム・イプサム)を使うと便利です。
VS CodeからLoremを入力するには「Lorem」を入力して[tab」キーを押します。
Loremの長さを指定するときは「Lorem100」のように入力して[tab]キーを押します。
この場合100文字分のダミーテキストが生成されます。
「Lorem50」「Lorem150」「Lorem500」などと入力して色々試してみてください。
<!DOCTYPE html>
<html lang="en">
<head>
:::
</head>
<body>
<header>
:::
</header>
<main class="mt-5">
<div class="container pt-5">
<h1 class="text-center">🎉Flask ToDo App📝</h1>
<h2 class="text-center fs-6"> (ToDo|Task|備忘録 ウェブアプリ)</h2>
<hr />
<form class="d-flex" action="/add" method="post">
<input class="form-control me-2" type="text" placeholder="Enter to do... (ToDOリストを入力してください...)" aria-label="Add">
<button class="btn btn-primary" style="width: 150px;" type="submit">Add (登録)</button>
</form>
<hr />
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Title<br />(タイトル)</th>
<th scope="col">Created<br />(作成日)</th>
<th scope="col">Updated<br />(更新日)</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>
<span class="fs-5">👨💻To do title 1... Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit officia, animi nihil eum repudiandae, alias dolore harum, </span><br />
<button type="button" class="btn btn-primary btn-sm position-relative" href="/update/1">
Update (未完▶完了)<span class="position-absolute top-0 start-100 translate-middle badge border border-light rounded-circle bg-danger p-2"><span class="visually-hidden">not completed</span></span>
</button>
<a class="btn btn-danger btn-sm mx-2" href="/delete/1">Delete (削除)</a>
</td>
<td>2022/09/01 12:19:10</td>
<td></td>
</tr>
<tr>
<th scope="row">2<br>✅</th>
<td>
<span class="fs-5">🧑🌾To do title 2... Lorem ipsum dolor, sit amet consectetur adipisicing elit. Provident, quae totam itaque deserunt natus eius iste. Aut quas obcaecati eum saepe, impedit quo asperiores ad ullam hic, doloribus, unde sed!</span><br />
<button type="button" class="btn btn-primary btn-sm disabled position-relative">
Update (完了)<span class="position-absolute top-0 start-100 translate-middle badge border border-light rounded-circle text-bg-success p-2"><span class="visually-hidden">completed</span></span>
</button>
<a class="btn btn-danger btn-sm mx-2" href="/delete/2">Delete (削除)</a>
</td>
<td>2022/09/01 12:20:10</td>
<td>2022/09/01 12:50:20</td>
</tr>
<tr>
<th scope="row">3<br>✅</th>
<td>
<span class="fs-5">🧑💼To do title 3... Lorem ipsum dolor, sit amet consectetur adipisicing elit. Provident, quae totam itaque deserunt natus eius iste. Aut quas obcaecati eum saepe, impedit quo asperiores ad ullam hic, doloribus, unde sed!</span><br />
<button type="button" class="btn btn-primary btn-sm disabled position-relative">
Update (完了)<span class="position-absolute top-0 start-100 translate-middle badge border border-light rounded-circle text-bg-success p-2"><span class="visually-hidden">completed</span></span>
</button>
<a class="btn btn-danger btn-sm mx-2" href="/delete/3">Delete (削除)</a>
</td>
<td>2022/09/01 12:20:10</td>
<td>2022/09/01 12:50:20</td>
</tr>
</tbody>
</table>
</div>
</main>
<div class="invisible" style="height: 40px; border: 1px solid orangered">
:::
</div>
<footer class="page-footer fixed-bottom font-small bg-dark py-1">
:::
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
</body>
</html>
図5-1はWebページをブラウザに表示させた画面です。
Webページのタイトル、テキストボックス、登録ボタンが上位に表示されています。
ToDoリストは表形式で表示されています。
表には「項番、タイトル、作成日、更新日」が表示されます。
DoToリストが完了したときは「項番」のセルに完了のチェック「✅」が表示されます。
表の「タイトル」セルには[Update][Delete]ボタンが表示されます。
ToDoリストが未完のときは[Update(未完▶完了)]ボタンが表示されます。
ToDoリストが完了のときは[Update(完了)]ボタンが表示されます。
完了のときはボタンが「無効(disabled)」にされた状態で表示されます。
[Update(未完▶完了)]ボタンをクリックするとToDoリストが「未完」から「完了」に変わります。
[Delete(削除)]ボタンをクリックするとToDoリストをデータベースから削除します。
これらの処理は後述する「Python + Flask + SQLite3」の記事で詳しく解説します。
TIP4:
行13, 34, 44等で絵文字を使用しています。
VS Codeから絵文字を入力するには拡張機能「emojisense」をインストールすると便利です。
「emojisense」のインストールと使い方については
「記事(Article099)」で解説しています。
図5-2はブラウザの画面の幅を狭めてスマホで表示した状態にしています。
図5-3ではToDoリストを最下位にスクロールさせた画面です。
ToDoリストの最後まで表示されています。
-
Webページのすべてを掲載
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flask ToDo App #4</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
</head>
<body>
<header>
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">📝</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Todo</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Goal</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Philosophy</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
GO
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Action 1</a></li>
<li><a class="dropdown-item" href="#">Action 2</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Action 9</a></li>
</ul>
</li>
</ul>
<form class="d-flex" role="search">
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
</div>
</nav>
</header>
<main class="mt-5">
<div class="container pt-5">
<h1 class="text-center">🎉Flask ToDo App📝</h1>
<h2 class="text-center fs-6"> (ToDo|Task|備忘録 ウェブアプリ)</h2>
<hr />
<form class="d-flex" action="/add" method="post">
<input class="form-control me-2" type="text" placeholder="Enter to do... (ToDOリストを入力してください...)" aria-label="Add">
<button class="btn btn-primary" style="width: 150px;" type="submit">Add (登録)</button>
</form>
<hr />
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Title<br />(タイトル)</th>
<th scope="col">Created<br />(作成日)</th>
<th scope="col">Updated<br />(更新日)</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>
<span class="fs-5">👨💻To do title 1... Lorem ipsum dolor sit amet consectetur adipisicing elit. Reprehenderit officia, animi nihil eum repudiandae, alias dolore harum, </span><br />
<button type="button" class="btn btn-primary btn-sm position-relative" href="/update/1">
Update (未完▶完了)<span class="position-absolute top-0 start-100 translate-middle badge border border-light rounded-circle bg-danger p-2"><span class="visually-hidden">not completed</span></span>
</button>
<a class="btn btn-danger btn-sm mx-2" href="/delete/1">Delete (削除)</a>
</td>
<td>2022/09/01 12:19:10</td>
<td></td>
</tr>
<tr>
<th scope="row">2<br>✅</th>
<td>
<span class="fs-5">🧑🌾To do title 2... Lorem ipsum dolor, sit amet consectetur adipisicing elit. Provident, quae totam itaque deserunt natus eius iste. Aut quas obcaecati eum saepe, impedit quo asperiores ad ullam hic, doloribus, unde sed!</span><br />
<button type="button" class="btn btn-primary btn-sm disabled position-relative">
Update (完了)<span class="position-absolute top-0 start-100 translate-middle badge border border-light rounded-circle text-bg-success p-2"><span class="visually-hidden">completed</span></span>
</button>
<a class="btn btn-danger btn-sm mx-2" href="/delete/2">Delete (削除)</a>
</td>
<td>2022/09/01 12:20:10</td>
<td>2022/09/01 12:50:20</td>
</tr>
<tr>
<th scope="row">3<br>✅</th>
<td>
<span class="fs-5">🧑💼To do title 3... Lorem ipsum dolor, sit amet consectetur adipisicing elit. Provident, quae totam itaque deserunt natus eius iste. Aut quas obcaecati eum saepe, impedit quo asperiores ad ullam hic, doloribus, unde sed!</span><br />
<button type="button" class="btn btn-primary btn-sm disabled position-relative">
Update (完了)<span class="position-absolute top-0 start-100 translate-middle badge border border-light rounded-circle text-bg-success p-2"><span class="visually-hidden">completed</span></span>
</button>
<a class="btn btn-danger btn-sm mx-2" href="/delete/3">Delete (削除)</a>
</td>
<td>2022/09/01 12:20:10</td>
<td>2022/09/01 12:50:20</td>
</tr>
</tbody>
</table>
</div>
</main>
<div class="invisible" style="height: 40px; border: 1px solid orangered">
margin to fix fixed-bottom
</div>
<footer class="page-footer fixed-bottom font-small bg-dark py-1">
<div class="container text-center">
<span class="text-muted">
Copyright © <a class="text-reset text-decoration-none" href="https://money-or-ikigai.com/" target="_blank">Akio Kasai</a>
</span>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
</body>
</html>