fix: bake node color into canvas texture to preserve emoji colors#4
Merged
Conversation
CanvasTextRenderer hardcoded fillStyle='white', causing the GPU shader to multiply the texture by node.color as a tint. This crushed native emoji colors to near-black when text color was dark (e.g. light theme). Bake node.color directly into the canvas fillStyle so text glyphs render in the correct color and emoji glyphs retain their native colors. Force the shader tint to white (with worldAlpha) so the texture passes through untinted. Re-render canvas text when color changes. Closes #1 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
node.colorintoCanvasTextRenderer.fillStyleinstead of hardcoding white — text glyphs render in the correct color directly on the canvas, and emoji glyphs retain their native browser colors since they bypassfillStyleworldAlpha) for canvas text nodes so the GPU doesn't double-tint the already-colored texturecolorsetter inCoreTextNodeto invalidate layout when color changes on a canvas text nodeWhy
CanvasTextRendererhardcodedcontext.fillStyle = 'white', producing a texture with white text + natively colored emoji. The shader then multiplied the entire texture bynode.color. For dark text colors (light theme), this crushed emoji to near-black silhouettes.Closes #1
Trade-off
Gradient-tinted canvas text (non-uniform
colorTl/Tr/Bl/Br) will no longer produce a gradient since the shader corners are forced to white. This is acceptable because canvas text color is now baked into the texture, and gradient text is an edge case that's better served by the SDF renderer.Test plan
<Text color={0x0f172aff}>🔥 hello</Text>with CanvasTextRenderer — emoji should retain native colors, text should be dark<Text color={0xf1f5f9ff}>🔥 hello</Text>— both text and emoji should appear correctly (regression check)colordynamically — text should re-render with the new coloralpha/worldAlphafading still works on canvas text nodes🤖 Generated with Claude Code