-
Notifications
You must be signed in to change notification settings - Fork 81
[WIP] Reduce base link specificity #1054
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Reduce base link specificity #1054
Conversation
This keeps all base element styles at a single element level of specificity and ensures any class will overrule the styles
4de77cb to
8931907
Compare
|
Hoping this direction is in line with transition to CSS variables |
8931907 to
1dd40e8
Compare
| :link { | ||
| color: $link-color; | ||
| a { | ||
| color: var(--link-color); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was interesting. I thought I could redeclare the --link-color var within the :visited selector scope, but it didn't seem to take effect as computed style (tested with and without the :where() selector). Dev tools seems to show both the correct variable and the wrong computed style 🤷♀️
:root {
--link-color: default-color;
--link-color-visited: visited-color;
}
a {
color: var(--link-color);
}
a:where(:visited) {
--link-color: var(--link-color-visited);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried about using :where since the support for it is less wide spread than support for dynamic properties. Can you help me understand what would happen if a visitor had variable support but not where support? Links are visible they just don't get hover/focus? Or something more dire?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked into CSS layers as a possible solution (I am so excited for these!) but the support for them is about the same as support for :where()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So on a background set to black with a CSS variable they will get blue/purple links?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will check tomorrow. I think they will get the --link-coloror --link-color-visited value. If we use mzp-t-dark for dark background, the links will still have good contrast (those variables will respond to the dark theme), but their color will not change on hover/focus.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
||
| // any descendant link will inherit dark theming (replace light-links mixin) | ||
| // this might belong in root file? site-wide dark theme class | ||
| .mzp-t-dark { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
||
| // any descendant link will inherit white theming (replace white-links mixin) | ||
| // link-specific theme class | ||
| .mzp-t-white-links { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love the move the variables here! We need to be careful we also move to variables for the background colours at the same time so we don't get in a circumstance where the background is defined as dark using a hex colour but the component depends on variables for the links.
Perhaps the prudent thing to do is to bundle the CSS variable changes into one big point release version that we test on un-supporting browsers.
| &:active { | ||
| color: var(--link-color-visited-hover); | ||
| } | ||
| &:where(:hover, :focus, :active) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another thing we could do to make styling links easier would be to drop the separate styles for visited links on hover/focus/active and rely on source order for the override.
- Link
- Visited
- Hover
- Active
- Focus
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've always found the a:link:visited:hover edge case to be confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will add a commit with source order for comparison
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only potential conflict here is that a stateful pseudo-class + element selector will beat a component class selector (if it doesn't also explicitly override the state). This is most likely to happen with button component and link-specific visited state.

We could unnest the link states so they are only 0,1,0 specificity and rely on source order to override (basic element styles are overruled by component styles)
Or we could also include the :visited state selector on any components that could also be links under the hood 0,2,0 beats 0,1,1
janbrasna
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense, looks elegant, and is easy to follow/understand. (Redefining the vars for states as shown above resulting in the rendering woes looks cool at first, but this take looks more reliable.)
The tricky part might be following the themes to the actual components, to make sure it either still works as intended, or what tweaks could be useful there… as it's usually the components being consumed and redefined downstream causing the specificity issues, e.g. the Modal: (#758)
protocol/assets/sass/protocol/components/_modal.scss
Lines 104 to 106 in d34e087
| .mzp-c-modal-overlay-contents { | |
| @include text-body-lg; | |
| @include light-links; |
| // this might belong in a different CTA file? | ||
| // it's not meant to apply to `a` elements unless they have the class added directly | ||
| // the other classes in this file are theme containers that select `a` elements inside them | ||
| .mzp-c-cta-link { | ||
| font-family: $button-font-family; | ||
| font-family: var(--button-font-family); | ||
| font-weight: bold; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"this might belong in a different CTA file?"
IIUC the organization of files match the grouping of component pages, so this chubby "CTA link" styling seems to belong with these, as it's one of the variants or states of the Links styles:
https://protocol.mozilla.org/components/detail/links--call-to-action
(It feels like it's listed as a flavor of a base element, not treated as a component per se — to be used mostly in other components…)
janbrasna
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should also no longer complain:
|
|
||
| .mzp-c-button, /* stylelint-disable-line no-duplicate-selectors */ | ||
| a.mzp-c-button { | ||
| .mzp-c-button /* stylelint-disable-line no-duplicate-selectors */ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| .mzp-c-button /* stylelint-disable-line no-duplicate-selectors */ { | |
| .mzp-c-button { |
| // * -------------------------------------------------------------------------- */ | ||
| // Primary | ||
|
|
||
| /* stylelint-disable-next-line no-duplicate-selectors */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /* stylelint-disable-next-line no-duplicate-selectors */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dee5f6e to
9de9e96
Compare
| background-color: transparent; | ||
| border-color: $color-black; | ||
| color: $color-black; | ||
| .mzp-c-button.mzp-t-secondary { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is so much nicer, I want this to work 😭
| --link-color-inverse: #{tokens.$color-blue-10}; | ||
| --link-color-visited-hover-inverse: #{tokens.$color-violet-05}; | ||
| --link-color-visited-hover: #{tokens.$color-link-hover}; | ||
| --link-color-visited-hover: #{tokens.$color-link-visited-hover}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another place that would be simplified by having the same hover state for both visited and unvisited links.
bce794b to
97cc65c
Compare
|
|
||
| &:hover, &:active, &:focus { | ||
| /* stylelint-disable-next-line color-function-notation */ | ||
| color: var(--link-color-with-state, hsl(from var(--link-color) h s calc(l - 10))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would allow us to drop support for hover tokens and (hopefully) simplify maintenance
|
After internal discussion, we want to drop support for Do Not Merge until Icon release is published |
|
Closing in favour of #1077 |













Description
This is more a thought experiment than anything at this stage. The idea is to reduce base element link specificity so it is easily overridden by a single class
Base element specificity: 0 0 1

not visited
visited

Class specificity: 0 1 0
NOTE: the danger of custom properties is that they inherit all the way down the tree, so could be going further than desired if "donut scope" was intended
CHANGELOG.md.Issue
Add a link to a related GitHub issue if applicable.
Testing
Enter helpful notes for whoever code reviews this change.