Skip to content

Fluent-Next: customAccentColor method - #35230

Open
Raushen wants to merge 61 commits into
DevExpress:feature/26_2_new_fluent_theme_with_design_tokensfrom
Raushen:feature/accentColor-method
Open

Raushen wants to merge 61 commits into
DevExpress:feature/26_2_new_fluent_theme_with_design_tokensfrom
Raushen:feature/accentColor-method

Conversation

@Raushen

@Raushen Raushen commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@Raushen
Raushen requested a review from a team September 16, 2026 08:49
@Raushen Raushen self-assigned this Sep 16, 2026
@Raushen Raushen added the 26_2 label Sep 16, 2026
@github-actions github-actions Bot added the .d.ts label Sep 16, 2026
@pharret31
pharret31 force-pushed the feature/26_2_new_fluent_theme_with_design_tokens branch from 20d716f to 1ffaf35 Compare September 16, 2026 10:46
@pharret31
pharret31 requested review from a team as code owners September 16, 2026 10:46
@Raushen
Raushen force-pushed the feature/accentColor-method branch from a7b7698 to 9effde3 Compare September 16, 2026 11:12
@Raushen
Raushen removed request for a team September 16, 2026 11:15
@Raushen Raushen changed the title customAccentColor method Fluent-Next: customAccentColor method Sep 16, 2026

W0024: 'Invalid accent color: \'{0}\'. The accent color is left unchanged.',

W0025: 'The \'{0}\' theme does not support a custom accent color. Use a fluent-next theme.',

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.

Suggested change
W0025: 'The \'{0}\' theme does not support a custom accent color. Use a fluent-next theme.',
W0025: 'The \'{0}\' theme does not support custom accent colors. Apply a Fluent Next theme to use the \'customAccentColor\' method.',

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.

Applied

+ '{0}\n\n'
+ 'Interoperability between different versions of the products listed herein cannot be guaranteed.\n\n',

W0024: 'Invalid accent color: \'{0}\'. The accent color is left unchanged.',

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.

Suggested change
W0024: 'Invalid accent color: \'{0}\'. The accent color is left unchanged.',
W0024: 'Invalid accent color: \'{0}\'. The previous accent color remains in effect.',

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.

Applied

const themeName = current();
const isCustomAccentSupported = isFluentNext(themeName);

if (!isCustomAccentSupported) {

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.

current() is null until the theme marker is readable from CSS and nobody has called current(name)
(no <link rel="DevExpress-theme"> -> knownThemes is empty -> resolveFullThemeName gives null).
That is the state while a theme <link> is still loading, is injected later, or ships in a deferred
styles bundle. In that window isFluentNext(null) is false, W0025 logs with an empty theme name and
the accent is dropped on the floor.

The variable is inert on every other theme, so refusing to write it buys nothing - it only makes the call
order-dependent. Suggestion: always set the property; warn only when a theme IS known and is not
Fluent Next. As a side effect an accent set while generic is loaded survives a later switch to
fluent-next, which is what a caller would expect.

(+1 to Arman's wording for W0025; with this change '{0}' can no longer come out empty.)

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.

Applied

return window.getComputedStyle(root).getPropertyValue(ACCENT_COLOR_PROPERTY).trim();
}

if (color === null) {

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 getter answers '' for "nothing set", the setter refuses '' with W0024, so
themes.customAccentColor(themes.customAccentColor()) on a fresh page logs a warning. The CSSOM itself
treats setProperty(name, '') as removal (measured: the ramp goes back to the designed one).

Either treat '' like null (my pick - the d.ts stays as is and whatever the getter hands out is
accepted back), or make the getter return null and type it string | null. The test at
__tests__/themes.test.ts:186 encodes the current asymmetry and flips with either choice.

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.

Applied


export type ColorInstance = Color;

export function isValidColor(value: string): boolean {

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.

#a703ff80 and rgb(167 3 255 / 50%) pass, and every step of the ramp is oklch(from src l c h) with
no alpha channel of its own, so the whole primary scale inherits the alpha (measured / 0.501961 on
step 100). This function now defines what an accent may be, so the decision belongs here: refuse it with
W0024, strip the alpha, or pin / 1 in _accent-color.scss as a follow-up. Passing it through silently
is the one option I would not take.

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.

Applied — pinned / 1 on all 18 steps, in this PR rather than as a follow-up: it is the only option that also covers var() and color-mix(). Held by accent-palette.test.ts and by a testcafe case that sets #a703ff80 and requires no step to carry an alpha.

@@ -1,5 +1,7 @@
/* eslint-disable spellcheck/spell-checker */

import domAdapter from '@js/core/dom_adapter';

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.

color.ts was a DOM-free parser; this adds domAdapter, and themes.ts - present in every build - now
pulls the whole module in, name table included (~7.6 KB of source). The package declares no
sideEffects: false, so bundlers keep it. Nothing else needs isValidColor: a local function in
themes.ts or a small module under core/utils would do.

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.

Applied — local function in themes.ts, color.ts is DOM-free again. __tests__/color.test.ts is gone with it; its value cases moved into themes.test.ts and now run through customAccentColor.

return color === undefined ? '' : undefined;
}

const root = domAdapter.getDocument().documentElement;

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.

readThemeMarker and init work with the module-level context (settable through init({ context }));
this reads domAdapter.getDocument(). Under a custom context the marker is read from one document and the
accent written to another. context.documentElement keeps them together.

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.

Applied


const THEME_MARKER_PREFIX = 'dx.';

const ACCENT_COLOR_PROPERTY = '--dx-accent-color';

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.

tools/naming/accent-contract.json says setBy: "the application, on the root element". From this PR
the runtime writes it too, through style.setProperty - exactly what runtime-contract.json records.
Update one of them (I would extend setBy in accent-contract.json with themes.customAccentColor) so
both entry points are on record, and say which one the docs present as primary. The same file should
also state whether var(--x) is a supported input - see C7.

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.

Applied

'hsl(280 100% 50%)',
'oklch(0.6 0.15 250)',
'color-mix(in oklab, red, blue)',
'transparent',

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.

isValidColor('var(--brand)') is true (any var() parses at set time) and the ramp resolves it
(measured). Handy, but today it is undocumented behaviour. Either add var(--brand) to this list so it
becomes a promise, or reject it explicitly.

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.

Applied — promised. The list is gone along with color.test.ts (see the bundle-size comment), so the promise now sits in themes.test.ts, a testcafe case, and acceptedForms in accent-contract.json.

expect(log).toHaveBeenCalledWith('W0024', 'inherit');
});

it('warns and sets nothing when the loaded theme knows no accent color', () => {

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.

One case is missing next to these two: no theme marker at all (see C1). Whatever the decision on the
gate, that state needs a test - it is the one a real page is in while its stylesheet loads.

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.

Applied

EugeniyKiyashko and others added 14 commits September 18, 2026 09:23
…ss#34744)

Co-authored-by: Andrei Kharitonov <pharret31@users.noreply.github.com>
…, fix hardcoded colors in Widget Gallery (DevExpress#34698)

Co-authored-by: Andrei Kharitonov <pharret31@users.noreply.github.com>
Co-authored-by: EugeniyKiyashko <EugeniyKiyashko@users.noreply.github.com>
Rebase onto main brought the adduse/dead-import rules; fluent-next was
written before them. Removes 257 adduse markers and 141 dead theme-root
imports (27 files become empty placeholders), plus the formatting fixes
the new @Stylistic rules require.
They were unrelated to the theme: 13 workflows had gained a
`push: branches: [26_1]` trigger (default_workflow a `[0-9][0-9]_[0-9]`
glob, wrapper_tests_e2e a `26_*` entry). The fluent-next additions to the
testcafe and demo visual-test matrices stay.
EugeniyKiyashko and others added 17 commits September 18, 2026 09:23
The gate was red on the base branch and no workflow ran it. registries.json
carried a drag-source sub-element the generator never produced, and the
reachability run had 13 unreviewed scopes. The generator emits the sub-element
now; twelve of the scopes are recorded as nested, each proven at runtime; the
thirteenth was two actionSheet selectors that never matched an element, since
the items live in the portalled popup wrapper and the state class lands on
.dx-actionsheet-container.
@pharret31
pharret31 force-pushed the feature/26_2_new_fluent_theme_with_design_tokens branch from 8b39347 to 577ba30 Compare September 18, 2026 07:25
@Raushen
Raushen force-pushed the feature/accentColor-method branch from 9effde3 to d68413c Compare September 21, 2026 08:36
@pharret31
pharret31 force-pushed the feature/26_2_new_fluent_theme_with_design_tokens branch 2 times, most recently from 17cde86 to 370c8f0 Compare September 21, 2026 13:58
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.

5 participants