[rebrand] Hold Bootstrap button brand colors on compound states (RE-177) - #74706
Merged
Merged
Conversation
Bootstrap's button-variant mixin bakes the Sass $background a variant was
generated from into two nested blocks whose selectors are specificity 0,3,0:
&:active, &.active, .open > &.dropdown-toggle {
&:hover, &:focus, &.focus { background-color: darken($background, 17%); }
}
&.disabled, &[disabled], fieldset[disabled] & {
&:hover, &:focus, &.focus { background-color: $background; }
}
The rebrand's .btn-primary and .btn-success overrides are flat `&:hover` /
`&:active` rules at 0,2,0, so they lose to both. $btn-primary-bg is still
$orange, which left four orange states on every .btn-primary in dashboard:
before after
pressed rgb(168,108,0) rgb(43,30,159)
pressed via keyboard rgb(168,108,0) rgb(43,30,159)
disabled + hover rgb(255,164,0) rgb(76,66,207)
disabled + focus rgb(255,164,0) rgb(76,66,207)
Each needs the pointer, focus, or the disabled attribute on the button, so none
of them shows at rest - which is why they survived the rebrand sweep. The
disabled pair is most visible on submit buttons, where the pointer is still over
the button for as long as the submission is in flight, and on the pixelation
level's #finished button, which ships disabled from page load.
Mirror the mixin's own selector shape so the specificity ties and source order
decides. Both blocks live in one mixin, taking keyword arguments, so a variant
cannot be half migrated.
.btn-success had the same latent bug, reverting to stock Bootstrap
rgb(92,184,92) instead of rgb(37,136,48).
Verified with CSS.forcePseudoState against the external-level Continue button
and the levelgroup Submit button, reading every state the mixin touches. No
eyes baseline should move: the at-rest paint is unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
levadadenys
force-pushed
the
denys/re/bootstrap-button-compound-states
branch
from
August 18, 2026 16:59
753c6cc to
9071254
Compare
levadadenys
requested review from
a team and
Nokondi
and removed request for
a team
August 18, 2026 17:01
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a Bootstrap rebrand regression where .btn-primary and .btn-success could revert to Bootstrap’s compile-time colors in compound interaction states (notably active+hover/focus and disabled+hover/focus), by matching Bootstrap’s nested selector specificity so the runtime CSS variable overrides win.
Changes:
- Add a
themable-button-statesSass mixin that mirrors Bootstrap’s nested selectors for:active/.activeand disabled compound states. - Apply the mixin to
.btn-primaryand.btn-successso compound states consistently use the intended runtime brand CSS variables. - Fix a comment typo (“Bootstramp” → “Bootstrap”).
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Nokondi
approved these changes
Aug 18, 2026
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.
Before:







After:







Problem
RE-177 reported the levelgroup submit button's pending state as still orange. It is, and so are three sibling states on every
.btn-primaryin dashboard.Bootstrap's
button-variantmixin bakes the Sass$backgrounda variant was generated from into two nested blocks whose selectors are specificity 0,3,0:The rebrand's
.btn-primary/.btn-successoverrides are flat&:hover/&:activerules at 0,2,0, so they lose to both.$btn-primary-bgis still$orange.Every one of these states needs the pointer, focus, or the
disabledattribute on the button, so none of them shows at rest — which is why they survived the rebrand sweep and why they are easy to miss when checking in devtools without a hover.Fix
One file. Mirror the mixin's own selector shape so the specificity ties and source order decides. Both blocks live in a single
themable-button-statesmixin taking keyword arguments, so a variant cannot be half migrated:.btn-successhad the same latent bug, reverting to stock Bootstraprgb(92,184,92)instead of ourrgb(37,136,48).Verification
Measured with
CSS.forcePseudoStateover CDP so every state is read rather than inferred — against the external-level Continue button and the levelgroup Submit button:rgb(76,66,207)rgb(76,66,207)rgb(43,30,159)rgb(43,30,159)rgb(43,30,159)rgb(43,30,159)rgb(168,108,0)rgb(43,30,159)rgb(168,108,0)rgb(43,30,159)rgb(76,66,207)rgb(76,66,207)rgb(255,164,0)rgb(76,66,207)rgb(255,164,0)rgb(76,66,207)Cascade resolution confirmed for all 10 compound selectors across both variants.
The disabled pair is most visible on submit buttons — the pointer stays over the button for as long as the submission is in flight — and on
_pixelation.html.haml's#finishedbutton, which shipsdisabledfrom page load.I also audited the rest of the mixin for anything else baked at compile time. Only
.badge { color: $background }remains, and nothing in the repo puts a badge inside a button, so this is the complete surface.Eyes
No baseline should move. Every changed state requires pointer, focus, or the
disabledattribute; the at-rest paint is byte-identical. Flagging it because 21 eyes-tagged scenarios touch pages with these buttons.Considered and not done here
Replacing these with design-system MUI buttons. It works, but eight buttons in five sibling partials (
_external,_standalone_video,_external_link,_curriculum_reference,_locked_lesson) would stay Bootstrap, and the metrics differ — 44px/6px radius/17.5px font vs 42px/4px/16px. A student would get an MUI Submit on a levelgroup and a Bootstrap Continue one level later, so a partial migration reads as worse consistency than today. Worth its own ticket covering all 11 partials, budgeted for the eyes baselines.🤖 Generated with Claude Code