Context
Recently, we noticed that the bloated Bootstrap CSS framework was causing an average drop of 5-10 points per page in our Google Lighthouse scores. Consequently, we planned to remove Bootstrap. Since we were only using Bootstrap's CSS and not its JavaScript components, the replacement process was relatively straightforward.
However, we have a large number of pages. Refactoring the grid layout on every page to use Tailwind's grid system would have been very time-consuming and would have increased the risk of breaking pages. Therefore, we decided to postpone the page refactoring to a later phase.
Why Use Tailwind
- Lightweight
- Fast for building layouts
- Rich set of selectors
- Easy to extend
- Saves time writing CSS and creating helper classes
- A mobile-first CSS framework
Tools Used for this Tailwind Conversion
- Bootstrap to Tailwind Online Tool
- CSS to Tailwind Online Tool
- ChatGPT Plus
- We also referenced the Bootstrap source code in the
node_modulesfolder.
First, for batch modifications, we wrote a Powershell script named convert-boostrap-to-tailwindcss.ps1
# 設定搜尋的目錄路徑
$directoryPath = "E:\VisualStudioProject\Apps\NextJS"
# 設定需要替換的字詞對應
$replacements = @{
'd-none' = 'hidden'
'd-block' = 'block'
'd-flex' = 'flex'
'd-inline-flex' = 'inline-flex'
'flex-column' = 'flex-col'
'justify-content-end' = 'justify-end'
'align-items-start' = 'items-start'
'align-items-center' = 'items-center'
'align-items-end' = 'items-end'
'align-items-stretch' = 'items-stretch'
'justify-content-center' = 'justify-center'
'justify-content-start' = 'justify-start'
'justify-content-between' = 'justify-between'
'justify-content-around' = 'justify-around'
'position-absolute' = 'absolute'
'd-xl-none' = 'xl:hidden'
'd-xl-block' = 'xl:block'
'd-md-block' = 'md:block'
'd-md-none' = 'md:hidden'
'w-100' = 'w-full'
'h-100' = 'h-full'
'p-3' = 'p-4'
'p-4' = 'p-6'
'p-5' = 'p-12'
'px-3' = 'px-4'
'px-4' = 'px-6'
'px-5' = 'px-12'
'py-3' = 'py-4'
'py-4' = 'py-6'
'py-5' = 'py-12'
'pt-3' = 'pt-4'
'pt-4' = 'pt-6'
'pt-5' = 'pt-12'
'pb-3' = 'pb-4'
'pb-4' = 'pb-6'
'pb-5' = 'pb-12'
'ps-3' = 'ps-4'
'ps-4' = 'ps-6'
'ps-5' = 'ps-12'
'p-md-4' = 'md:p-6'
'p-lg-4' = 'lg:p-6'
'py-md-4' = 'md:py-6'
'pt-lg-5' = 'md:p-12'
'pb-md-4' = 'md:pb-6'
'ps-lg-3' = 'lg:ps-4'
'pe-md-5' = 'md:pe-12'
'm-3' = 'm-4'
'm-4' = 'm-6'
'm-5' = 'm-12'
'mx-3' = 'mx-4'
'mx-4' = 'mx-6'
'mx-5' = 'mx-12'
'my-3' = 'my-4'
'my-4' = 'my-6'
'my-5' = 'my-12'
'mt-3' = 'mt-4'
'mt-4' = 'mt-6'
'mt-5' = 'mt-12'
'mb-3' = 'mb-4'
'mb-4' = 'mb-6'
'mb-5' = 'mb-12'
'ms-3' = 'ms-4'
'ms-4' = 'ms-6'
'ms-5' = 'ms-12'
'my-md-3' = 'md:my-4'
'mt-md-5' = 'md:mt-12'
'mt-lg-5' = 'md:lg-12'
'fs-1' = 'text-6xl'
'fs-2' = 'text-5xl'
'fs-3' = 'text-4xl'
'fs-4' = 'text-3xl'
'fs-5' = 'text-2xl'
'fs-6' = 'text-xl'
'fw-bold' = 'font-bold'
'fw-lighter' = 'font-[lighter]'
'fw-light' = 'font-light'
'fw-semibold' = 'font-semibold'
'col-12' = 'w-full'
'col-md-4' = 'md:w-1/3'
'col-6' = 'w-1/2'
'col-lg-4' = 'lg:w-1/3'
'col-lg-2' = 'lg:w-1/6'
'col-sm-6' = 'sm:w-1/2'
'col-sm-4' = 'sm:w-1/3'
'col-3' = 'w-1/4'
'col-4' = 'w-1/3'
'col-lg-6' = 'lg:w-1/2'
'col-lg-3' = 'lg:w-1/4'
'col-md-8' = 'md:w-2/3'
'col-xl-5' = 'xl:w-5/12'
'col-md-6' = 'md:w-1/2'
'col-md-10' = 'md:w-5/6'
'col-md-12' = 'md:w-full'
'col-lg-5' = 'lg:w-5/12'
'col-lg-7' = 'lg:w-7/12'
'col-lg-8' = 'lg:w-2/3'
'col-lg-9' = 'lg:w-3/4'
'col-sm-8' = 'sm:w-2/3'
'col-sm-9' = 'sm:w-3/4'
'col-sm-12' = 'sm:w-full'
'col-1' = 'w-1/12'
'col-2' = 'w-1/6'
'col-5' = 'w-5/12'
'col-7' = 'w-7/12'
'col-8' = 'w-2/3'
'col-9' = 'w-3/4'
'col-11' = 'w-11/12'
'col-md-5' = 'md:w-5/12'
'col-md-7' = 'md:w-7/12'
'col-xl-6' = 'xl:w-1/2'
'col-xl-2' = 'xl:w-1/6'
'col-sm-offset-1' = 'sm:ml-1/12'
// 依據你們專案用到的,可以自己增加...
}
Set-Location -Path $directoryPath
Get-ChildItem -Recurse -Filter '*.tsx' | ForEach-Object {
$escapedFileName = $_.FullName -replace '\[(.*?)\]', '`[$1`]'
$content = Get-Content $escapedFileName -Raw
$replacements.GetEnumerator() | ForEach-Object {
$content = $content -replace "(?<!\!)$($_.Key)", $_.Value
}
[System.IO.File]::WriteAllText($_.FullName, $content)
}
pause
Manual Replacements Needed
container => 'container mx-auto
// bootstrap 的 container 比較特別,因此需要在 global scss 額外宣告 container
.container {
width: 100%;
margin-right: auto;
margin-left: auto;
}
@media (min-width: 576px) {
.container {
max-width: 540px;
}
}
@media (min-width: 768px) {
.container {
max-width: 720px;
}
}
@media (min-width: 992px) {
.container {
max-width: 960px;
}
}
@media (min-width: 1200px) {
.container {
max-width: 1140px;
}
}
@media (min-width: 1400px) {
.container {
max-width: 1320px;
}
}
Classes like row, due to potential naming conflicts, required manual replacement with "flex flex-wrap".
Classes like btn, close, dropdown-toggle, collapse, modal-header, and modal-body all had to be changed manually.
Some default font sizes and spacing that were misaligned.
Finally
Although switching technologies is always challenging, it's a worthwhile endeavor when you consider the long-term benefits for performance and maintainability. If you're also considering such an upgrade, we hope this helps you solve your problems.





























Comments