どういう仕組みで動いてるのか

普通に使ってる分には内部構造とか知らなくてもいいんだけど、カスタマイズで詰まったり、謎のバグに遭遇した時に「ああ、そういうことね」ってなる話をしとく。深夜3時のデバッグで役に立つ。俺も何回か助けられた。
何で作られてるか
Bootstrapの技術構成:
- CSS: 普通のCSS3 + CSS Variables(CSS Custom Properties)
- JavaScript: jQuery使わないVanilla JavaScript
- Build Tool: Sass
- Grid System: Flexboxがメイン、CSS Gridも使える
Bootstrap 5になって一番大きな変更は、jQuery依存をやめたこと。これでファイルサイズが軽くなったし、他のJavaScript frameworkとの相性も良くなった。
ファイル構成
bootstrap/
├── dist/ # コンパイル済みファイル
│ ├── css/
│ │ ├── bootstrap.min.css # CSS本体(圧縮版)
│ │ └── bootstrap.css # CSS本体(非圧縮版)
│ └── js/
│ ├── bootstrap.bundle.min.js # Popper.js付き
│ └── bootstrap.min.js # Bootstrap JSのみ
├── scss/ # Sassファイル
│ ├── _variables.scss # 色とかサイズの設定
│ ├── _grid.scss # Grid system
│ └── bootstrap.scss # メインファイル

ここでハマる: JavaScriptファイルが2種類あって、ちょいちょい間違える。bootstrap.bundle.min.js
にはPopper.jsが入ってて、bootstrap.min.js
には入ってない。dropdownとかtooltip使って「なんで動かねー」ってなるのは大体Popper.js読み込み忘れ。面倒だからbundle版使っとけ。俺は3回ぐらい同じミスした。
Grid Systemの仕組み

画面サイズの区切り
Bootstrapは画面サイズを6段階に分けてる:
サイズ |
接頭辞 |
画面幅 |
極小 |
なし |
576px未満 |
小 |
sm |
576px以上 |
中 |
md |
768px以上 |
大 |
lg |
992px以上 |
特大 |
xl |
1200px以上 |
超特大 |
xxl |
1400px以上 |
これらのサイズは、実際のデバイス利用統計を元に決められてる。適当じゃない。
Grid Mechanics - 内部動作
.container {
width: 100%;
padding-right: calc(var(--bs-gutter-x) * .5);
padding-left: calc(var(--bs-gutter-x) * .5);
margin-right: auto;
margin-left: auto;
}
.row {
display: flex;
flex-wrap: wrap;
margin-right: calc(-.5 * var(--bs-gutter-x));
margin-left: calc(-.5 * var(--bs-gutter-x));
}
.col {
flex: 1 0 0%;
padding-right: calc(var(--bs-gutter-x) * .5);
padding-left: calc(var(--bs-gutter-x) * .5);
}
CSS Variables (--bs-gutter-x
) を使ってるのがポイント。これで動的にgutter幅を変更可能:
.custom-container {
--bs-gutter-x: 3rem; /* Default: 1.5rem */
}
Advanced Grid Techniques
Auto-layout columns:
<div class="row">
<div class="col">自動で等幅</div>
<div class="col">これも等幅</div>
<div class="col">これも等幅</div>
</div>
Variable width content:
<div class="row">
<div class="col">Content based width</div>
<div class="col-6">固定で50%</div>
<div class="col">残りの幅</div>
</div>
Offset classes:
<div class="col-md-4 offset-md-4">
<!-- 中央寄せ: 4列分 + 4列分のoffset = 中央配置 -->
</div>
CSS Variablesがやっと使えるようになった
Bootstrap 5.3でCSS Variables(CSS Custom Properties)がまともに使えるようになった。公式のCSS変数ガイドやMDBootstrap実装例を見ても分かるけど、これで色とかをJavaScriptで動的に変更できるから、ダークモード切り替えとかが楽になった。やっとまともな時代が来たって感じ。
Color System
:root {
--bs-primary: #0d6efd;
--bs-primary-rgb: 13, 110, 253;
--bs-success: #198754;
--bs-danger: #dc3545;
/* ... */
}
これをJavaScriptで動的に変更可能:
// Dark mode toggle example
document.documentElement.style.setProperty('--bs-primary', '#bb86fc');
document.documentElement.style.setProperty('--bs-body-bg', '#121212');
Component-level Variables
各componentも独自のCSS variablesを持ってる:
.btn {
--bs-btn-padding-x: 0.75rem;
--bs-btn-padding-y: 0.375rem;
--bs-btn-font-family: ;
--bs-btn-font-size: 1rem;
--bs-btn-border-width: 1px;
/* ... */
padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x);
font-family: var(--bs-btn-font-family);
font-size: var(--bs-btn-font-size);
border: var(--bs-btn-border-width) solid var(--bs-btn-border-color);
}
特定のbuttonだけカスタマイズしたい場合:
.custom-btn {
--bs-btn-padding-x: 2rem;
--bs-btn-border-radius: 0;
}
JavaScriptもjQuery地獄から脱出
コンポーネントの使い方
Bootstrap 5のJavaScript APIは2つの方法でJavaScript component動かせる。公式JS詳細ガイドにも書いてあるけど、data attributeでHTMLに書くか、JavaScriptで直接操作するか。大体data attributeの方が楽だから、それ使っとけ:
<!-- Data attributes (推奨) -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#myModal">
Launch modal
</button>
<!-- JavaScript API -->
<script>
const modal = new bootstrap.Modal(document.getElementById('myModal'), {
keyboard: false,
backdrop: 'static'
});
modal.show();
</script>
Event System
全部のcomponentで同じようにeventを扱える。modalが表示される前とか後とか、いろんなタイミングでJavaScript実行できるから便利:
// Modal events
const modalElement = document.getElementById('myModal');
modalElement.addEventListener('show.bs.modal', event => {
// Modal表示直前
console.log('About to show modal');
});
modalElement.addEventListener('shown.bs.modal', event => {
// Modal表示完了後
console.log('Modal is now visible');
});
modalElement.addEventListener('hide.bs.modal', event => {
// Modal非表示直前
if (hasUnsavedChanges()) {
event.preventDefault(); // 非表示をキャンセル
}
});
API Methods
各componentは統一されたAPI pattern:
// Toast component example
const toastElement = document.querySelector('.toast');
const toast = bootstrap.Toast.getOrCreateInstance(toastElement);
toast.show(); // 表示
toast.hide(); // 非表示
toast.dispose(); // イベントリスナー削除 + データ削除

