A powerful HTMX extension that allows you to dynamically add, remove, or toggle CSS classes from elements based on server responses, without requiring JavaScript.
- π― Declarative: Use simple HTML attributes in server responses
- π Powerful: Supports any valid CSS selector
- π Flexible: Add/remove/toggle single or multiple classes
- π¨ Wildcard Support: Remove classes by pattern (e.g.,
bg-*,*-disabled) - π¦ Lightweight: < 3KB minified
- π Debuggable: Built-in debug mode with detailed console logging
- β‘ Fast: Selector caching for optimal performance
- π‘οΈ Robust: Advanced error handling and validation
- π Universal: Works with all server-side frameworks
<script src="https://unpkg.com/htmx.org@2.0.0"></script>
<script src="https://unpkg.com/htmx-class-manager@1.1.0/dist/class-manager.min.js"></script>Or use jsDelivr:
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.0"></script>
<script src="https://cdn.jsdelivr.net/npm/htmx-class-manager@1.1.0/dist/class-manager.min.js"></script>npm install htmx-class-managerThen include in your project:
import 'htmx-class-manager';Download class-manager.min.js from the releases page and include it in your HTML:
<script src="path/to/class-manager.min.js"></script>Add hx-ext="class-manager" to your HTML:
<body hx-ext="class-manager">
<-l Your content -->
</body>Or on specific elements:
<div hx-ext="class-manager">
<-l Only this section uses the extension -->
</div>In your server's HTML response, add class-add, class-remove, or class-toggle attributes:
<-l Server returns this partial HTML -->
<div class-add="#myElement:highlight"></div>That's it! The extension will automatically add the highlight class to the element with id="myElement".
<div class-add="selector:class1,class2"></div>
<div class-remove="selector:class1,class2"></div>
<div class-toggle="selector:class1,class2"></div>Format: "selector:classes" where:
selector= Any valid CSS selectorclasses= Comma-separated class names (no dots)- Multiple targets = Separate with pipe
|
<-l Add single class -->
<div class-add="#alert:visible"></div>
<-l Add multiple classes -->
<div class-add="#alert:visible,fade-in,shadow-lg"></div>
<-l Multiple targets -->
<div class-add="#alert:visible | .notification:active"></div><-l Remove single class -->
<div class-remove="#loader:hidden"></div>
<-l Remove multiple classes -->
<div class-remove="#modal:open,active,visible"></div>
<-l Multiple targets -->
<div class-remove="#modal:open | .overlay:visible"></div><-l Toggle single class -->
<div class-toggle="#sidebar:collapsed"></div>
<-l Toggle multiple classes -->
<div class-toggle="#menu:open,active"></div>
<-l Multiple targets -->
<div class-toggle="#menu:open | .overlay:visible"></div>Remove classes that match a pattern using wildcards in class-remove:
Remove all classes starting with a prefix:
<-l Remove all background color classes -->
<div class-remove="#element:bg-*"></div>
<-l Removes: bg-red-500, bg-blue-300, bg-gray-100, etc. -->Remove all classes ending with a suffix:
<-l Remove all disabled state classes -->
<div class-remove=".button:*-disabled"></div>
<-l Removes: is-disabled, btn-disabled, input-disabled, etc. -->Remove all classes containing a substring:
<-l Remove all text-related utility classes -->
<div class-remove="#content:*-text-*"></div>
<-l Removes: has-text-centered, no-text-transform, etc. --><-l Replace Tailwind color scheme -->
<div class-remove="#button:bg-*,text-*"
class-add="#button:bg-blue-500,text-white"></div>
<-l Clear all state classes -->
<div class-remove=".form-input:*-error,*-success,*-warning"></div>
<-l Remove all responsive classes -->
<div class-remove="#grid:sm-*,md-*,lg-*"></div><-l Use any CSS selector -->
<div class-add=".card > .header:highlighted"></div>
<div class-add="[data-status='active']:badge-success"></div>
<div class-add="#parent .child:animated"></div><-l Process in order: remove, then add, then toggle -->
<div class-remove="#element:old-class"
class-add="#element:new-class"
class-toggle="#element:active"></div>Server Response (Django/Flask/Rails):
<-l Show toast with animation -->
<div class-add="#toast:opacity-100,translate-y-0"
class-remove="#toast:opacity-0,translate-y-2"></div>HTML:
<div id="toast" class="opacity-0 translate-y-2 transition-all">
Success! Your changes have been saved.
</div>Server Response:
<-l Open modal and add overlay -->
<div class-add="#modal:flex | #overlay:block"
class-remove="#modal:hidden | #overlay:hidden"></div>HTML:
<div id="modal" class="hidden">Modal Content</div>
<div id="overlay" class="hidden">Backdrop</div>Server Response:
<-l Clear old states, show error -->
<div class-remove="#email-field:*-success,*-error"
class-add="#email-field:border-red-500,text-red-600"></div>Server starts processing:
<div class-add="#submit-btn:opacity-50,cursor-not-allowed"
class-remove="#submit-btn:hover:bg-blue-600"></div>Server completes:
<div class-remove="#submit-btn:opacity-50,cursor-not-allowed"
class-add="#submit-btn:hover:bg-blue-600"></div><-l Switch from light to dark theme -->
<div class-remove="body:bg-*,text-*"
class-add="body:bg-gray-900,text-white"></div><-l Update active nav item -->
<div class-remove=".nav-item:active,font-bold"
class-add="#nav-home:active,font-bold"></div><-l Show success state -->
<div class-remove=".form-group:*-error"
class-add=".form-group:border-green-500,text-green-600"></div><-l Activate new tab -->
<div class-remove=".tab:active,border-blue-500"
class-add="#tab-2:active,border-blue-500"></div>Enable detailed console logging:
// In your browser console or script
htmx.config.extensions = htmx.config.extensions || {};
htmx.config.extensions['class-manager'] = { debug: true };Or programmatically:
// After extension loads
const ext = htmx.ext['class-manager'];
if (ext) ext.debug = true;When enabled, you'll see:
- Server responses received
- Classes being added/removed/toggled
- Validation errors and warnings
- Selector matching results
| Attribute | Description | Example |
|---|---|---|
class-add |
Add classes to target elements | class-add="#el:class1,class2" |
class-remove |
Remove classes from target elements | class-remove="#el:class1,class2" |
class-toggle |
Toggle classes on target elements | class-toggle="#el:class1,class2" |
attribute="selector:class1,class2,class3 | selector2:class4"
- Selector: Any valid CSS selector (id, class, attribute, etc.)
- Classes: Comma-separated class names (without dots)
- Multiple Targets: Separated by pipe
| - Wildcards: Use
*for pattern matching (class-remove only)
| Pattern | Matches | Example |
|---|---|---|
prefix-* |
Classes starting with prefix | bg-* matches bg-red-500, bg-blue-300 |
*-suffix |
Classes ending with suffix | *-disabled matches is-disabled, btn-disabled |
*-substring-* |
Classes containing substring | *-text-* matches has-text-bold |
HTML:
<body hx-ext="class-manager">
<div id="toast"
class="hidden opacity-0 transition-all duration-300">
<p id="toast-message"></p>
</div>
<button hx-post="/save"
hx-target="#toast-message">
Save
</button>
</body>Server Response (Success):
<div class-remove="#toast:hidden,opacity-0"
class-add="#toast:opacity-100">
<p>β Saved successfully!</p>
</div>Server Response (Error):
<div class-remove="#toast:hidden,opacity-0,bg-*"
class-add="#toast:opacity-100,bg-red-500">
<p>β Error saving data</p>
</div>HTML:
<form hx-ext="class-manager">
<input id="email"
type="email"
hx-post="/validate-email"
hx-trigger="blur"
class="border-2">
<span id="email-error"></span>
</form>Server Response (Valid):
<div class-remove="#email:border-*,*-error"
class-add="#email:border-green-500"></div>Server Response (Invalid):
<div class-remove="#email:border-*,*-success"
class-add="#email:border-red-500">
<span id="email-error" class="text-red-600">Invalid email</span>
</div>HTML:
<nav hx-ext="class-manager">
<a id="nav-home" class="nav-item active">Home</a>
<a id="nav-about" class="nav-item" hx-get="/about">About</a>
<a id="nav-contact" class="nav-item" hx-get="/contact">Contact</a>
</nav>Server Response (About page):
<div class-remove=".nav-item:active,font-bold"
class-add="#nav-about:active,font-bold">
<-l About page content -->
</div>Server Response (Dark Mode):
<div class-remove="body:bg-*,text-*"
class-add="body:bg-gray-900,text-gray-100"
class-remove=".card:bg-*"
class-add=".card:bg-gray-800"></div>Server Response (Light Mode):
<div class-remove="body:bg-*,text-*"
class-add="body:bg-white,text-gray-900"
class-remove=".card:bg-*"
class-add=".card:bg-gray-100"></div>- Wildcards (
*) are only supported inclass-remove - Using wildcards in
class-addorclass-togglewill show a warning (in debug mode)
When multiple attributes are present, they execute in this order:
class-removeclass-addclass-toggle
- Invalid CSS selectors are detected and skipped
- Enable debug mode to see validation errors
- Selectors are cached for performance
After processing, the class-add, class-remove, and class-toggle attributes are automatically removed from elements.
- Enable debug mode to see what's happening
- Check your selector - ensure it's valid CSS
- Verify the element exists when the response is processed
- Check the order - remove happens before add
- Wildcards only work with
class-remove - Ensure the pattern is correct (
bg-*,*-disabled) - Enable debug mode to see matching classes
- The extension caches selectors automatically
- Avoid overly complex selectors when possible
- Use specific selectors (IDs) instead of broad ones
Contributions are welcome! Please feel free to submit a Pull Request.
MIT License - see LICENSE file for details.
- β¨ Added
class-toggleattribute for toggling classes - π¨ Added wildcard pattern support in
class-remove(bg-*,*-disabled,*-text-*) - β‘ Added selector caching for improved performance
- π‘οΈ Enhanced error handling and selector validation
- π Improved documentation with more examples
- π Initial release
- β
class-addandclass-removefunctionality - π Debug mode
- π¦ Multiple selector support
Made with β€οΈ for the HTMX community