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

Issue-2464: Add color luminosity options #2566

Merged
merged 11 commits into from
Sep 26, 2022

Conversation

fbuys
Copy link
Contributor

@fbuys fbuys commented Sep 19, 2022

Summary

Closes: #2464

We added 3 new config options.

  1. Ability to specify light or dark a hex color.
  2. Ability to specify hue, saturation or lightness when generating a hex color.
  3. Ability to specify hue, saturation or lightness when generating an HSL color.

The change to the HSL color generator enables us to easily generate a light / dark color since the lightness (In HSL) directly affect the color's luminosity.

We added a private method (not directly tested) that converts and HSL value into a HEX color value.
The conversion algorithm was heavily inspired by:

The original issue suggested we implement it as follows:

  • Faker::Color.hex_color(luminosity: 'dark')
  • Faker::Color.hex_color(luminosity: 'light')

Instead I implemented with a slightly less verbose syntax, but I am open to changing it.

  • Faker::Color.hex_color(:dark) -> generates a color with lightness set to 20%
  • Faker::Color.hex_color(:light)-> generates a color with lightness set to 80%

A side effect of this change is that you can now also (optionally) provide a specific HSL values when generating a hex or hsl color.

  • Faker::Color.hex_color(hue: 100, saturation: 0.52, lightness: 0.3) will generate the specified color.
  • Faker::Color.hsl_color(hue: 100, saturation: 0.52, lightness: 0.3) will generate the specified color.

Other Information

If you would like to manually verify that the generated color is light/dark.

  1. Output the generated value with something like this: puts @tester.hex_color(:light)
  2. Then insert the generated hex value here: https://colordesigner.io/convert/hextohsl
  3. Confirm that the color is light (with a lightness of 80% / 0.8)
  4. Similar process can be followed for `:dark: colors.
  5. Generate a new hex_color, by specifying the hue, saturation or lightness - use this color picker to confirm that the generated color is correct: https://duckduckgo.com/?q=color+picker+%23ee54f8&atb=v341-3&ia=answer
  6. Generate a new hsl_color, by specifying the hue, saturation or lightness - use this color picker to confirm that the generated color is correct: https://duckduckgo.com/?q=color+picker+%23ee54f8&atb=v341-3&ia=answer

So we can generate lighter and darker colors as requested in the issue:
faker-ruby#2464.

Next step is to convert a light/dark hsl to hex.
So we can easily specify when we want light or dark colors, since hsl
values have a very intuitive way to specify light and dark colors.

See: faker-ruby#2464
New param allows user to pass in :light for light hex color and :dark
for dark hex colors.

Additional specs confirm that this works.

See: faker-ruby#2464
def hex_color
format('#%06x', (rand * 0xffffff))
def hex_color(lightness = nil)
lightness_lookup = { light: 0.8, dark: 0.2 }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Might become a constant?

Copy link
Contributor

Choose a reason for hiding this comment

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

A constant for this would be good

test/faker/default/test_faker_color.rb Show resolved Hide resolved
@nickjj
Copy link

nickjj commented Sep 20, 2022

This isn't the prettiest code in the world but it's a code snippet I use to determine if a color should get white or black text based on whether or not the background is dark or light:

  def contrast_color(hex)
    yiq_threshold = 128

    # TODO: This doesn't account for 3 character hex codes.
    red, green, blue = hex.scan(/../).map{ |c| c.to_i(16) }

    yiq = ((red * 299) + (green * 587) + (blue * 114)) / 1000
    yiq > yiq_threshold ? "#000" : "#fff"
  end

It's a well known algorithm https://24ways.org/2010/calculating-color-contrast/ , here's a JS demo: http://jsfiddle.net/decx/RRt3q/. It could be useful to help test this PR because a dark color should always produce contrasted white text.

@fbuys
Copy link
Contributor Author

fbuys commented Sep 22, 2022

yiq_threshold = 128

    # TODO: This doesn't account for 3 character hex codes.
    red, green, blue = hex.scan(/../).map{ |c| c.to_i(16) }

    yiq = ((red * 299) + (green * 587) + (blue * 114)) / 1000
    yiq > yiq_threshold ? "#000" : "#fff"

The interesting thing is that not all light colors contrast well with black for example.
This is a light color (#b6e2b6) but it contrasts better with white.

So perhaps the question here is if we want a color that contrasts with white or black?
This color does have a lightness of 80%, so it is a light color by that definition.

@nickjj
Copy link

nickjj commented Sep 23, 2022

Text size does play a role here. What size were you testing it on?

If you use a contrast checker such as https://webaim.org/resources/contrastchecker/, having black text with a #b6e2b6 background produces a 14.54:1 ratio where as white text is 1.44:1.

Personally on both my monitors (IPS displays) the black text is much easier to read. White text fails the contrast test checker.

Copy link
Contributor

@Zeragamba Zeragamba left a comment

Choose a reason for hiding this comment

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

Just a few minor changes and LGTM

def hex_color
format('#%06x', (rand * 0xffffff))
def hex_color(lightness = nil)
lightness_lookup = { light: 0.8, dark: 0.2 }
Copy link
Contributor

Choose a reason for hiding this comment

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

A constant for this would be good

lib/faker/default/color.rb Outdated Show resolved Hide resolved
@nickjj
Copy link

nickjj commented Sep 23, 2022

This is just a thought but what do you think about introducing a way to let users customize the lightness?

For example, let's say I want to generate dark colors but 0.2 is too dark and I'd like to use 0.3. At the moment we can't do that.

Something like being able to do hex_color(lumonisity: 0.3) could be useful. For quality of life enhancements you could allow hex_color(:dark) or hex_color(:light) and the function can internally convert those to 0.8 and 0.2 so that the implementation only ever has to deal with a value between 1.0 and 0?

Adding specs to confirm this works, and also prevents specifying invalid
hsl colors.

@see faker-ruby#2566 (comment)
So it cash be used to pass values (such as hue and saturation) to
hsl_color.

See: faker-ruby#2566 (comment)
So clients are able to generate very specific colors when needed.

See: faker-ruby#2566 (comment)
@fbuys
Copy link
Contributor Author

fbuys commented Sep 26, 2022

Hey @Zeragamba and @nickjj I have pushed some updates to address your feedback. Please let me know what you think or if you have concerns with the implementation. Thank you!

lib/faker/default/color.rb Outdated Show resolved Hide resolved
@fbuys fbuys requested review from nickjj and Zeragamba and removed request for Zeragamba and nickjj September 26, 2022 17:02
@fbuys fbuys requested review from nickjj and Zeragamba and removed request for Zeragamba and nickjj September 26, 2022 17:03
@nickjj
Copy link

nickjj commented Sep 26, 2022

Based on the documented example usage this looks great, thanks for making it so customizable. This will let folks pick any type of variety category they prefer.

There's also neat options that we can do now like: Faker::Color.hex_color(lightness: [0.5, 0.6, 0.7].sample)

@Zeragamba
Copy link
Contributor

Tests be passin, I be sayin LGTM, and we be mergin.

Thanks for the feature!

@Zeragamba Zeragamba merged commit a7746b9 into faker-ruby:master Sep 26, 2022
sudeeptarlekar pushed a commit to sudeeptarlekar/faker that referenced this pull request Oct 7, 2022
* Add ability to override hsl_color lightness

So we can generate lighter and darker colors as requested in the issue:
faker-ruby#2464.

Next step is to convert a light/dark hsl to hex.

* Use hsl_color and hsl_to_hex to generate hex_color

So we can easily specify when we want light or dark colors, since hsl
values have a very intuitive way to specify light and dark colors.

See: faker-ruby#2464

* Add ability to specify light or dark hex color

New param allows user to pass in :light for light hex color and :dark
for dark hex colors.

Additional specs confirm that this works.

See: faker-ruby#2464

* Update version to next

As per: https://github.com/faker-ruby/faker/blob/master/CONTRIBUTING.md#documentation

* Fix rubocop linting issue

* Allow specific hue and saturation values for hsl_color

Adding specs to confirm this works, and also prevents specifying invalid
hsl colors.

@see faker-ruby#2566 (comment)

* Add LIGHTNESS_LOOKUP constant

@see faker-ruby#2566 (comment)

* Introduct hsl_hash to hex_color

So it cash be used to pass values (such as hue and saturation) to
hsl_color.

See: faker-ruby#2566 (comment)

* Rename lightness to args

To make it more general.

@see faker-ruby#2566 (comment)

* Allow hue, saturation or lightness for hex colors

So clients are able to generate very specific colors when needed.

See: faker-ruby#2566 (comment)

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

Successfully merging this pull request may close these issues.

Ability to generate dark and light hex colors with a new optional luminosity option
4 participants