Utility Classes - Tailwindの前からやってたやつ
Bootstrapは実はTailwindより5年も前からutility classes使ってた。「Tailwindが最初」とか思ってる奴いるけど、Bootstrapの方が先輩なんだよ:
Spacing System
<!-- Margin/Padding: {property}{sides}-{size} -->
<div class="m-3"> <!-- margin: 1rem -->
<div class="px-4"> <!-- padding-left/right: 1.5rem -->
<div class="mb-auto"> <!-- margin-bottom: auto -->
Display Utilities
<div class="d-none d-md-block"> <!-- Mobile: hidden, Desktop: block -->
<div class="d-flex justify-content-center align-items-center">
Color Utilities
<p class="text-primary">Primary text</p>
<div class="bg-danger text-white">Danger background</div>
<a href="#" class="link-success">Success link</a>
Customization Strategies:カスタマイズの極意
Sass Variables Override
Bootstrap 5 Sass カスタマイズガイドや実践的Sass変数活用法を参考に:
// custom.scss
$primary: #8b5a3c;
$enable-rounded: false;
$enable-shadows: true;
$enable-gradients: true;
@import "bootstrap/scss/bootstrap";
重要: 公式Sassドキュメントにも書いてあるけど、variablesのoverridは@import
の前に書くこと。
実際にハマった話
CSS Variablesでハマった地獄体験
クライアントの「ダークモードも対応できますか?」でBootstrap 5でダークテーマ作ろうと思って、プライマリカラー変更したら意味不明な現象に遭遇。
:root {
--bs-primary: #8B5A3C; /* プライマリ色変更 */
}
ボタンの通常時は#8B5A3Cで正しく表示されるのに、:hover
したらrgba(13, 110, 253, 0.25)
みたいな青っぽい半透明色になって「は?なんじゃこりゃ」ってなった。開発者ツールのComputedタブで調べたら、Bootstrap内部で--bs-primary-rgb
っていうRGB値の変数も使ってて、こっちはデフォルトの13, 110, 253
(青)のまま。
hover時の計算がrgba(var(--bs-primary-rgb), 0.25)
になってるから、RGBが青のまま→青い半透明になってた。なんで同じ色をHEXとRGBで二重管理なんだよ。
結局RGBも手動で計算して更新:
:root {
--bs-primary: #8B5A3C;
--bs-primary-rgb: 139, 90, 60; /* #8B5A3CをRGBに変換 */
}
でもこの計算間違えやすすぎる。#8B5A3C
→「8Bは139、5Aは90、3Cは60」ってHEXからRGBの変換を電卓で計算して、3回間違えて、最終的にオンラインツール使った。なんでBootstrapはHEXからRGB自動計算してくれないんだ。2025年にもなって手動計算とかマジでありえない。
しかも全部のカラー(secondary、success、danger...)について同じことやらないといけなくて、結局Sass関数作った:
@function hex-to-rgb($hex) {
@return red($hex), green($hex), blue($hex);
}
最初からこれ使えって話だよ。
Popper.jsで発狂しそうになった件
新人が「dropdownが動きません、コンソールに変なエラー出てます」って言うから見に行ったら、案の定JSファイル読み込みがめちゃくちゃ:
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
コンソール見たら:
Uncaught TypeError: Cannot read properties of undefined (reading 'createPopper')
Uncaught ReferenceError: Popper is already defined
TypeError: bootstrap.Dropdown is not a constructor
bootstrap.bundle.min.js:6 Uncaught TypeError: Popper.createPopper is not a function
とか意味不明なエラー山盛り。bundle版にはすでにPopper.js入ってるのに、わざわざ別で読み込んでて競合してた。片方消したら秒で治った。
新人あるあるだけど、これで半日溶かした。しかも俺も初回は同じミスしてたから何も言えない。ファイル名見りゃ分かるだろ...って思うけど、確かに分かりづらいよな。公式ドキュメントもこの辺りの説明微妙だし。
Component Partial Import
全部は要らない場合:
// Required
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/root";
// Optional - 必要なものだけ
@import "bootstrap/scss/grid";
@import "bootstrap/scss/buttons";
@import "bootstrap/scss/forms";
@import "bootstrap/scss/navbar";
Bundle sizeを50%以上削減可能。
Custom Utilities
独自のutility classesも追加可能:
$utilities: map-merge(
$utilities,
(
"cursor": (
property: cursor,
class: cursor,
values: auto pointer grab grabbing not-allowed
),
"user-select": (
property: user-select,
class: user-select,
values: all auto none
)
)
);
これで.cursor-pointer
、.user-select-none
が使えるようになる。
パフォーマンスの現実
実際のファイルサイズ
Bootstrapのファイルサイズ(圧縮後):
- bootstrap.min.css: 約23KB (gzip後)
- bootstrap.bundle.min.js: 約26KB (gzip後)
合計50KB弱。「重い」って騒ぐ人もいるけど、2025年の回線速度なら瞬殺レベル。スマホの写真1枚の方がよっぽど重い。
実際のページ読み込みへの影響は誤差程度:
- CSS読み込み: 2-5ms
- JavaScript実行: 1-3ms
- 体感での違い: ほぼゼロ
Optimization Techniques
CSS Purging:
PurgeCSSでunused classesを削除。Bootstrap最適化の詳細ガイドやCDN活用法も効果的:
// purgecss.config.js
module.exports = {
content: ['./src/**/*.{html,js}'],
css: ['./node_modules/bootstrap/dist/css/bootstrap.css'],
safelist: ['btn-primary', 'modal-backdrop'] // Keep these
}
Tree Shaking:
ES6 modulesでJavaScript components:
// 必要なcomponentだけimport
import { Modal, Dropdown } from 'bootstrap';
ブラウザ対応状況
Bootstrap 5.3のブラウザ対応状況:
- Chrome: 60以上
- Firefox: 60以上
- Safari: 12以上
- Edge: 79以上
要するに、2018年以降のブラウザなら大丈夫。IE11は完全に切り捨て。
IE11どうするの問題
「まだIE11対応必要です」って言われたら、諦めてBootstrap 4.6使うしかない。Bootstrap 5はCSS VariablesとかFlexboxバリバリ使ってるから、IE11で動かすのは無理。
でも正直、2025年でIE11対応求めてくる案件って、もうヤバい案件の可能性高い。逃げることをお勧めする。
Polyfillとか面倒なことは一切なし
Bootstrap 5は最新ブラウザ前提で作られてるから、polyfillとか古いブラウザ対応のための変なコードは不要。その分軽いし、メンテも楽。
結局Bootstrap 5は普通に使える
技術的に見ても、Bootstrap 5は別に古くない:
- 最新CSS使ってる: CSS Variables、Flexbox、CSS Grid全部入り
- jQuery地獄から脱出: 素のJavaScriptでクリーンなAPI
- 軽量化対応: 必要な分だけ読み込める
- カスタマイズ可能: SassもCSS Variablesも好きな方で
- 段階的導入: 部分的に使って徐々に移行可能
「Bootstrapって古いでしょ?」とか言ってる奴は、多分Bootstrap 3の記憶で止まってる。Bootstrap 5.3は技術的に見ても普通にモダンだし、現場で使って困ることはない。
まあ、最新技術に飛びつくのもいいけど、安定して動くものの方が大事だろ。