Skip to content

refactor: replace Bootstrap with Tailwind CSS and Base UI - #3196

Draft
Soxasora wants to merge 69 commits into
stackernews:masterfrom
Soxasora:feat/redesign-replace-bootstrap
Draft

refactor: replace Bootstrap with Tailwind CSS and Base UI#3196
Soxasora wants to merge 69 commits into
stackernews:masterfrom
Soxasora:feat/redesign-replace-bootstrap

Conversation

@Soxasora

@Soxasora Soxasora commented Aug 20, 2026

Copy link
Copy Markdown
Member

wip, PR description in construction

Description

Part of #3190, supersedes #3126, #3187, and #3188.

This PR replaces Bootstrap and React Bootstrap with Tailwind CSS v4, Base UI primitives, and SN UI components.

The migration:

  • replaces Bootstrap layout and utility classes with Tailwind
  • replaces React Bootstrap primitives with shared Base UI based components
  • introduces --sn-* design tokens
  • removes Bootstrap, React Bootstrap, Sass, and all tracked SCSS
  • places all CSS Modules inside @layer components
  • adds a lint check enforcing the CSS Module layer contract (maybe useless)
  • separates design tokens, base styles, component styles, utilities, and global styles
  • tries to preserve the prod behavior

The global stylesheet order is:

  1. styles/tokens.css
  2. styles/tailwind.css
  3. styles/base.css
  4. styles/app.css
  5. KaTeX styles
  6. styles/text.css

Screenshots

tbd

Additional Context

Checklist

Are your changes backward compatible? Please answer below:

Yes.

On a scale of 1-10 how well and how have you QA'd this change and any features it might affect? Please answer below:

currently 6/10 as every component has been QA'd the moment it got migrated, but the whole app hasn't been QA'd if not automatically.

For frontend changes: Tested on mobile, light and dark mode? Please answer below:

Yes.

Did you introduce any new environment variables? If so, call them out explicitly here:

No

Did you use AI for this? If so, how much did it assist you?

Yes: mechanical migration of bootstrap to tailwind, CSS cascade and Base UI adoption.

@socket-security

socket-security Bot commented Aug 20, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​base-ui/​react@​1.7.0721008993100
Addedtailwindcss@​4.3.31001008497100
Addedtailwind-merge@​3.6.01001008696100
Addedpostcss-preset-env@​11.3.29610010093100
Added@​tailwindcss/​postcss@​4.3.310010010097100

View full report

@Soxasora Soxasora linked an issue Aug 20, 2026 that may be closed by this pull request
Notification headers, payment status text, and external transaction icons
built their color classes with template interpolation, which the Tailwind
scanner cannot see. The text colors only worked through a safelist in the
Tailwind entry. Map the runtime color names through literal classes at each
site and drop the safelist.
The iOS input-zoom floor, the safe-area paddings, and the page column width
were repeated as arbitrary values. Add text-touch, px-safe, px-safe-gutter,
and a container-page theme token, replace the arbitrary values at their call
sites, and register the new names with tailwind-merge.
The monospace stack was defined four times: the Tailwind font-mono theme
value, the math editor input, rendered code blocks, and the wallet logger.
Point font-mono and the three hand-rolled stacks at the canonical
--sn-font-monospace token. Code blocks move from a Menlo-first to the
SF Mono-first stack and the logger picks up the full stack instead of the
browser default.
The avatar upload chip and the bounty badges painted their grays as
arbitrary hex classes, and the grey-medium, dark and outline-grey-darkmode
button variants repeated the same literals. Add --sn-dark, --sn-grey-medium
and --sn-grey-darkmode next to --sn-light as static tokens, expose the four
as bg-* and text-* utilities through the inline theme, and point the call
sites, the button module and the warning toast's text at them. The painted
values are unchanged in both themes.
Tailwind only sees class names that appear literally in source, and the
token rule says colors ride --sn-* values. Add scripts/class-soundness-check.js
to npm run lint: it fails on class fragments composed with template
interpolation inside className attributes and class recipes, on raw hex
colors in bracket utilities, and on any reference to the retired --theme-*
and --bs-* custom properties.
The tooltip, button, field, checkbox, multi-select and range modules and
the new-comment dot repeated palette values as hex literals that already
exist as --sn-* tokens. Point them at the tokens, express the focus tints
and rings as color-mix() on the primary, danger and success tokens, and
let the input placeholder ride --sn-muted. The link editor's remove icon
hover rides --sn-toolbarHover instead of an undefined custom property.
In dark mode the placeholder is lighter and the slider focus ring no
longer paints the light body color; branded territories get focus tints
from their own primary.
Icon fills came from a hand-written, unlayered family in app.css that
silently outranked the fill-* utilities Tailwind already emits for the
theme colors, with two descendant rules faking "follow the text color"
under .text-muted and .text-primary. Delete the family and the two rules:
fill-grey becomes fill-muted, fill-theme-color becomes fill-current, the
icons under muted and primary text carry fill-current themselves, and
Info's icon follows its surrounding text by default. fill-warning now
paints the warning orange next to its warning label instead of the old
brown emphasis color, so that token goes too. The footer toggles and the
table-of-contents icon drop a redundant fill-grey and paint with the
.theme class alone, matching the nav icons at rest.
The link button variant carried its own --sn-link-color pair whose dark
value, a pale yellow, was Bootstrap's derived dark link color rather than
an SN choice, and it ignored territory link branding. Point the variant at
--sn-link and --sn-linkHover and drop the pair from tokens.css. Light mode
is unchanged; in dark mode link buttons are the same blue as content links.
Territory branding emitted a YIQ-derived text color and, under @supports,
a contrast-color() override, so owners and visitors could see different
text on the same brand color depending on the browser. Emit the YIQ answer
alone. contrast-color() only ever returns black or white as well, so the
dual path bought no capability, only divergence.
Remove the job item's table-of-contents call, which rendered nothing
without a reader ref, make the toast default delay a module constant since
nothing else reads it, move the sub select's fixed widths from !important
module classes to utilities that merge through cn(), and replace the
notification rows' inline color, size and line-height styles with
utilities.
Restore the pale dark tint link buttons lost when they were unified on the
content link color. It is now mixed from --sn-primary on the button, so a
territory primary reaches it. The default theme paints the old values;
light mode is unchanged.
Master added the guided wallet onboarding, the passphrase confirmation
step and a freebie checkbox against Bootstrap. Wrap the onboarding module
in the components layer and ride --sn tokens, render its links with
buttonClasses(), swap the remaining Bootstrap utilities for their
Tailwind names, and render the freebie checkbox with the house Checkbox.
The textual merge kept the root postcss spec at the old range and lost
the dev flags on the postcss tree that moved to devDependencies.
@Soxasora
Soxasora force-pushed the feat/redesign-replace-bootstrap branch from dbe5025 to 2e76991 Compare August 29, 2026 19:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate to TailwindCSS and Base UI

1 participant