GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — feat: Switch to culori color library by camdecoster · Pull Request #7962 · plotly/plotly.js

Conversation

@camdecoster

@camdecoster camdecoster commented Aug 13, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Description

Switch to the culori color processing library.

Closes #7961.

Changes

  • Remove color library
  • Add culori library
  • Update color functions per library change
  • Update call sites
  • Update and add tests

Screenshots

color_syntax_formats mock results

Before After
image image

Testing

  • Check CI results
  • Review the color_syntax_formats mock and try changing some of the color specifiers to valid/invalid values
  • Try entering some of the color specifiers from the mock into an element through browser devtools and see how it compares to the after results

Notes

  • This library bumps up the bundle size a bit (+17KB gzipped) but it's worth the added CSS 4 compatibility
  • We can mitigate this in the future by switching to ESM for this file (so it can be tree-shaken)
  • I removed the color-normalize dependency in favor of managing that internally

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
camdecoster added 17 commits August 12, 2026 10:46
@camdecoster
Switch to culori color library
d46b3f4
@camdecoster
Update color functions per the new API
3525cc8
@camdecoster
Update color function call sites
c87fa57
@camdecoster
Add text contrast test helper
927ab57
@camdecoster
Add/update tests
3cc82a3
@camdecoster
Update color syntax mock
7d93b21
@camdecoster
Add comments about renaming
7c85245
@camdecoster
Add array support to Color.normalize
fbc644e
@camdecoster
Default to black for undefined values in Color.stroke, Color.fill
7ab0f92
@camdecoster
Delete obsolete mock
a3e909c
@camdecoster
Revert changes to fill/stroke, rename parse, switch to previous alpha…
765ffca
… check
@camdecoster
Add more tests
9c0e4b8
@camdecoster
Delete another obsolete mock
ec45487
@camdecoster
Update mock to show whole legend
4992e04
@camdecoster
Remove dead code
1c23467
@camdecoster
Update baseline images
0403993
@camdecoster
Add draftlog
13373c7
@camdecoster
camdecoster marked this pull request as ready for review August 14, 2026 17:35
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread test/jasmine/assets/check_contrasting_text.js Outdated

