sassc-ruby icon indicating copy to clipboard operation
sassc-ruby copied to clipboard

How to expose more functionality from libsass?

Open sevenseacat opened this issue 6 years ago • 4 comments

libsass appears to define a lot of colour-related functions that don't get exposed in sassc, such as lighten, darken, etc.

Can we add a way to access these functions? I can't work out how to do it, as I'm not super-familiar with FFI...

sevenseacat avatar Jan 24 '19 06:01 sevenseacat

I don't exactly follow what you are asking for. Could you please give me a code example of what you'd like to do?

bolandrm avatar Jan 24 '19 13:01 bolandrm

I ended up using an alternative gem (Chroma) to do it, but eh.

The sass gem provided methods to adjust colours, and I can see that the code for libsass provides methods to do it also, in functions.cpp (lighten, darken, etc), but these methods haven't been imported into Ruby. Ideally I wanted to do something like SassC::Util::Native.lighten("#4edd9e", 0.35) and get #e4faf0.

sevenseacat avatar Jan 25 '19 02:01 sevenseacat

In my case, I need to do custom bootstrap theming on the fly. I see exactly how the colors I need are derived, ultimately using the Sass "mix" function. Our CSS is generated by Webpacker during slug compliation. At run time I need to generate custom themes. Like @sevenseacat, I am forced back to Chroma in order to do this. It is somewhat frustrating because, the Libsass "mix" function is right here, but I don't see how to access it. Chroma does not provide a "mix" function, so I end up needing to fudge it. Oh well, it will work for now.

vestedpr-dev avatar Jun 23 '22 21:06 vestedpr-dev

EDIT: 3 hours later -- this works quite well. It's a funny workaround, but on the other hand I have to weight the tradeoff of incorporating the Chroma gem as a dependency, which is not maintained (even less so than sassc-ruby), and also it does not do the color transformations in the same way that Bootstrap does. Whereas this shim, albeit ugly, is very performant, and does the color transformations in exactly the same way as Bootstrap, allowing for exact matches:

# https://github.com/sass/sassc-ruby/issues/104#issuecomment-1164890694

require "sassc"

module SasscFunctions
  # https://github.com/twbs/bootstrap/blob/v5.2.0-beta1/scss/_functions.scss#L206
  def self.bootstrap_tint_color(color, weight)
    sass_mix("white", color, weight);
  end

  # https://github.com/twbs/bootstrap/blob/v5.2.0-beta1/scss/_functions.scss#L210
  def self.bootstrap_shade_color(color, weight)
    sass_mix("black", color, weight);
  end

  # https://github.com/twbs/bootstrap/blob/v5.2.0-beta1/scss/_functions.scss#L216
  def self.bootstrap_shift_color(color, weight)
    weight_int = weight.to_i
    weight_int > 0 ? bootstrap_shade_color(color, weight) : bootstrap_tint_color(color, weight)
  end

  # Using mix() is not the same as lighten() and darken()—the former blends the specified color with white or black, while the latter only adjusts the lightness value of each color. The result is a much more complete suite of colors, as shown in this CodePen demo.
  # Our tint-color() and shade-color() functions use mix() alongside our $theme-color-interval variable, which specifies a stepped percentage value for each mixed color we produce. See the scss/_functions.scss and scss/_variables.scss files for the full source code.
  # https://getbootstrap.com/docs/5.0/customize/color/#notes-on-sass
  def self.sass_mix(color1, color2, weight)
    self.sass_color_raw("mix(#{color1}, #{color2}, #{weight})")
  end

  def self.sass_to_rgb(color_string)
    red = SasscFunctions.sass_color_raw("red(#{color_string})")
    blue = SasscFunctions.sass_color_raw("blue(#{color_string})")
    green = SasscFunctions.sass_color_raw("green(#{color_string})")
    "#{red}, #{green}, #{blue}"
  end

  def self.sass_color_raw(color_string_raw)
    simple_stylesheet = "p { color: #{color_string_raw}}"
    css = SassC::Engine.new(simple_stylesheet, style: :compressed).render
    parse_resulting_color_regex = /p{color:(.+)}/
    match = parse_resulting_color_regex.match(css)
    match[1]
  end
end

vestedpr-dev avatar Jun 24 '22 00:06 vestedpr-dev