Skip to content
This repository has been archived by the owner on Jul 19, 2021. It is now read-only.

CSS Custom Variable - SCSS operations and functions #503

Closed
jonathanmoore opened this issue Apr 19, 2018 · 11 comments
Closed

CSS Custom Variable - SCSS operations and functions #503

jonathanmoore opened this issue Apr 19, 2018 · 11 comments

Comments

@jonathanmoore
Copy link
Contributor

Problem

With the new workflow of defining css-variables.liquid that are then imported into a theme's SCSS file, it currently is not possible to utilize any SCSS color functions. Hypothetically this would apply to all other SCSS functions/mixins that need to be applied to values from the theme editor.

snippets/css-variables.liquid

<style>
  :root {
    --color-body-text: {{ settings.color_body_text }};
    --color-main-background: {{ settings.color_main_bg }};
  }
</style>

styles/theme.scss

$color-body: var(--color-main-background);
$color-body-text: var(--color-body-text);

// SCSS blend of colors
$color-body-blend: mix($color-body, $color-body-text, 50%);

Error:

Error in ./assets/styles/theme.scss)
Module build failed:
$color-body-blend: mix($color-body, $color-body-text, 50%);
^
Argument $color-1 of mix($color-1, $color-2, $weight: 50%) must be a color

More Information

Obviously this one basic example could be rewritten to use basic Liquid color filters in the css-variables file. More complex color functionality like checking for brightness on a background color and setting an a11y contrast compatible text color would result in very complex liquid

@t-kelly
Copy link
Contributor

t-kelly commented Apr 19, 2018

@jonathanmoore you're correct in that the replacement solution would be to use Liquid colour filters:

<style>
  :root {
    --color-body-text: {{ settings.color_body_text | color_modify: 'red', 255 }};
    --color-main-background: {{ settings.color_main_bg | color_lighten: 30 }};
  }
</style>

We could continue to add to this list of available filters. Besides a colour contrast filter, are there any other filters that would be useful for you to replace SCSS functionality?

@bertiful
Copy link
Contributor

This is a larger issue with using custom properties with SCSS.

There is currently no way, from what I see, to use custom properties inside of SCSS functions/mixins. I learned this the hard way, and it now makes sense why it doesn't work as expected. An example I ran into was the vertical rhythm implementation (using the Plumber Sass library): Shopify/starter-theme#1. I'm going to keep thinking about this 🤔.

@jonathanmoore
Copy link
Contributor Author

I am completely stumped on a solution to fit basic and common SCSS functionality into the new concept of CSS variables. Here is a scenario where our current and future themes that will not work with the new version of Slate:

  • Currently we give merchants a choice to set accent colors for the primary elements, header, footer and other sections/blocks like slideshows
  • When we use the accent color for a background on a button or link we use a SCSS function to determine if the text color should be white or black based on the lightness of the accent.
@function get-color-accent-text($color) {
  @if (lightness($color) > 50) {
    @return #000000;
  } @else {
    @return #ffffff;
  }
}

$color-accent: {{ settings.color_accent }};
$color-accent-text: get-color-accent-text($color-accent);

In working with Slate 1.0.0 beta there does not seem to be a way to pull off any {% if %} logic, and we are limited to long chains of filters. Here is what I attempted, however on deploy the assign values from snippets/css-variables.liquid are not available in the generated .css.liquid file.

{%- assign color_accent_lightness = settings.color_accent | color_brightness | divided_by: 255 | times: 100 -%}
{%- if color_accent_lightness > 50 -%}
  {%- assign color_accent_text = '#000000' -%}
{%- else -%}
  {%- assign color_accent_text = '#FFFFFF' -%}
{%- endif -%}
--color-accent-text: {{ color_accent_text }};

@jonathanmoore
Copy link
Contributor Author

To take it even further, we have plans to improve the contrast for accessibility by using the YIQ color space which provides better results over brightness.
http://dannyruchtie.com/color-contrast-calculator-with-yiq/
https://medium.com/@MikeKellyWeb/calculating-color-contrast-with-sass-eff39ef23f96
image

Perhaps this would be a good example for a new liquid color filter...

Input: #7AB55C
{{ '#7AB55C' | color_contrast }}
Output: #000000

Input: #F33D6A with manually overwriting the dark and light value
{{ '#F33D6A' | color_contrast: dark: '#222222', light: '#DDDDDD' }}
Output: #DDDDDD

I still there was a way to preserve some basic SCSS that could be rendered by Shopify's servers. Being able to run the theme's dynamic settings through logic is quite helpful.

@jonathanmoore
Copy link
Contributor Author

jonathanmoore commented May 17, 2018

More information to expand this issue beyond color example...

When relying on CSS variables to locally compile SCSS files for development or production, Sass will fail to build if you apply any operations to Sass variables assigned by CSS variables.

