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

MDL v2 Theming Strategy #4456

Closed
traviskaufman opened this issue Jun 27, 2016 · 8 comments
Closed

MDL v2 Theming Strategy #4456

traviskaufman opened this issue Jun 27, 2016 · 8 comments

Comments

@traviskaufman
Copy link
Contributor

Continued discussion from #4447

For MDL v2 we have to figure out a strategy for theming that's maintainable, easy to use by developers, and future-forward such that frameworks using newer technologies such as CSS Variables can support it.

Original issue comment from @sgomes

Maintainability

One aspect is maintainability. How do we determine which properties should live in the base file or the theme file? Should we e.g. move all colour properties to the theme file? How about size properties? Also, is this something we do manually, or through some tool?

Customising the primary and the accent

I suspect that the simplest level of customisation that users will want to do is simply to change the primary and accent colours for their site. This actually translates into a few more colours, but we can select those automatically:

  • A contrast colour for text on top of the primary colour. This is either 66% black or 100% white, and can be calculated with a colour contrast formula.
  • A contrast colour for text on top of the accent colour. This is either 66% black or 100% white, and can be calculated with a colour contrast formula.
  • A "dark primary" colour, used in certain places. This is a darker version of the primary colour, and can also be calculated by e.g. using the 700 colour of the primary palette (assuming a Material colour), or simply by blending a certain amount of black into the primary.
  • A contrast colour for text on top of the "dark primary" colour. Same as the other contrast colours.

Our current customizer allows for downloading a custom CSS file with the entire set of MDL styles, using a particular set of colours. The theme file approach would allow us to instead provide add-on theme files, which would override the default colours.

However, how would this play with the modularisation effort? Would we have:

  • A single theme file that overrides the colours for every component, regardless of which components you're actually using? May cause some size concerns.
  • A theme file per component? Would require extra effort from developers simply to override the defaults, and the number of individual files we need to keep on our CDN (and potentially the number of npm packages we maintain) would massively increase.

Deep integration with frameworks and end-user code

The final aspect we need to consider is how we can have deep theming integration both with npm and with the frameworks we expect to use our code as a base:

  • How could we make it easy to choose a theme through npm, if the user is using the plain compiled CSS?
  • How could we make it easy to choose a theme through npm, if the user is running our sources through the preprocessor (SASS or PostCSS, depending on what we end up choosing)? This one seems easier to me, as it should simply be a case of overriding a few variables.
  • How could we allow a framework that uses CSS variables to easily style component instances individually (with e.g. --primary-color, --accent-color, etc.)?

None of these questions need to be answered on this PR, but they're interesting topics to have on the back of our minds; it may help to have some sense of the direction we're going to take early on, since it could translate into constraints on tool and architecture decisions.

@traviskaufman
Copy link
Contributor Author

Original issue comment reply:

Theming

Yeah, so, theming is a large and complicated discussion :). I'll address what I can below but maybe worth moving into a separate larger issue?

Maintainability

How do we determine which properties should live in the base file or the theme file?

I think it depends on what aspects of the component are deemed "them-able". For example, in the case of a button, we'd apply all stylistic treatments that distinguish it as a "raised button" within the base css. Then, in the theme css, we would perform color applications.

// _variables.scss
$mdl-button-raised-bg-color: white !default;
$mdl-button-raised-text-color: $mdl-typography-text-color-dark !default; // imported from mdl-typography

// mdl-button.scss
.mdl-button {
  // omitting typography stuff...
  border-radius: 2px;
  cursor: pointer;
}

.mdl-button--raised {
  box-shadow: /* .... */;
}

// mdl-button-theme.scss
.mdl-button--raised {
  background-color: $mdl-button-raised-bg-color;
  color: $mdl-button-raised-text-color;
}

Then if you wanted to override this in sass:

$mdl-button-raised-bg-color: $google-blue-500;
$mdl-button-raised-text-color: white;

@import 'mdl-button/mdl-button-theme';

Or in plain css:

/* Button CSS is loaded, but no theme css. */
.mdl-button--raised {
  background-color: #2196f3;
  color: white;
}

I imagine this would be manual, and I imagine it would primarily deal with colors. A lot of preceding implementations have taken the route of actually exposing too much as theme-able. While end users are free to alter and modify and override as they see fit, there are only certain things - mostly having to do with color - that the spec facilitates theming by default. This includes element dimensions. A button, for example, has a certain size that according to the spec should not change. However, if someone wants to make a gigantic CTA button for their site, they can simply attach an additional class and make the necessary overrides.

Customizing Primary + Accent Colors

I think you're right in that this serves as the basis for a theme. Here's what I've been thinking for this, which we can discuss further:

  • We create a sass library called mdl-theme. mdl-theme is the only package in all of MDL that has any notion of "primary" and "accent" colors. It has no dependent packages.
  • mdl-theme has implicit knowledge of all of the sass variables throughout MDL that deal with components' themes. For example, even though it doesn't depend on mdl-button, it knows about $mdl-button-bg-color.
  • mdl-theme exposes a mixin, @include mdl-theme($primary-color, $accent-color, $is-dark), that, when invoked, overrides all !default theme properties with the correctly calibrated props based on the $primary-color and $accent-color given.
  • A client can invoke mdl-theme before importing all of the theme files for components.

