feat: decouple from Tailwind + CSS-variable theming + sliding active indicator - #5
Merged
Merged
Conversation
…dicator The bar previously styled itself entirely with Tailwind utility classes, so any consumer without Tailwind (or who forgot to add the package to their content globs) got an unstyled bar — the biggest adoption risk. - Ship a self-contained stylesheet (src/styles.ts) using an `mtb-` class namespace; the component injects it once per document at runtime via useInsertionEffect (SSR-safe, shared across instances). No Tailwind needed. - Theme via CSS variables (--mtb-accent, --mtb-surface, …) with the current design as inline fallbacks, so consumers rebrand by setting vars on any ancestor. Also emit dist/styles.css (tsup onSuccess) + a "./styles.css" export, and export STYLE_CSS, for SSR/no-FOUC. - Replace the per-tab active background with a single sliding indicator pill that animates between tabs (measured via offsetLeft/Top, layout-correct in LTR and RTL, recomputed on active/compact/dir/size changes, reduced-motion aware, hidden when no tab is active). - Replicate the Tailwind variants in plain CSS: lg:hidden -> @media (min-width:1024px), motion-reduce -> prefers-reduced-motion, hover/active pseudo-states. - Playground: drop the Tailwind CDN, size icons with one plain CSS rule, add a live accent-theming control — proving the bar renders with zero Tailwind. - Tests for stylesheet injection and the indicator; README "Styling & theming" section (variables table + SSR note). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WCE1RpZpzupe5kvo5aGGvV
NagyVikt
marked this pull request as ready for review
June 22, 2026 10:44
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Final follow-up PR — the styling overhaul (#3 Tailwind decouple, #6 theming, #8 sliding indicator).
Why
The bar styled itself entirely with Tailwind utility classes, so any consumer without Tailwind — or who forgot to add the package to their
contentglobs — got a completely unstyled bar. This was the biggest adoption risk, and a prerequisite for real theming.What changed
No Tailwind required (#3)
src/styles.tsships a self-contained stylesheet under anmtb-class namespace. The component injects it once per document at runtime (useInsertionEffect, SSR-safe, shared by all instances).lg:hidden→@media (min-width:1024px),motion-reduce:→prefers-reduced-motion,hover:/active:→ pseudo-states.tsupemitsdist/styles.css(viaonSuccess), added as a"./styles.css"export, plus an exportedSTYLE_CSSstring.CSS-variable theming (#6)
Every themable color/size reads
var(--mtb-*, <default>), so consumers rebrand by setting variables on any ancestor — no fork. Full table in the README (--mtb-accent,--mtb-surface,--mtb-radius, …).Sliding active indicator (#8)
Replaces the per-tab active background with one pill that animates between tabs. Position is measured via
offsetLeft/Top(layout-correct in LTR and RTL), recomputed on active/compact/dir/size changes (incl.ResizeObserver), reduced-motion aware, and hidden when no tab is active.Playground
Dropped the Tailwind CDN, sized icons with a single plain-CSS rule, and added a live accent-theming button — demonstrating the bar renders fully styled with zero Tailwind.
Tests
Verification
npm run type-check,npm run build(emitsdist/styles.css,.d.ts4.36KB),npm testall green.vite build playgroundbuilds clean. The sliding-indicator geometry is exercised in the playground (jsdom has no layout, so geometry isn't unit-tested).🤖 Generated with Claude Code
https://claude.ai/code/session_01WCE1RpZpzupe5kvo5aGGvV
Generated by Claude Code