Replication steps

  1. Add a settings_schema.json setting for a base font size, base_font_size, with values ranging 15px-20px.
  2. Assign CSS value in `css-variables.liquid
<style>
  :root {
    --base-font-size: {{ settings.base_font_size }};
  }
</style>
  1. In one of the SCSS files, applying any type of operation to a CSS variable will result in the build error.
$base-font-size: var(--base-font-size);
body { font-size: $baseFontSize; }
h1 { font-size: $baseFontSize * 2; }
$base-font-size: var(--base-font-size);
@function em($target, $context: $base-font-size) {
  @return ($target / $context) * 1em;
}
body { font-size: $base-font-size; }
h1 { font-size: em(28px); }
  1. Example error
ERROR in ./assets/styles/theme.scss
Module build failed: ModuleBuildError: Module build failed:
  font-size: $base-font-size * 2;
            ^
      Undefined operation: "var(--base-font-size) times 2".

More Information

Researching into the use of CSS variables along with Sass, it seems like you would have to use calc(). I don't know if there is a good workaround that isn't going to feel like a big hack. Overall this seems like it would have a big negative impact on the usefulness of Sass.

Trying to fit a complex theme into the current CSS vars approach would result in several hundred lines of code to chain together liquid assigns and filters, css vars and scss variables.

@jonathanmoore jonathanmoore changed the title CSS Custom Variable - SCSS functions CSS Custom Variable - SCSS operations and functions May 17, 2018
@t-kelly
Copy link
Contributor

t-kelly commented May 23, 2018

Update - we shipped some more Liquid color filters!

@jonathanmoore
Copy link
Contributor Author

Now understanding where Slate is heading with CSS Vars, I have finally reworked our theme code to fit into the concept of Slate. I just wanted to follow up and document some details in case anyone else runs into similar challenges.

In general Slate will not allow for SCSS processing or manipulation of theme settings values. This is something we have taken advantage of in the past, so I had to approach processing colors, font sizes, etc from a different direction. Now we are using Liquid to manipulate colors or sizes and pass it along directly to the CSS vars. I see this approach as having two advantages:

  1. As @t-kelly mentioned above, Shopify can fairly easily (and quickly) roll out new liquid filters as needed
  2. Using CSS Vars to pass values along to CSS is the way that things are heading. Once IE11 is no longer a requirement this opens the door to liquid-free CSS.

Here are the two ways I'm now solving the examples detailed above...

Calculating Accent Text Color (YIQ color profile)

{%- assign color_light_contrast = '#ffffff' -%}
{%- assign color_dark_contrast = '#000000' -%}

{%- comment -%}
  Color - Primary Accent Text
{%- endcomment -%}
{%- assign color_r = color_primary_accent | color_extract: 'red' -%}
{%- assign color_g = color_primary_accent | color_extract: 'green' -%}
{%- assign color_b = color_primary_accent | color_extract: 'blue' -%}
{%- assign color_y_r = color_r | times: 0.299 -%}
{%- assign color_y_g = color_g | times: 0.587 -%}
{%- assign color_y_b = color_b | times: 0.114 -%}
{%- assign color_y = color_y_r | plus: color_y_g | plus: color_y_b | divided_by: 255 -%}
{%- if color_y >= 0.5 -%}
  {%- assign color_primary_accent_text = color_light_contrast -%}
{%- else -%}
  {%- assign color_primary_accent_text = color_light_contrast -%}
{%- endif -%}

Font Size Variations

The theme's settings allow for a choice of 16px to 20px as the base font size (body size), and then we are using REM or EM throughout the style to alter the size for various use. However, we wanted to offer a theme setting where merchants could control how large or small heading font sizes are. Rather than assigning pixel values for the headings, the values range from 0.75 to 1.5. Then the theme's CSS will use calc() to multiply the default heading REM size by the setting value.

<!-- css-variables.liquid -->
<style>
  :root {
    --typography-body-size: {{ setting.typography_body_size }}; // 16px to 20px
    --typography-heading-size: {{ setting.typography_heading_size }}; // 0.75 to 1.5
    --typography-base-line-height: 1.5rem;
    --
  }
</style>
// theme.scss
$typography-body-size: val(--typography-body-size);
$typography-heading-size: val(--typography-heading-size);
$typography-base-line-height: val(--typography-base-line-height);

@mixin calc-font-size(
  $font-size: $typography-body-size,
  $line-height: $typography-base-line-height,
  $size: 1
) {
  font-size: calc(#{$font-size} * #{$size});
  line-height: calc(#{$line-height} * #{$size});
}

html {
  font-size: $typography-body-size;
}
body {
  font-size: 1rem;
  line-height: $typography-base-line-height;
}
h1 {
  @include calc-font-size(2.5rem, 3rem, $typography-heading-size);
}
h2 {
  @include calc-font-size(2rem, 2.5rem, $typography-heading-size);
}

@georgebutter
Copy link

are there any other filters that would be useful for you to replace SCSS functionality?

Using css vars you cannot modify opacity like you previously could rgba($colorHighlight, 0.35).
Would this be something that would require a liquid filter now? Or is there a more obvious approach that I am missing?

I'm not sure I understand the full benefits of using css vars as opposed to scss vars.

@jonathanmoore
Copy link
Contributor Author

@ButsAndCats There is a liquid filter that would take care of that example...
{{ settings.color_highlight | color_modify: 'alpha', 0.35 }}

Although the current version of Slate still is missing the ability to pass along usable assigned liquid variables. Solution in the works
#541

@montalvomiguelo
Copy link

Here is another example for adaptive colors by using color filters

src/snippets/liquid-variables.liquid

{%- capture color_btn_primary_focus -%}
  {%- assign color = settings.color_button  -%}
  {%- assign color_brightness = color | color_brightness -%}
  {%- if color_brightness <= 26 -%}
    {{- color | color_lighten: 25 -}}
  {%- elsif color_brightness <= 64 -%}
    {{- color | color_lighten: 15 -}}
  {%- else -%}
    {{- color | color_darken: 10 -}}
  {%- endif -%}
{% endcapture %}

src/snippets/css-variables.liquid

{%- include 'liquid-variables' -%}

<style>
  :root {
    --color-btn-primary-focus: {{ color_btn_primary_focus }};
  }
</style>

https://github.com/montalvomiguelo/starter-debut/blob/liquid-variables/src/snippets/liquid-variables.liquid

@lock
Copy link

lock bot commented Oct 26, 2018

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Oct 26, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants