Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 31 additions & 41 deletions assets/sass/protocol/base/elements/_links.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,61 +4,51 @@

@use '../../includes/lib' as *;

:link {
color: $link-color;
// Rely on source order for the stateful style overrides
// 1. Visited
// 2. Hover
// 3. Active
// 4. Focus


a {
color: var(--link-color);
Copy link
Collaborator Author

@maureenlholland maureenlholland Apr 16, 2025

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);
}

Copy link
Collaborator Author

@maureenlholland maureenlholland Apr 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pseudo-class variable correctly identified
computed value still default color
Screenshot 2025-04-16 at 12 13 36 PM

root inheritance correctly overruled
Screenshot 2025-04-16 at 12 14 14 PM
^unrelated, looks like we have a mixed up value declaration on :root, visited hover color should be #952bb9

Copy link
Contributor

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?

Copy link
Contributor

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()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When custom properties are supported but :where() is not, browsers will ignore any :where() selectors, so you get default user agent styles for hover/focus etc.
does-not-support-where

vs. supported and applying the :where() rules
supports-where

Copy link
Contributor

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?

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so older browser links would always get the --link-color regardless of state or :visited status (all those rules are ignored). The correct link color will apply according to light/dark theme.

states visited

text-decoration: underline;

&:hover,
&:focus,
&:active {
color: $link-color-hover;
&:visited {
color: var(--link-color-visited);
}

&: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)));
Copy link
Collaborator Author

@maureenlholland maureenlholland May 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Demo of functional hover color approach:

  1. By default, we automatically darken the lightness of the color by 10%
  2. We can override this default with another function (i.e. dark theme automatically lightens color) or another set value (i.e. white theme uses white)

relative colors with light & dark hover
light
dark

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also do this with sass color.scale for better support but we have to use a sass variable as the input argument (CSS custom properties are not available as values at build time)
need-sass-variable

Copy link
Collaborator Author

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

text-decoration: none;
}

&:active {
background-color: rgba(0, 0, 0, 0.05);
}

@supports (--css: variables) {
color: var(--link-color);

&:hover,
&:focus,
&:active {
color: var(--link-color-hover);
}
}
}

:visited {
color: $link-color-visited;

&:hover,
&:focus,
&:active {
color: $link-color-visited-hover;
}

@supports (--css: variables) {
color: var(--link-color-visited);

&:hover,
&:focus,
&:active {
color: var(--link-color-visited-hover);
}
}
// 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 {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inherited values
Screenshot 2025-04-16 at 12 35 52 PM

--link-color: var(--link-color-inverse);
--link-color-visited: var(--link-color-visited-inverse);
/* stylelint-disable-next-line color-function-notation */
--link-color-with-state: hsl(from var(--link-color-inverse) h s calc(l + 10));
}

