Skip to content
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

Add CSS Custom Properties to Protocol Themes #841

Merged
merged 14 commits into from
Jul 25, 2023
Merged

Conversation

nathan-barrett
Copy link
Contributor

@nathan-barrett nathan-barrett commented Jan 12, 2023

Description

This PR updates the themes in Protocol to use CSS Custom Properties over sass variable maps. To support legacy browsers, sass variables were created that will be used as a fallback. I've also added my notes to the bottom of the description if anyone needs a refresher

  • I have documented this change in the design system.
  • I have recorded this change in CHANGELOG.md.

Testing

To test that these changes are working I suggest using npm link(article on npm-link) since we haven't pushed any of the changes in protocol to the npm package. Directions below:

To test this works: (you will need a local version of both protocol and bedrock to test these changes)

In local protocol directory

  • npm run build-package (this will build the package)
  • cd package && npm link

In local bedrock

  • checkout the branch origin/protocol-update-test
  • npm link @mozilla-protocol/core (This will use the linked protocol package that was just built instead of using the NPM package)
  • add @import '~@mozilla-protocol/core/protocol/css/includes/themes'; to protocol-mozilla.scssandprotocol-firefox.scss`
  • npm run start
  • the server should start without any compilation errors, and there shouldnt be any style differences between prod and what you are viewing locally

Notes on Custom Properties

Blog Posts

How to create better themes with CSS variables - LogRocket Blog

Theming with CSS variables

Color Theme Switcher

Why we prefer CSS Custom Properties to SASS variables | CodyHouse

In SCSS:

$color-primary: hsl(220, 90%, 56%);

.link {
  color: $color-primary;
}

In CSS:

:root {
  --color-primary: hsl(220, 90%, 56%);
}

.link {
  color: var(--color-primary);
}

Differences between the two:

  • Custom Properties can be accessed at run time and stay in CSS code using --variable-name syntax that browsers recognize

    • you can preview all of the CSS variables in the browser

    • CSS custom properties can be accessed in JavaScript

      // retrive the root element
      const element = document.documentElement
      
      // get variable from the inline style
      element.style.getPropertyValue("--bg-color");
      OR
      // get variable from element
      getComputedStyle(element).getPropertyValue("--bg-color");
      
      // update variable value
      element.style.setProperty("--bg-color", "orange");
  • CSS custom properties can accept a fallback value: var(--variable-name, fallbackValue)

  • Custom Properties are scoped to an element, most commonly the :root element

Theming with Custom Properties

Currently in protocol we use this function to retrieve variables depending on the theme:

// Get a theme variable from the collected map.
// This swaps the map based on the value of $brand-theme.
// Theme maps are defined in includes/_theme.scss

@function get-theme($var) {
    // Default to Firefox theme
    $theme-map: $theme-firefox;

    // Switch to Mozilla theme
    @if $brand-theme == 'mozilla' {
        $theme-map: $theme-mozilla;
    }

    @if not map-has-key($theme-map, $var) {
        @error '`#{$var}` is not a valid theme variable. ' +
								'Valid theme variable names: #{map-keys($theme-map)}';
    }

    @return map-get($theme-map, $var);
}

// and are used like this:

body {
    @include text-body-md;
    background: get-theme('background-color');
    color: get-theme('body-text-color');
    font-family: get-theme('body-font-family');
    line-height: type-scale('body-line-height');
}

With Custom properties, the variables would be set up like this:

// When using SASS variables in custom properties, 
// they need to be nested inside of #{}
:root {
	--bg-color: #{$color-blue};
	// etc etc...

	@if $brand-theme == 'mozilla' {
       --bg-color: #{$color-black};
  } @else if $brand-theme == 'firefox' {
       --bg-color: #{$color-orange};
  }
}

body {
    @include text-body-md;
    background: get-theme('background-color');
    color: get-theme('body-text-color');
    font-family: get-theme('body-font-family');
    line-height: type-scale('body-line-height');

    @supports (--css: variable) {
        background: var(--background-color);
        color: var(--body-text-color);
        font-family: var(--body-font-family);
				 line-height: var(--body-line-height);
    }
}

When used with #{} removes quotation marks from quoted strings, it’s sometimes necessary to wrap them in the meta.inspect() function to preserve quotes:

@use "sass:meta";

$font-stack-mozilla: 'Zilla Slab', Inter, X-LocaleSpecific, sans-serif;;

:root {
  --font-stack-mozilla: #{meta.inspect($font-stack-mozilla)};
}

@reemhamz reemhamz self-requested a review January 16, 2023 16:22
Copy link
Contributor

@reemhamz reemhamz left a comment

Choose a reason for hiding this comment

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

What a long PR!
You did such a great job getting this done with almost zero nits and fixes!
Most of the nits I found were spelling errors in the markdown files, though.

GOOD JOB!!!!!!!!!!!!! 🕌

r+wc

assets/sass/protocol/includes/_mozilla.scss Outdated Show resolved Hide resolved
assets/sass/protocol/includes/_themes.scss Outdated Show resolved Hide resolved
docs/01-fundamentals/02-themes.md Outdated Show resolved Hide resolved
docs/01-fundamentals/02-themes.md Outdated Show resolved Hide resolved
docs/02-usage/02-framework.md Outdated Show resolved Hide resolved
docs/02-usage/02-framework.md Outdated Show resolved Hide resolved
docs/01-fundamentals/02-themes.md Outdated Show resolved Hide resolved
docs/02-usage/02-framework.md Outdated Show resolved Hide resolved
@nathan-barrett nathan-barrett added Needs:Review 👋 Ready for Developer Review and removed Do Not Merge ⚠️ Do Not Merge labels Feb 2, 2023
Copy link
Contributor

@maureenlholland maureenlholland left a comment

Choose a reason for hiding this comment

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

🎉 to echo Reem, this is a whole lot of great work. Really clean enhancement.

I did a code review (with a few todo notes to myself for tomorrow) and will do the local testing review tomorrow. Thought I'd put those comments out today for async discussion.

CHANGELOG.md Show resolved Hide resolved
assets/sass/protocol/base/elements/_containers.scss Outdated Show resolved Hide resolved
@@ -129,7 +169,7 @@
.mzp-c-split-body {
@include border-box;
@include bidi(((float, left, right),));
padding: 0 (get-theme('h-grid-lg') * 0.5);
padding: 0 ($h-grid-lg * 0.5);
Copy link
Contributor

Choose a reason for hiding this comment

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

curious: how is this working without calc?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

lol! no clue, going to add calc in to just be extra sure it works.

Copy link
Contributor

Choose a reason for hiding this comment

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

🤷‍♀️ it works in both cases, no idea why in the first, seeing calc feels better for consistency 👍

@@ -6,6 +6,69 @@
// themes. Mixins and functions can draw from these variable maps, swapping to
// a different map based on the value of the global $brand-theme variable.

// Default "theme" that will be served to legacy browsers - IE 11 etc
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 great comment

assets/sass/protocol/includes/forms/_lib.scss Outdated Show resolved Hide resolved
@extend %mozilla-theme;
@extend %mozilla-type-scale;

@if $type-scale == 'condensed' {
Copy link
Contributor

Choose a reason for hiding this comment

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

👏 I was thinking all the custom properties would be set on the :root inside the individual theme files but that would make the type scale logic trickier. This is an effective, readable way of breaking them up.

docs/01-fundamentals/02-themes.md Outdated Show resolved Hide resolved
docs/02-usage/02-framework.md Outdated Show resolved Hide resolved
@@ -44,5 +44,5 @@ $type-scale: 'standard' !default;
// Import all the things!
@import './node_modules/@mozilla-protocol/tokens/dist/index';
@import 'functions';
Copy link
Contributor

Choose a reason for hiding this comment

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

curious: are we keeping the get-theme and type-scale functions in code for a while? I don't see them deleted (and I guess if they are not deleted, we don't have to make this a breaking change?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't want to delete them, since it would introduce a breaking change - but if we should go all out and remove them here I'd be happy to do so. @craigcook

Copy link
Contributor

Choose a reason for hiding this comment

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

Happy to leave as-is in this PR.

Copy link
Member

Choose a reason for hiding this comment

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

It might be worth keeping them for a while and adding a deprecation notice like we've done with some components, to give some transition time for anyone using those functions. But I'm not sure how widely they're used outside of Protocol itself... we only use get-theme in a few places in bedrock.

Copy link
Contributor

Choose a reason for hiding this comment

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

wonder how difficult it would be to write a migration script to do a search and replace (potentially with a flag setting for optional support query wrapping):

color: get-theme('title-text-color');
/* replace only */
color: var(--title-text-color);

@include bidi(((padding-left, get-theme('h-grid-sm'), padding-right, get-theme('h-grid-sm')),))
/* replace & use support query */
@support (--css: variables) {
    @include bidi(((padding-left, var(--h-grid-sm), padding-right, var(--h-grid-sm)),))
}

I'm on the fence about including the support queries in end user code. I think it's important for clarity in Protocol, but since we've set all the IE defaults already, it should be safe for users to directly set custom property values on their style rules. Browser parsers that don't support custom properties will ignore those rules and use the Protocol defaults.

Copy link
Contributor

@maureenlholland maureenlholland left a comment

Choose a reason for hiding this comment

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

A couple of minor bugs noted in the previous review

one more blocking issue from local testing of Protocol docs: the custom properties are not set in demos, i.e. http://localhost:3000/components/detail/paragraphs
Screenshot 2023-02-16 at 12 32 05 PM

local
Screenshot 2023-02-16 at 12 28 42 PM

live
Screenshot 2023-02-16 at 12 28 37 PM

I do think this counts as a breaking change if end users need to add a themes import.

If we want to try avoiding the duplicated :root rule declarations we ran into with @import, I started a POC here: f5b656b#diff-6da31b8d13d36754703085ab3c9ae106a4d80abb5f51e3a5db324042d4a9e63fR6

I ran into the "can't locate protocol tokens" error when trying to run a bedrock test. I think you had a solution for this, but I can't remember it. Happy to help try and debug that if we think it's worth pursuing.


// colors
--background-color-tertiary-inverse: #{$color-dark-gray-40};
--background-color-tertiary: #{$color-marketing-gray-20};
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (blocking): mismatched custom property value with original moz theme value

Suggested change
--background-color-tertiary: #{$color-marketing-gray-20};
--background-color-tertiary: #{$color-light-gray-30};

https://github.com/mozilla/protocol/blob/main/assets/sass/protocol/includes/_themes.scss#L71

@nathan-barrett
Copy link
Contributor Author

@maureenlholland made some fixes and tested in protocol docs and bedrock and things looked good to me

Copy link
Contributor

@maureenlholland maureenlholland left a comment

Choose a reason for hiding this comment

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

👏 👏 👏 r+wc (mozilla tertiary background value is out of sync with prod)

My two cents (I think we discussed similar before) is we should bundle this change with a sass module update and be loud about a breaking release that brings us up to date with sass internally and creates new theming flexibility for Protocol users externally


// colors
--background-color-tertiary-inverse: #{$color-dark-gray-40};
--background-color-tertiary: #{$color-marketing-gray-20};
Copy link
Contributor

Choose a reason for hiding this comment

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

still need a minor fix here because we have a different mozilla tertiary value than in prod (I don't think anything is using this tertiary background at the moment, but it could cause confusion later)
Screenshot 2023-02-23 at 11 25 52 AM

can have different values for different brands.
the Mozilla and Firefox brands. Themes work by declaring a set of CSS Custom Properties
on the document's root element (`:root`) in `/includes/_themes.scss` and are retrieved
using the `var()` function. The custom properties will be updated depending on what theme is declared.
Copy link
Contributor

@maureenlholland maureenlholland Feb 23, 2023

Choose a reason for hiding this comment

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

(non-blocking, just noting for a potential future docs update)
This is a very exciting change because we're not compiling away sass variables with internal functions anymore. We're using CSS inheritance. This allows end users more flexibility if the pre-defined Protocol themes don't apply (i.e. they are early days in the branding journey and want to experiment before creating an official Protocol theme).

We could provide a list of themeable CSS properties and demo how a user can define their own theme by declaring these properties after the theme declarations

/* user-lib.scss */
@use '~@mozilla-protocol/core/protocol/css/includes/themes';
/* user-defined theme values */
:root {
    --background-color-inverse: #{$color-green-80};
}

Screenshot 2023-02-23 at 12 01 02 PM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

such a good idea!

@nathan-barrett
Copy link
Contributor Author

nathan-barrett commented Feb 23, 2023

Agree on bundling this with use and forward updates! Thanks for all of your help sifting through this @maureenlholland @reemhamz

@maureenlholland
Copy link
Contributor

as discussed internally, waiting on a few non-breaking changes to create one more published version before creating a breaking change version

@nathan-barrett nathan-barrett removed the Needs:Review 👋 Ready for Developer Review label Jul 20, 2023
@alexgibson
Copy link
Member

When I run the protocol docs site locally, many of the components seem to be missing their theme styling?

image

image

image

Copy link
Member

@alexgibson alexgibson left a comment

Choose a reason for hiding this comment

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

I haven't gone as far as testing the bedrock changes, but it looks like things aren't quite working right on the protocol docs pages. I'm happy to take another look once things are working there.

I think we should also do some testing in legacy browsers (in bedrock) to make sure that the default theming is working as expected.

assets/sass/protocol/protocol.scss Outdated Show resolved Hide resolved
@nathan-barrett
Copy link
Contributor Author

@alexgibson - I fixed the doc site issue you mentioned (i think the theme import was removed during rebase). I will test thoroughly in legacy browsers with bedrock.

Copy link
Member

@alexgibson alexgibson left a comment

Choose a reason for hiding this comment

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

The code changes here look good to me and work well locally, nice work! r+

I do have one question though on something I'm not 100% clear on: at this point are we saying get-theme() and type-scale() should no longer be used?

If so, how are we planning to end of life their use? There are a couple of options I can think of:

  1. We could warn people that they are deprecated and provide a migration guide, later removing the functions in a future major version bump?
  2. We could remove the functions now and provide a guide, forcing a hard requirement for sites to remove their use of the functions before this next update.

Looking at mozilla/bedrock#13146 for example, I still see many calls to get-theme(), throughout bedrock. Does that mean there's likely pages using a mixture of both the new and old method for theming?

@nathan-barrett
Copy link
Contributor Author

@alexgibson - I think option two makes the most sense. And I will write up a migration guide in the changelog that we can add when we push the next major version.

For bedrock, I would like to remove the refrences to the get-theme and get-type functions - just didn't do it in that PR.

@alexgibson
Copy link
Member

Sounds good @nathan-barrett 👍

@nathan-barrett nathan-barrett merged commit e244b27 into main Jul 25, 2023
2 checks passed
@nathan-barrett nathan-barrett deleted the themes-css-variables branch July 25, 2023 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants