Skip to content

feat: add Prettier plugin for Bootstrap class ordering#42359

Closed
pierluigilenoci wants to merge 1 commit intotwbs:mainfrom
pierluigilenoci:feat/prettier-class-ordering-plugin
Closed

feat: add Prettier plugin for Bootstrap class ordering#42359
pierluigilenoci wants to merge 1 commit intotwbs:mainfrom
pierluigilenoci:feat/prettier-class-ordering-plugin

Conversation

@pierluigilenoci
Copy link
Copy Markdown
Contributor

Summary

Adds prettier-plugin-bootstrap, a Prettier v3+ plugin that automatically sorts Bootstrap CSS classes following the framework's recommended order — similar to what prettier-plugin-tailwindcss provides for Tailwind CSS.

Closes #38397

How it works

The plugin hooks into Prettier's existing parsers (HTML, JSX, Vue, Angular, Astro) and re-orders class/className attribute values according to Bootstrap's architecture:

  1. Layoutcontainer, row, col-*
  2. Componentsbtn, card, modal, navbar, etc. (following bootstrap.scss import order)
  3. Helpersclearfix, visually-hidden, stretched-link, etc.
  4. Utilities — following the key order of the $utilities map in scss/_utilities.scss

Responsive variants (e.g. d-md-flex) sort immediately after their base class (d-flex). Unknown/custom classes are pushed to the end, preserving their original relative order.

Before

<div class="text-center p-3 container bg-primary text-white mb-4 rounded">
  <button class="px-4 btn btn-primary btn-lg rounded-pill">Click me</button>
</div>

After

<div class="container bg-primary text-white text-center mb-4 p-3 rounded">
  <button class="btn btn-primary btn-lg px-4 rounded-pill">Click me</button>
</div>

What's included

  • prettier-plugin-bootstrap/src/class-order.mjs — canonical Bootstrap class ordering (derived from bootstrap.scss import stack + $utilities map)
  • prettier-plugin-bootstrap/src/index.mjs — Prettier plugin entry point (parser wrappers for HTML, JSX, Vue, Angular, Astro)
  • prettier-plugin-bootstrap/tests/ — 24 unit + integration tests using Node.js built-in test runner
  • Plugin options: bootstrapAttributes (additional attributes to sort) and bootstrapFunctions (function calls containing class lists)

Design decisions

  • ESM only — aligns with Prettier v3's plugin architecture
  • No build step — plain .mjs files, no TypeScript/bundler needed
  • Zero runtime dependencies — only peer-depends on Prettier
  • Ordering source of truth — class order is derived directly from Bootstrap's SCSS source structure, making it maintainable as Bootstrap evolves
  • Stable sort — classes in the same category and breakpoint tier keep their original relative order

Known limitations / future work

  • The bootstrapFunctions option (for sorting inside clsx(), classNames() etc.) is declared but the JSX/TS parser integration for function arguments is not yet fully wired up — this can be added incrementally
  • The class ordering definition is static; a future enhancement could read the ordering from a compiled Bootstrap CSS file or the $utilities Sass map directly
  • Currently does not handle template literal class expressions (e.g. class={`btn ${variant}`})

Test plan

  • All 24 tests pass: cd prettier-plugin-bootstrap && node --test tests/*.test.mjs
  • Manual verification: create a test HTML file with unsorted Bootstrap classes and run npx prettier --plugin=./prettier-plugin-bootstrap/src/index.mjs --write test.html
  • CI validation

Add prettier-plugin-bootstrap, a Prettier v3+ plugin that automatically
sorts Bootstrap CSS classes following the framework's recommended order.

The plugin sorts classes into categories that mirror Bootstrap's
architecture: layout → components → helpers → utilities. Within the
utilities category, classes follow the key order of the $utilities
Sass map defined in scss/_utilities.scss. Responsive variants (e.g.
d-md-flex) sort immediately after their base class.

Supports HTML, JSX/TSX, Vue, Angular, and Astro parsers.

Closes twbs#38397

Signed-off-by: Pierluigi Lenoci <pierluigilenoci@gmail.com>
@coliff
Copy link
Copy Markdown
Contributor

coliff commented Apr 28, 2026

neat project! I'm not a decision-maker on this at all, but just a thought - would it be better to have this as a separate project on GitHub and published to npm and then it could be used as a dev dependency on different projects (including this one). I think I'd be interested in using it if it was on npm!

@pierluigilenoci
Copy link
Copy Markdown
Contributor Author

Thanks @coliff! That's a great suggestion — a standalone npm package would make it reusable across projects beyond Bootstrap. I'll look into extracting it to its own repo with proper test coverage and documentation. Would you be interested in collaborating on defining the plugin's API/options?

@julien-deramond
Copy link
Copy Markdown
Member

Really cool initiative @pierluigilenoci 🙌

As mentioned by @coliff (#42359 (comment)), this would be better as a standalone tool so it can be reused across different projects. For example, our Stylelint config lives in a separate repository (https://github.com/twbs/stylelint-config-twbs-bootstrap/) for that exact reason, as it’s useful beyond just this repo.

The second point is maintenance. To be transparent, maintaining the library is already quite demanding, and introducing an additional tool here would increase that burden further.

If you’re able to move forward with a standalone version, feel free to ping us, we’d be happy to evaluate if and how it could be integrated.

Closing this PR for now.

@pierluigilenoci
Copy link
Copy Markdown
Contributor Author

Following the feedback from @coliff and @julien-deramond, I've extracted the plugin into a standalone npm package:

📦 npm: prettier-plugin-bootstrap
🔗 Repo: pierluigilenoci/prettier-plugin-bootstrap

I've also opened #42383 to integrate it into the Bootstrap docs site as a dev dependency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create a Bootstrap class ordering prettier

3 participants