Example:

// mdl-theme/_mixins.scss
@mixin mdl-theme($primary-color, $accent-color) {
  // override all component-specific theme vars.
  $mdl-button-raised-bg-color: correct-bg-color($primary-color) !global;
  $mdl-button-raised-text-color: $primary-color !global;
  $mdl-checkbox-bg-color: $primary-color !global;
  // ...
}

// file-using-mdl-theme.scss

@import 'mdl-colors/variables';
@import 'mdl-theme/mixins';

// Sets up all of the correct theme variables.
@include mdl-theme($mdl-color-indigo-500, $mdl-color-pink-a200);

@include 'mdl-button/mdl-button-theme';
@include 'mdl-checkbox/mdl-checkbox-theme';
// ....

This would then override the default variables within the theme file and output the proper CSS.

This Gist demonstrates a very simple example of how this might work.

  • Pros:
    • Theming from the sass side is super simple
    • The approach is non-invasive.
    • It's modular enough to be used as part of the MDL mega-library or can be included by 3rd party users and used with components a la carte.
  • Cons:
    • Requires sass buy-in
    • ???

I actually have to take off for the day but I'll address the last part of this first thing monday.

In short:

  • That's a really interesting use case about installing a theme via npm. I never really thought about it from that perspective, but it seems like a really cool idea to be able to just pull in pre-built css theme files. Perhaps we could build out some tooling to do so?
  • For CSS variables, we could try using @supports(), the idea being:
    • We create a mixin that uses either a hard-coded sass value or a css property if it's supported
    • In addition to having sass variables, we can export them as CSS variables
@mixin css-var-prop($property-name, $css-var, $fallback-value) {
    @supports (--css-variables: 1) {
        & {
          #{$property-name}: $css-var;
        }
    }
    #{$property-name}: $fallback-value;
}

Simple example gist

@Garbee
Copy link
Collaborator

Garbee commented Jun 29, 2016

One of the things I was hoping we'd do is reduce a bunch of these "variable variables" and simple variables within the codebase. For example

$mdl-button-raised-bg-color: white !default;
$mdl-button-raised-text-color: $mdl-typography-text-color-dark !default; // imported from mdl-typography

The raised background color is just "white", there isn't any maintenance benefit provided by having this. It isn't used in any calculations in the CSS, so it isn't worth having.

The raised text color, is just a reference to the typography variable. This also isn't used in calculations for the component, therefore there isn't a benefit.

I think we should heavily look at restricting our variable usage only to places where they provide a tangible benefit. They also shouldn't necessarily be !default right away. By keeping things private first except for the global variables (theme, colors, etc.) then that allows us later to open up the internal workings as appropriate when needs are clearly articulated and we see a clear benefit to developers.

@sgomes
Copy link
Contributor

sgomes commented Jun 30, 2016

Here's a potential thought on how to handle theming across all systems we want to support. This strategy works with both PostCSS and SASS.

The idea would be for our "template" output to be valid CSS that uses CSS custom variables for theming, with a fixed colour fallback:

:root {
  --primary-color: #EF5350;
}

.md-button--raised {
  background: #EF5350;
  background: var(--primary-color);
}

This is a good thing to do anyway, as it allows us to provide theming to systems that make use of it dynamically in the browser (e.g. systems that use native CSS property support or include a polyfill).

This could easily be achieved in both SASS:

$primary-color: #EF5350;

:root {
  --primary-color: $primary-color;
}

.md-button--raised {
  background: $primary-color;
  background: var(--primary-color);
}

and in PostCSS (by cleverly using the custom property plugin's options):

:root {
  --primary-color: #EF5350;
}

.md-button--raised {
  background: var(--primary-color);
}

Once we have the output CSS, and if we wanted to use a different color (say, #42A5F5), we could simply make a textual substitution for var(--primary-color) and we'd end up with:

:root {
  --primary-color: #EF5350;
}

.md-button--raised {
  background: #EF5350;
  background: #42A5F5;
}

The second definition would override the first, and we'd have successfully customized the colors.

The only thing to ensure is that every usage of --primary-color is done correctly, without string interpolation.

The textual substitution could happen on the customizer tool, or perhaps more interestingly, as part of an npm install script. And systems that want to use the CSS custom properties would simply not do the textual substitution.

Thoughts?

@traviskaufman traviskaufman added this to the V2 Beta milestone Jun 30, 2016
@Garbee
Copy link
Collaborator

Garbee commented Jun 30, 2016

I thought is what we'd be doing anyways @sgomes... So SGTM 😉. That is the great thing about cascading and CSS Custom Properties.

@traviskaufman
Copy link
Contributor Author

@sgomes That does seem like a good approach that doesn't directly tie us to what sass is doing. It also means we don't need a separate theme CSS file. The double definitions seem a bit weird, but hopefully that goes away as custom props become supported.

@sgomes
Copy link
Contributor

sgomes commented Jul 1, 2016

@traviskaufman We could also have a quick pass with CSSO (which does work directly in the browser) or similar tools to remove the duplicates.

@traviskaufman
Copy link
Contributor Author

Since we're going with PostCSS (via #4514), can we go with saying we're going to be using custom properties / custom property sets for theming?

@traviskaufman
Copy link
Contributor Author

#4609

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants