fix(chart): Enhance Canvas based text measurement to account for various font features#2798
Draft
awahab07 wants to merge 15 commits intoelastic:mainfrom
Draft
fix(chart): Enhance Canvas based text measurement to account for various font features#2798awahab07 wants to merge 15 commits intoelastic:mainfrom
awahab07 wants to merge 15 commits intoelastic:mainfrom
Conversation
…unt open font attributes like `font-feature-settings` and `font-variant-numeric`.
…anvas to make sure canvas based text measurement takes into account font features like `font-feature-settings` and `font-variant-numeric`.
Collaborator
Author
|
This following limitation has been circumvented when Lens introduced a specialized font
|
awahab07
commented
Mar 2, 2026
| // measureCtx doesn't need withContext, so not using it for performance reasons | ||
| if (isMeasureCtx) { | ||
| // Avoid setting the font multiple times if it hasn't changed for performance | ||
| if (fontString !== lastFont) { |
Collaborator
Author
…nts-for-Lens # Conflicts: # storybook/stories/utils/elastic_ui_numeric_font.scss
Collaborator
Author
|
buildkite update vrt |
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.


Problem
Text measurement should account for inherited CSS advanced font properties, not easily guessable/enumerable (e.g.
font-feature-settings,font-variant-numericand other advanced font props/settings). Text layout calculations using a detached off-screen canvas could not inherit take into account all properties reliably, causing unprecedented text clipping or overlap in charts.For example, when a consumer application Lens uses a specialized font
"Elastic UI Numeric", Metric chart withvalueFontSize: 'fit'gets the primary metric label clipped.Solution
An in-Dom hidden canvas should naturally inherit all font features/settings making text measurement accurate. This PR implement that and uses a zero sized hidden canvas element in every chart whose ctx passed on when measuring text for the chart.
Details
The Canvas 2D
ctx.fontproperty only accepts CSSfontshorthand syntax, which does not includefont-feature-settingsorfont-variant-numeric. TheCanvasTextDrawingStylesinterface also has nofontFeatureSettingsattribute. The only way formeasureText()to account for these properties is through CSS inheritance — which requires the canvas element to be present in the DOM tree. A detached off-screen canvas has no parent, so inherited font properties default tonormal, producing measurements that don't match the rendered output.Additionally, not all chart types use a
<canvas>for rendering (e.g. Metric and Bullet graph are fully DOM-rendered), yet they still rely on canvas-basedmeasureText()for layout calculations — so there is no rendering canvas to reuse.This change:
ChartMeasureCanvascomponent — a hidden, zero-size,aria-hidden<canvas>placed inside each Chart's.echChartDOM subtree so it inherits computed font properties from ancestors.withTextMeasureto prefer the registered in-DOM canvas, falling back to an off-screen canvas when no Chart component is mounted.Performance
The in-DOM canvas is reused across all
withTextMeasurecalls for the lifetime of the Chart component, whereas the previous approach created a new off-screen canvas (document.createElement('canvas')) on every invocation. This reuse eliminates repeated canvas allocation and context creation, resulting in a net performance improvement. A DOM<span>+getBoundingClientRect()alternative was also evaluated but was significantly slower per call and triggers layout reflow, making canvas-based measurement the better choice. AlsoChartMeasureCanvascomponent is kept to not re-render or re-mount against parent updates.The following snippets demonstrate text measurement and performance different, with and without font features applied.
Issues
Related to elastic/kibana#251576
Checklist
:xy,:partition):interactions,:axis)closes #123,fixes #123)[ ] New public API exports have been added topackages/charts/src/index.tslightanddarkthemes