[Bug] Button's utility color classes seems to not hover properly due specificity issues
First of all, congratulations on Matcha CSS; it has been very interesting to create projects using it. I was exploring the documentation and noticed that buttons with utility color classes turn blue on :hover.
If the example below is the expected behavior of these buttons, I believe this is a specificity bug:
Bug description
Using the .default variant as an example, this is the CSS with the intended behavior:
button.default:not(:disabled):active,button.default:not(:disabled):hover {
background: var(--default);
color: var(--contrast)
}
It produces a 0,3,1 specificity and it is being overridden by this selector, which also has a specificity of 0.3.1 because its declared further on the code.
:is(button,input:is([type=submit],[type=reset],[type=button],[type=image])):not(:disabled):active,:is(button,input:is([type=submit],[type=reset],[type=button],[type=image])):not(:disabled):hover {
border-color: transparent;
background: var(--accent);
color: var(--light)
}
Proposal
There are a few solutions for this bug, ranging from the highest to the lowest impact:
1 - Using an :where selector
[!NOTE]
This solution is agnostic and non-obstrusive and have sufficient browser support. PR with proposal #43
In styles/forms/mod.css, L226
:where(button, input:is([type="submit"],[type="reset"],[type="button"],[type="image"])):not(:disabled):hover,
:where(button, input:is([type="submit"],[type="reset"],[type="button"],[type="image"])):not(:disabled):active {
border-color: transparent;
background: var(--accent);
color: var(--light);
}
It lowers the specificity to 0,2,0 and it allows the button's utility CSS to take precedence.
2 - Using an :where selector as an architectural axiom
[!NOTE]
This solution is agnostic and non-obstrusive and have sufficient browser support but it will require a more comprehensive change and may potentially lead to a major version increment. Proposal in this draft PR
Every CSS subdirectory acts as a module that serves both base styles and utility-oriented styles. For base styles related directly to HTML elements, you may want to maintain the lowest specificity possible to avoid collision with utility styles. I suggest Elad Shechter approach on CSS Reset. This ensures that every "base" styles will be safely overridden.
3 - Using an @layer at rule to separate base and utility layers
[!NOTE]
This solution is agnostic and non-obstrusive and have sufficient browser support but it will require a more comprehensive change and may potentially lead to a major or breaking version increment.
You may create two layers in every module, defining the layers in mod.css and appending the base styles to the base layer in the standalone version:
/* styles/forms/mod.css */
@layer base, components;
@layer base {
/* Form */
form {
overflow: auto;
padding: 1rem;
border-radius: var(--bd-radius);
margin: 0 auto 1rem;
background: var(--bg-muted);
}
}
And appending the utility code in the utility layer. If you re-declare this layer combination in multiple files, they will all append to the implicit layer with the same name, so there will be no drawback in declaring @layer base, components; in every mod file for À la carte build.
Hi ! Thanks a lot for the great, detailed and clean write-up, it was very pleasing to read.
I think 2 and 3 should indeed be :where matcha is moving toward, as it seems to be a pretty flexible paradigm and will definitely make overriding rules easier. I do agree there's some rules with exceptionnally high specificity out there.
Thank you very much ✨ If I can be of any help, let me know!