Skip to content

Fluent-next: viz tokens - #35275

Open
Raushen wants to merge 11 commits into
DevExpress:feature/26_2_new_fluent_theme_with_design_tokensfrom
Raushen:feature/viz-tokens
Open

Raushen wants to merge 11 commits into
DevExpress:feature/26_2_new_fluent_theme_with_design_tokensfrom
Raushen:feature/viz-tokens

Conversation

@Raushen

@Raushen Raushen commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@Raushen Raushen self-assigned this Sep 20, 2026
@Raushen
Raushen requested a review from a team as a code owner September 20, 2026 21:41
@Raushen Raushen added the 26_2 label Sep 20, 2026
@Raushen
Raushen force-pushed the feature/viz-tokens branch 6 times, most recently from eda9e37 to 6b135e3 Compare September 21, 2026 05:36
@pharret31
pharret31 force-pushed the feature/26_2_new_fluent_theme_with_design_tokens branch from 6f53ff6 to 17cde86 Compare September 21, 2026 13:56
@pharret31
pharret31 requested review from a team as code owners September 21, 2026 13:56
@pharret31
pharret31 force-pushed the feature/26_2_new_fluent_theme_with_design_tokens branch from 17cde86 to 370c8f0 Compare September 21, 2026 13:58
@Raushen
Raushen removed request for a team September 22, 2026 12:05

@EugeniyKiyashko EugeniyKiyashko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The three boxes in card 5086 are ticked, but your own note on the card says otherwise: only family is on a name for fonts, only the rangeSelector follows the accent, and there is no etalon next to ACCENT_GRID. You have already escalated the conflict between the card's acceptance list and the decision that no viz colour follows the accent - who resolves it, and should the boxes be unticked until then?

Comment thread packages/devextreme/js/__internal/viz/core/renderers/renderer.ts Outdated
Comment thread packages/devextreme/js/__internal/core/utils/css_variables.ts
Comment thread e2e/testcafe-devextreme/tests/common/themeModes.ts
Comment thread packages/devextreme/js/__internal/core/utils/m_svg.ts
Comment thread packages/devextreme/js/__internal/viz/core/utils.ts
return container.get(0);
}

const scope = swatchContainer.getSwatchContainer(this._widgetRoot);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also for every theme: the viz tooltip container moves from body to the swatch container. view_port is set to .dx-viewport on ready and falls back to body only when there is no such element - so on a bare page nothing changes, but on the recommended markup the tooltip moves out of body into the viewport, with its own stacking context and overflow. Was this checked on the other themes?

@Raushen Raushen Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, it moves, and for every theme. But this is the settled practice for overlays in DevExtreme rather than something new: overlay_position_controller.ts:218 resolves the markup container through getSwatchContainer for every Overlay descendant — Popup, Popover, the Tooltip widget, ContextMenu, LoadPanel, Toast, the drop-down editors. On the recommended markup all of those have been living in the viewport, not in body, for a long time. The viz tooltip was the one exception.

And that practice is exactly what lets the swatch mechanism reach a viz tooltip at all, which is the reason for the change. Measured on a built library, a chart inside dx-theme-mode-dark under .dx-viewport, tooltip raised by a real hover:

placement lands in cloud fill
default div.dx-theme-mode-dark inside .dx-viewport rgb(255, 255, 255)
container: 'body' body rgb(36, 36, 36)

The theme declares --dx-viz-tooltip-bg as #242424 in light and #ffffff in dark, so the chart's scope calls for #ffffff; only the container inside the scope produces it. In body the name resolves against :root and the tooltip comes out in the build mode's colour, not its own scope's. The cloud landed at the same coordinates either way, so the reparenting did not shift placement here.