nodes.each(function (_, i) {
const { fill } = this.style;
const other = Color.equals(fill, Color.background) ? Color.defaultLine : Color.background;

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
const other = Color.equals(fill, Color.background) ? Color.defaultLine : Color.background;
// Ensure text fill color matches either Color.background or Color.defaultLine
expect([Color.background, Color.defaultLine]).toContain(fill);
const other = Color.equals(fill, Color.background) ? Color.defaultLine : Color.background;

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Good catch.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread test/jasmine/tests/axes_test.js Outdated
Comment on lines +570 to +572
// A translucent axis color is what exercises the rule: the channel weight
// scales by the alpha difference, so mixing toward an opaque background
// moves the channels less than a plain interpolation would.

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
// A translucent axis color is what exercises the rule: the channel weight
// scales by the alpha difference, so mixing toward an opaque background
// moves the channels less than a plain interpolation would.
// axis.gridcolor is determined by mixing the axis color with the paper and plot background colors.
// If the axis color contains an alpha channel, its weight should scale by the alpha value, so that
// more-transparent colors are weighted less.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Updated.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread test/jasmine/tests/axes_test.js
};

supplyLayoutDefaults(layoutIn, layoutOut, fullData);
expect(layoutOut.xaxis.gridcolor).toEqual('rgba(255, 247, 0, 0.95)');

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Maybe add a comment here stating what gridcolor we would expect if the weight was not scaled by alpha?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

That's reasonable. I'll add it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread test/jasmine/tests/color_test.js
Comment on lines +22 to +23
Color.fill({ style: (o) => seen.push(o) }, undefined); // Mock the selection to track it's call

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
Color.fill({ style: (o) => seen.push(o) }, undefined); // Mock the selection to track it's call
Color.fill({ style: (o) => seen.push(o) }, undefined);

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I'm going to leave this one because I think it's non-obvious as to why the object is being passed in.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread test/jasmine/tests/color_test.js
Comment on lines +42 to +43
Color.stroke({ style: (o) => seen.push(o) }, undefined); // Mock the selection to track it's call

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
Color.stroke({ style: (o) => seen.push(o) }, undefined); // Mock the selection to track it's call
Color.stroke({ style: (o) => seen.push(o) }, undefined);

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I'm going to leave this one because I think it's non-obvious as to why the object is being passed in.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread test/jasmine/tests/color_test.js Outdated
Comment on lines +95 to +96
const other = Color.equals(picked, Color.background) ? Color.defaultLine : Color.background;

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
const other = Color.equals(picked, Color.background) ? Color.defaultLine : Color.background;
// Ensure selected color matches either Color.background or Color.defaultLine
expect([Color.background, Color.defaultLine]).toContain(picked);
const other = Color.equals(picked, Color.background) ? Color.defaultLine : Color.background;

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Updated.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread test/jasmine/tests/color_test.js Outdated
Comment on lines +115 to +117
// Drawing code needs the alpha of a color it is about to paint, which is
// not the same question `opacity` answers. A color that is simply unset
// still gets painted, so it resolves to opaque black.

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
// Drawing code needs the alpha of a color it is about to paint, which is
// not the same question `opacity` answers. A color that is simply unset
// still gets painted, so it resolves to opaque black.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Updated.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread test/jasmine/tests/color_test.js Outdated
// Drawing code needs the alpha of a color it is about to paint, which is
// not the same question `opacity` answers. A color that is simply unset
// still gets painted, so it resolves to opaque black.
it('treats a missing color as opaque black, without warning', () => {

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

The description implies that the test checks all channels of the color, but it only checks the alpha. Either the description or the test should be updated (I don't think it checks for a warning either, although maybe our test infrastructure does surface warnings).

Same for the next test.

Suggested change
it('treats a missing color as opaque black, without warning', () => {
it('treats a missing color as opaque', () => {

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I'm going to update these to be less focused on alpha.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread test/jasmine/tests/color_test.js
});
});

describe('parse', () => {

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Honestly all of these parse tests could probably be rolled into other sections

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread test/jasmine/tests/color_test.js Outdated
BAD.forEach((v) => expect(Color.isValid(v)).toBe(false));
});

// Null channels used to reach the WebGL buffers through this path.

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
// Null channels used to reach the WebGL buffers through this path.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Updated.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread test/jasmine/tests/color_test.js
it('sets alpha', () => {
expect(Color.addOpacity('red', 0.5)).toBe('rgba(255, 0, 0, 0.5)');
expect(Color.addOpacity('rgba(255, 0, 0, 0.5)', 1)).toBe('rgb(255, 0, 0)');
expect(Color.addOpacity('red', 2)).toBe('rgb(255, 0, 0)');

@emilykl emilykl Aug 14, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

No opacity is added here: is that because 2 is an invalid value so the function does nothing, or because a ceiling is applied so that 2 becomes 1?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Yes, alpha is clamped to the [0, 1] range. Since 2 is greater than 1, 1 is passed into the formatter. culori drops alpha in this case, so rgb is returned.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread test/jasmine/tests/colorscale_test.js Outdated
Comment on lines +769 to +772
expect(fills.length).toBe(5);
expect(fills.every((f) => f === 'rgb(0, 0, 0)')).toBe(false, 'all points black');
expect(new Set(fills).size).toBeGreaterThan(1, 'every point the same color');
fills.forEach((f) => expect(f).toMatch(/^rgba?\(/, `not a usable color: ${f}`));

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Instead of all this, could you just expect(fills).toBe(...) and copy the actual array expected for the Viridis colorscale?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Sure, that seems more reasonable.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js Outdated
Comment on lines +36 to +43
* Parse a color specifier, falling back to opaque black.
*
* A missing color falls back quietly, because it means the caller left the
* attribute unset rather than gave a bad value. Callers that treat a missing
* color as nothing to paint test for it themselves, as `opacity` does.
*
* @param {*} cstr - color specifier
* @return {Color} color object
* @param {Boolean} [silent] - skip the warning, for callers that run per data point

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
* Parse a color specifier, falling back to opaque black.
*
* A missing color falls back quietly, because it means the caller left the
* attribute unset rather than gave a bad value. Callers that treat a missing
* color as nothing to paint test for it themselves, as `opacity` does.
*
* @param {*} cstr - color specifier
* @return {Color} color object
* @param {Boolean} [silent] - skip the warning, for callers that run per data point
* Parse a color specifier string and return it as a culori rgb color object.
* If the input is not a string or cannot be parsed, fall back to opaque black (#fff).
*
* @param {String} cstr - color specifier
* @param {Boolean} [silent] - if true, do not emit a warning for un-parseable colors

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Updated.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js
Comment on lines 60 to +61
* Convert any color specifier to a normalized `rgb(r, g, b)` string.
* Force alpha to 1 so that it gets dropped in the result.

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Since this function calls parse(), that means any invalid input will return rgb(0, 0, 0), right?

Might be worth putting that info in the docstring.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Fair point, but I think it makes more sense to leave that note in the docstring for parse. It seems better to keep it there since so many functions call parse.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js Outdated
* Return the alpha channel of a color (0 if falsy).
*
* @param {*} cstr - color specifier
* @return {Number}

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

What is the range for the number, is it [0, 1]?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Yes, culori clamps to that range.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js Outdated
* @param {*} cstr - color specifier
* @return {Color} color object
* @param {Boolean} [silent] - skip the warning, for callers that run per data point
* @return {Object} culori rgb color

@emilykl emilykl Aug 14, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

A culori rgb object is just an object that looks like

{ mode: 'rgb', r: _, g: _, b: _, alpha: _ }

right? Maybe add that info to the docstring since parse() is used everywhere.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Added.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js Outdated
/**
* Convert a color specifier to a 4-element `[r, g, b, a]` representation.
* Accepts strings, numeric float arrays (`[0, 1]`), or uint8 arrays (`[0, 255]`).
* Falls back to opaque black rather than null: WebGL paths index the result.

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
* Falls back to opaque black rather than null: WebGL paths index the result.
* Returns opaque black ([0, 0, 0, 0]) if color specifier is invalid.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I opted to add a note at the top of the file and remove this one because most of the functions would get the same note.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js Outdated
* @param {*} cstr - color specifier
* @param {Number} op - opacity in [0, 1]
* @return {String}
* @param {Number} op - opacity in [0, 1], clipped to that range

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

clipped to that range

Unclear — does this mean the input must be clipped to [0, 1] before passing to this function, or that if it's outside [0, 1] this function will clip it?

In any case, I see the clipping for values above 1, but what happens when the value is below 0?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I'll update the description to be clearer. If the value is below zero, it gets set to zero. I'm going to rename the function to clamp01.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js Outdated

/**
* Combine two colors into one apparent color by compositing `front` over `back`.
* If `back` is missing or transparent, the module `background` is assumed behind it.

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
* If `back` is missing or transparent, the module `background` is assumed behind it.
* If `back` is missing, the module `background` is assumed behind it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Updated.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js Outdated
* @param {*} cstr - color specifier
* @param {Number} delta - lightness shift in HSL percentage points
* @return {Color} adjusted color object
* @return {String} resulting color string

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
* @return {String} resulting color string
* @return {String} resulting color string as rgb

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Updated.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js Outdated
Comment on lines +216 to +224
const newColor = isDark(cstr)
? lightAmount
? adjustLightness(c, lightAmount)
: color(background)
? adjustLightness(cstr, lightAmount)
: background
: darkAmount
? adjustLightness(c, -darkAmount)
: color(defaultLine);
? adjustLightness(cstr, -darkAmount)
: defaultLine;

return newColor.rgb().string();
return formatRgb(parse(newColor));

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

If newColor is the result of adjustLightness(), then it's already a formatted rgb string and the extra round-trip through formatRgb(parse(...)) is unnecessary, right?

Suggested change
const newColor = isDark(cstr)
? lightAmount
? adjustLightness(c, lightAmount)
: color(background)
? adjustLightness(cstr, lightAmount)
: background
: darkAmount
? adjustLightness(c, -darkAmount)
: color(defaultLine);
? adjustLightness(cstr, -darkAmount)
: defaultLine;
return newColor.rgb().string();
return formatRgb(parse(newColor));
const newColor = isDark(cstr)
? lightAmount
? adjustLightness(cstr, lightAmount)
: formatRgb(parse(background));
: darkAmount
? adjustLightness(cstr, -darkAmount)
: formatRgb(parse(defaultLine));
return newColor;

Honestly it would probably make sense to convert the constants to RGB just once at the top of the file and reuse them.

const backgroundRGB = formatRgb(parse(background))
const defaultLineRGB = formatRgb(parse(defaultLine))

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I like your first suggestion (and I'm going to get rid of the nested ternary). I'll add the constants as well. This is the only place where backgroundRGB/defaultLineRGB would get used, but they'll get used a lot so we might as well save the function calls.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js Outdated
Comment on lines +230 to +232
* A missing color paints opaque black. Shapes and annotations leave
* `line.color` unset when the user gives none, and the outline still has to
* show. Use `opacity` instead when a missing color means "nothing to paint".

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
* A missing color paints opaque black. Shapes and annotations leave
* `line.color` unset when the user gives none, and the outline still has to
* show. Use `opacity` instead when a missing color means "nothing to paint".
* A missing or invalid color specifier applies opaque black.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

What you deleted is out of date, so I removed it. I also removed the note about black in favor of the top level comment.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js Outdated
/**
* Apply `fill` and `fill-opacity` styles to a D3 selection.
*
* A missing color paints opaque black, the same as `stroke`.

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
* A missing color paints opaque black, the same as `stroke`.
* A missing or invalid color specifier applies opaque black.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I removed the comment in favor of the module note at the top of the file.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js
Comment on lines +308 to +309
const w = 2 * p - 1;
const w2 = ((w * d === -1 ? w : (w + d) / (1 + w * d)) + 1) / 2;

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I'm sure this is fine, but do you know where these equations come from?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

It came from Qix-/color, which got it from SASS. I'll add a note.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js
};

/**
* Convert any color specifier to an `rgb(...)` or `rgba(...)` string,

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Probably all of these functions should specify that passing an invalid color specifier returns black.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

The top level comment will cover these.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js Outdated
Comment on lines +363 to +365
* Channels as `[r, g, b, a]`, with `r`/`g`/`b` in [0, 255] and `a` in [0, 1].
* An array rather than an object so callers cannot depend on the color library's
* shape. Unrounded, since callers do further arithmetic.

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Suggested change
* Channels as `[r, g, b, a]`, with `r`/`g`/`b` in [0, 255] and `a` in [0, 1].
* An array rather than an object so callers cannot depend on the color library's
* shape. Unrounded, since callers do further arithmetic.
* Returns the given color specifier as an `[r, g, b, a]` array,
* with `r`/`g`/`b` in [0, 255] and `a` in [0, 1].

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Updated.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/components/color/index.js
}
// `toRgb` omits alpha when it's 1; make sure it's added since we expect it
c.alpha ??= 1;

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Is there any reason not to clip each of the r, g, b values before returning the color object?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

It originally got clipped here, but it was causing some issues. I moved the clipping to where it was needed.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl reviewed Aug 14, 2026
View reviewed changes
Comment thread src/lib/gl_format_color.js Outdated
var colorOut = rgba(colorIn);
// A per-point color may be raw channels rather than a color string, which
// `Color.isValid` rejects but `Color.normalize` handles.
if (!isArrayOrTypedArray(colorIn) && !Color.isValid(colorIn)) return colorDfltRgba;

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Would it make sense to use Color.isChannelArray() here?

Suggested change
if (!isArrayOrTypedArray(colorIn) && !Color.isValid(colorIn)) return colorDfltRgba;
if (!Color.isChannelArray(colorIn) && !Color.isValid(colorIn)) return colorDfltRgba;

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@emilykl emilykl Aug 14, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

But actually this logic feels like a code smell. How about adding a parameter arrayAllowed to Color.isValid() so that you could write

if (!Color.isValid(colorIn, true)) return colorDfltRgba;

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@camdecoster camdecoster Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I'd like to keep isValid specific to color strings because it gets used in the coerce test. I'll use your first suggestion. validateColor is essentially a wrapper that does what you're suggesting we add to isValid. If this becomes a more common pattern, we can update isValid at that time.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emilykl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
emilykl
emilykl approved these changes Aug 14, 2026
View reviewed changes

@emilykl emilykl left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Left a bunch of comments, but nothing blocking 🌈

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
camdecoster added 3 commits August 17, 2026 14:47
@camdecoster
Update color coerce description
4b3532c
@camdecoster
Address PR feedback
97aa2c3
@camdecoster
Handle certain edge cases for culori, add tests
1b5ad5e
@camdecoster
camdecoster merged commit 635182a into v4.0 Aug 17, 2026
84 of 85 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@camdecoster
camdecoster deleted the cam/7961/switch-to-culori-library branch August 17, 2026 22:07
This was referenced Aug 30, 2026
Colorscale should be betwen 0-255 or 0-1 but not a mixed behaviour #4942
Closed
plotly.graph_objects.scatter3d.Marker color incorrect HSV/HSL interpretation plotly/plotly.py#4382
Closed
Passing color as tuple not working as expected plotly/plotly.py#4716
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

1 more reviewer
@emilykl emilykl emilykl approved these changes
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@camdecoster @emilykl
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.