.mzp-t-dark {
@include light-links;
// any descendant link will inherit white theming (replace white-links mixin)
// link-specific theme class
.mzp-t-white-links {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inherited values
Screenshot 2025-04-16 at 12 37 39 PM

Copy link
Contributor

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.

--link-color: #{$color-white};
--link-color-visited: #{$color-white};
--link-color-with-state: #{$color-white};
}

// apply directly to link
.mzp-c-cta-link {
font-family: $button-font-family;
font-family: var(--button-font-family);
font-weight: bold;

@supports (--css: variables) {
font-family: var(--button-font-family);
}
}
99 changes: 44 additions & 55 deletions assets/sass/protocol/components/_button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@
// * -------------------------------------------------------------------------- */
// Button shape and size

.mzp-c-button, /* stylelint-disable-line no-duplicate-selectors */
a.mzp-c-button {
.mzp-c-button {
@include border-box;
@include font-size(16px);
@include transition(background-color 100ms, box-shadow 100ms, color 100ms);
Expand Down Expand Up @@ -139,8 +138,7 @@ a.mzp-c-button {
// Primary

/* stylelint-disable-next-line no-duplicate-selectors */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/* stylelint-disable-next-line no-duplicate-selectors */

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do still have a duplicate selector here (there's no mzp-t-primary theme class to distinguish this selector from the other default button styles), but this is the only place we need the disable comment now

duplicate-selector

.mzp-c-button,
a.mzp-c-button {
.mzp-c-button {
background-color: $color-black;
border: 2px solid $color-black;
color: $color-white;
Expand All @@ -160,32 +158,27 @@ a.mzp-c-button {
// * -------------------------------------------------------------------------- */
// Secondary

/* stylelint-disable-next-line no-duplicate-selectors */
.mzp-c-button,
a.mzp-c-button {
&.mzp-t-secondary {
background-color: transparent;
border-color: $color-black;
color: $color-black;
.mzp-c-button.mzp-t-secondary {
Copy link
Contributor

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 😭

background-color: transparent;
border-color: $color-black;
color: $color-black;

@include default-states;
@include default-states;

// dark
&.mzp-t-dark {
background-color: transparent;
border-color: $color-white;
color: $color-white;
// dark
&.mzp-t-dark {
background-color: transparent;
border-color: $color-white;
color: $color-white;

@include default-states-dark;
}
@include default-states-dark;
}
}

// * -------------------------------------------------------------------------- */
// Product Buttons

.mzp-c-button.mzp-t-product,
a.mzp-c-button.mzp-t-product {
.mzp-c-button.mzp-t-product {
background-color: $color-blue-50;
border-color: transparent;
color: $color-white;
Expand Down Expand Up @@ -216,51 +209,47 @@ a.mzp-c-button.mzp-t-product {
// * -------------------------------------------------------------------------- */
// Neutral Buttons

/* stylelint-disable-next-line no-duplicate-selectors */
.mzp-c-button,
a.mzp-c-button {
&.mzp-t-neutral {
background-color: transparent;
border-color: $color-marketing-gray-30;
.mzp-c-button.mzp-t-neutral {
background-color: transparent;
border-color: $color-marketing-gray-30;
color: $color-marketing-gray-70;

&:focus {
border-color: forms.$button-border-color-focus;
}

&:hover {
background-color: $color-marketing-gray-20;
border-color: $color-marketing-gray-40;
color: $color-marketing-gray-70;
}

&:active {
background-color: $color-marketing-gray-20;
border-color: $color-marketing-gray-50;
color: $color-marketing-gray-70;
}

// dark
&.mzp-t-dark {
background-color: rgba($color-white, 0.1);
border-color: rgba($color-white, 0.6);
color: $color-white;

&:focus {
border-color: forms.$button-border-color-focus;
}

&:hover {
background-color: $color-marketing-gray-20;
border-color: $color-marketing-gray-40;
color: $color-marketing-gray-70;
background-color: rgba($color-white, 0.2);
border-color: rgba($color-white, 0.6);
color: $color-white;
}

&:active {
background-color: $color-marketing-gray-20;
border-color: $color-marketing-gray-50;
color: $color-marketing-gray-70;
}

// dark
&.mzp-t-dark {
background-color: rgba($color-white, 0.1);
border-color: rgba($color-white, 0.6);
background-color: rgba($color-white, 0.2);
border-color: rgba($color-white, 0.4);
color: $color-white;

&:focus {
border-color: forms.$button-border-color-focus;
}

&:hover {
background-color: rgba($color-white, 0.2);
border-color: rgba($color-white, 0.6);
color: $color-white;
}

&:active {
background-color: rgba($color-white, 0.2);
border-color: rgba($color-white, 0.4);
color: $color-white;
}
}
}
}
Expand Down
61 changes: 12 additions & 49 deletions assets/sass/protocol/includes/mixins/_utils.scss
Original file line number Diff line number Diff line change
Expand Up @@ -223,61 +223,24 @@
}

// Light color links for dark backgrounds.
// Prefer mzp-t-dark class in markup where possible
@mixin light-links {
a:link {
color: themes.$link-color-inverse;
}

a:visited {
color: themes.$link-color-visited-inverse;
}

a:hover,
a:focus,
a:active {
color: themes.$link-color-hover-inverse;
}

a:visited:hover,
a:visited:focus,
a:visited:active {
color: themes.$link-color-visited-hover-inverse;
}

@supports (--css: variables) {
a:link {
color: var(--link-color-inverse);
}

a:visited {
color: var(--link-color-visited-inverse);
}

a:hover,
a:focus,
a:active {
color: var(--link-color-hover-inverse);
}

a:visited:hover,
a:visited:focus,
a:visited:active {
color: var(--link-color-visited-hover-inverse);
}
a {
--link-color: var(--link-color-inverse);
--link-color-hover: var(--link-color-hover-inverse);
--link-color-visited: var(--link-color-visited-inverse);
--link-color-visited-hover:var(--link-color-visited-hover-inverse);
}
}

// White color links for dark backgrounds, when link colors are undesirable.
// Prefer mzp-t-white-links class in markup where possible
@mixin white-links {
a:link,
a:visited {
color: tokens.$color-white;

&:hover,
&:focus,
&:active {
color: tokens.$color-white;
}
a {
--link-color: #{tokens.$color-white};
--link-color-hover: #{tokens.$color-white};
--link-color-visited: #{tokens.$color-white};
--link-color-visited-hover: #{tokens.$color-white};
}
}

Expand Down
2 changes: 1 addition & 1 deletion assets/sass/protocol/includes/themes/_firefox.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
--link-color-hover: #{tokens.$color-link-hover};
--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};
Copy link
Contributor

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.

--link-color-visited-inverse: #{tokens.$color-violet-10};
--link-color-visited: #{tokens.$color-link-visited};
--link-color: #{tokens.$color-link};
Expand Down
2 changes: 1 addition & 1 deletion assets/sass/protocol/includes/themes/_mozilla.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
--link-color-hover: #{tokens.$color-link-hover};
--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};
--link-color-visited-inverse: #{tokens.$color-violet-10};
--link-color-visited: #{tokens.$color-link-visited};
--link-color: #{tokens.$color-link};
Expand Down