One thing that is not covered: tooltip.tests.js asserts body in six places and passes only because the QUnit page has no .dx-viewport. I'll add a test that a chart's tooltip inside a dx-theme-mode-* scope paints in that scope.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The test you promised here is not in the branch. tooltip.tests.js was only touched for fill-opacity (18 insertions / 17 deletions, all rgba(147,147,147,0.7)#939393), so what this change is for - the name resolving in the chart's own scope - is asserted nowhere.

The six assert.deepEqual(this.tooltip._wrapper.appendTo.firstCall.args, [$('body').get(0)]) (663, 703, 854, 885, 991, 1023) and assert.equal(tooltip._getContainer(), $('body').get(0)) (181) pass only because the QUnit page has no .dx-viewport and view_port falls back to body, so green says nothing about the new behaviour.

Expected: the test from your reply - a chart inside a dx-theme-mode-* scope, tooltip raised, cloud painted in that scope's --dx-viz-tooltip-bg - plus one that pins the container it lands in, so the fallback to body without a viewport stays asserted on purpose rather than by accident.

Comment thread packages/devextreme/js/__internal/viz/series/points/base_point.ts
Comment thread packages/devextreme/js/__internal/viz/palette.ts
Comment thread packages/devextreme/js/__internal/core/utils/css_variables.ts
Comment thread packages/devextreme-scss/scss/widgets/fluent-next/common/_public-links.scss Outdated
offset: attributes.offset.value,
stopColor: attributes['stop-color'].value,
stopColor: attributes['stop-color']
? attributes['stop-color'].value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

exportTo and print throw on a gradient now. This branch was written for the previous approach, where the reference went into an inline style and the attribute was absent. After 506c95451d the attribute is always there and holds the raw var(…).

The chain: sankey.ts:313 takes the node colour from the palette, which for fluent-next is var(--dx-viz-blue, #0078d4); sankey.ts:333 puts it into stop-color when link.colorMode: 'gradient'; renderer.ts:2054 writes it as an attribute; export.ts:753 (exportTo) and :788 (print) hand the exporter this._renderer.root.element, a live DOM element rather than a string, so copyResolvedStyles never runs on that path; createGradient takes the attribute as is; image_creator.ts:481 passes it to gradient.addColorStop.

addColorStop throws on an invalid colour rather than degrading - measured: SyntaxError: Failed to execute 'addColorStop' on 'CanvasGradient'.

Repro: dxSankey with link: { colorMode: 'gradient' } on fluent-next, exportTo('PNG') or print().

Why CI is green: QUnit runs on generic, where the stops are literals, and the e2e references === 0 assertion goes through getMarkup, i.e. the string path, where copyResolvedStyles already resolves the attribute. Nothing covers exportTo / print.

Expected:

  1. Keep the fallback - it also fixes a stop with no colour at all (renderer.ts:2054 skips undefined, and the old code threw on attributes['stop-color'].value). Widen the condition instead: take the computed value when the attribute is missing or its value is a reference (isCssVariableReference).
  2. A gate in imageCreator.tests.js, where gradients are already covered: a stop whose stop-color is var(--dx-viz-blue, #0078d4) has to export as the painted colour.

For the record, I checked the rest of the live-DOM path: other paints go through parseStyles + rootAppended (image_creator.ts:235), the SVG export goes through m_svg.getSvgMarkup and resolves there, and flood-color in filters plus the pattern colours are literals in the theme. Gradients are the only hole.

@EugeniyKiyashko

Copy link
Copy Markdown
Contributor

Checked the card's requirements against the state of the product rather than against the branch diff, on cd4880203f. Three of them are not fully met.

1. Two colour values are painted with default settings and are not on the token layer.

A search through the fluent-next theme file finds no tile, no lineColor and no firstLastColor. Both values are inherited from the base themes:

Theme path Value Source
sparkline.lineColor, sparkline.firstLastColor #666666 generic/light/sparkline.ts:5,14
treeMap.tile.border.color #000000, width 1, opacity 0.2 generic/light/tree_map.ts:5-10

Both are painted without any extra option: the first is the sparkline's own line colour, the second is the border drawn around every tile. They are not part of the 126 literals awaiting a designer - you separated them in the card under "Painted today, and wrong in a dark scope" and wrote that both need a published name. #666666 on a #1d1d1d surface is an unmet part of "colors should be tokenized".

Expected: either both move onto published names in this PR, or the card records the move explicitly, naming the task that receives them.

2. The accent behaviour the branch does implement is not covered by any automated check.

accentColor.ts creates a single widget, dxDataGrid (line 227), and takes the Accent palette ${palette}.png etalon (line 216). No visualization component is present in that scenario. Wider check: e2e/testcafe-devextreme/tests/common/ contains no mention of RangeSelector, and neither themeModes.ts nor vizPalettes.ts mentions --dx-accent-color or customAccentColor. That RangeSelector follows the custom accent rests on the manual measurement in the card alone.

The card asks for this directly: "a Chart / RangeSelector etalon next to ACCENT_GRID". Your answer explains that ACCENT_GRID is the grid's id and that it falls inside the etalon with its parent - it does not address adding a visualization component there.

Expected: a RangeSelector next to the grid in accentColor.ts, so the accent etalon catches a regression in the one component that follows the accent.

3. On fonts, the answer names the weight only.

The theme file carries no size: or weight: declaration at all; only the family is on a published name. By your own measurement in the card, the theme has 141 font blocks with sizes 11, 12, 14, 16, 18, 20 and weights 200, 400, 500, 600. The next PR is announced for the weight.

Expected: confirm whether the sizes are in that same PR. If not, "fonts should be tokenized" stays unmet after it lands.

Two requirements verified as met, for the record: the accent resolution chain is real - --dx-viz-primaryds.$color-bg-primaryvar(--dxds-color-bg-primary) (_design-system/fluent/mode-shared.scss:29) → var(--dxds-primary-100)var(--dx-accent-color-100, …), so RangeSelector follows the accent set either way; and there is no disclaimer in this repository - the customAccentColor JSDoc at js/ui/themes.d.ts:54-69 is clean, so that note lives on the documentation site only.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants