Skip to content

fix(webapp): restore hostname suggestion contrast - #1929

Merged
Benoît Cortier (CBenoit) merged 1 commit into
masterfrom
kristahouse-dgw-337-autocomplete-theming
Aug 12, 2026
Merged

fix(webapp): restore hostname suggestion contrast#1929
Benoît Cortier (CBenoit) merged 1 commit into
masterfrom
kristahouse-dgw-337-autocomplete-theming

Conversation

@kristahouse

Copy link
Copy Markdown
Contributor

The hostname suggestion list did not set its own text colour, so options were painted with whatever colour the PrimeNG theme supplied. When that colour was close to the panel background the suggestions were invisible until the pointer moved over them.

The styles meant to prevent this had been written against PrimeNG 18 class names and stopped matching anything when the app moved to PrimeNG 20. They are now ported to the current class names and the suggestion list is themed like every other dropdown, so options stay readable in both light and dark themes.

Issue: DGW-337

Copilot AI balanced review requested due to automatic review settings August 11, 2026 15:40
@github-actions

Copy link
Copy Markdown

Let maintainers know that an action is required on their side

  • Add the label release-required Please cut a new release (Devolutions Gateway, Devolutions Agent, Jetsocat, PowerShell module) when you request a maintainer to cut a new release (Devolutions Gateway, Devolutions Agent, Jetsocat, PowerShell module)

  • Add the label release-blocker Follow-up is required before cutting a new release if a follow-up is required before cutting a new release

  • Add the label publish-required Please publish libraries (`Devolutions.Gateway.Utils`, OpenAPI clients, etc) when you request a maintainer to publish libraries (Devolutions.Gateway.Utils, OpenAPI clients, etc.)

  • Add the label publish-blocker Follow-up is required before publishing libraries if a follow-up is required before publishing libraries

@kristahouse

Copy link
Copy Markdown
Contributor Author

Implementation notes

Root cause

.p-autocomplete-option had no app-level color declaration. PrimeNG registers its own styles in the primeng cascade layer:

.p-autocomplete-option { color: dt('autocomplete.option.color'); }

Because nothing in the app declared a colour for that element, the rendered text colour was entirely owned by the PrimeNG token. Whenever that token resolved close to the panel background, the options were invisible until hover swapped the row background.

.p-select-option never had this problem because _p-select.scss declares its colours explicitly. That is why only the hostname combo was reported.

Why the existing styles did not help

_p-autocomplete.scss did contain option styling, but it targeted PrimeNG 18 class names. PrimeNG 20 renamed them, so those rules stopped matching any element:

Old (dead) Current
.p-autocomplete-panel .p-autocomplete-overlay
.p-autocomplete-items .p-autocomplete-list
.p-autocomplete-item .p-autocomplete-option
.p-autocomplete-dd removed
.p-autocomplete-multiple-container .p-autocomplete-input-multiple
.p-autocomplete-token .p-autocomplete-chip

The only occurrences of p-autocomplete-panel and p-autocomplete-item left in primeng@20.3.0 are inside PrimeNG's own findSingle() scroll-into-view calls — stale selectors in the library itself, never applied to the DOM.

Roughly 190 lines of the file matched nothing. The chip and multi-select rules were doubly dead: the app's single p-autoComplete is single-select.

Changes

  1. _p-autocomplete.scss — ported the surviving rules onto current class names and mirrored the structure of _p-select.scss, so both dropdowns are styled the same way. Dead rules removed.
  2. dvl.preset.ts — added an autocomplete section. DvlPreset already covered select, multiselect, listbox and 29 other components but not autocomplete, so its tokens fell back to raw Aura values instead of the DVLS variables. This is defence in depth; the stylesheet is what actually guarantees the contrast.
  3. web-client-form.component.html — dropped field="hostname", redundant with optionLabel="hostname", and an overlayOptions.styleClass pointing at dv-autocomplete-panel-open, a class that is not defined anywhere in the repository.

Testing

definePreset performs excess-property checking, so every token name is compiler-verified. Confirmed by temporarily inserting a bogus token and observing the build fail with TS2353: ... does not exist in type 'Root', then reverting.

Beyond that, I rendered the exact PrimeNG 20 overlay DOM against the real compiled stylesheet in headless Edge and measured computed colours. The harness injects a deliberately hostile @layer primeng { .p-autocomplete-option { color: rgb(250,250,250) } } to reproduce the failure mode, and includes p-select-option as a control.

Before, in the light theme:

autocomplete normal    text=rgb(250, 250, 250)  bg=rgb(249, 249, 249)  contrast=1.01:1
autocomplete focus     text=rgb(250, 250, 250)  bg=rgb(249, 249, 249)  contrast=1.01:1
select normal          text=rgb(33, 33, 33)     bg=rgb(249, 249, 249)  contrast=15.29:1

1.01:1 is invisible text, and the control shows why select was unaffected.

After:

autocomplete normal    text=rgb(33, 33, 33)     bg=rgb(249, 249, 249)  contrast=15.29:1
autocomplete focus     text=rgb(33, 33, 33)     bg=rgb(245, 245, 245)  contrast=14.77:1
autocomplete selected  text=rgb(0, 104, 195)    bg=rgb(229, 240, 249)  contrast=4.82:1

Autocomplete now matches select exactly in all three states across both themes, and the layered value no longer leaks through. Since the app's overrides are unlayered they win over any cascade layer, so this failure mode cannot return regardless of what the theme resolves to.

One pre-existing observation, not addressed here: the selected-row colours give 4.47:1 in the dark theme, marginally under WCAG AA. The measurement is identical for select, so it comes from the shared $dropdown-item-selected-* variables and affects every dropdown in the app rather than anything specific to this change.

pnpm --filter gateway-ui build, lint, and check all pass with no new warnings.

Regression window

Introduced by the PrimeNG 20 migration (#1652), first released in v2025.3.4, which matches the version on the ticket. #1845 later resynchronised darkModeSelector with the app theme and set the overlay background, which resolved the reported symptom from v2026.2.4 onward. This change fixes the underlying cause, so the colours are owned by the app rather than left to whatever the theme supplies.

Note

Written by Copilot (claude-opus-5), reviewed by Krista House (@kristahouse).

Copilot AI 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.

Pull request overview

Restores readable hostname autocomplete suggestions under PrimeNG 20 in light and dark themes.

Changes:

  • Adds autocomplete design tokens to the application preset.
  • Updates autocomplete CSS selectors and option states for PrimeNG 20.
  • Removes obsolete template properties and panel styling hooks.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
webapp/apps/gateway-ui/src/client/app/styles/dvl.preset.ts Defines autocomplete theme tokens.
webapp/apps/gateway-ui/src/client/app/modules/web-client/form/web-client-form.component.html Uses the current autocomplete configuration.
webapp/apps/gateway-ui/src/assets/css/override/_p-autocomplete.scss Ports autocomplete overrides to current PrimeNG classes.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@kristahouse
Krista House (kristahouse) marked this pull request as draft August 11, 2026 15:47
The hostname suggestion list did not set its own text colour, so options
were painted with whatever colour the PrimeNG theme supplied. When that
colour was close to the panel background the suggestions were invisible
until the pointer moved over them.

The styles meant to prevent this had been written against PrimeNG 18
class names and stopped matching anything when the app moved to PrimeNG
20. They are now ported to the current class names and the suggestion
list is themed like every other dropdown, so options stay readable in
both light and dark themes.

Issue: DGW-337

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@kristahouse
Krista House (kristahouse) force-pushed the kristahouse-dgw-337-autocomplete-theming branch from 5f2ecef to a12c59f Compare August 11, 2026 16:35
@kristahouse

Krista House (kristahouse) commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up after review: fixed a padding regression introduced by this PR's own cleanup, and verified both states visually and by measurement.

Padding regression (fixed)

The dropdown arrow button is absolutely positioned and floats over the input rather than sitting beside it, so the input carries padding-right: 2em to keep typed text out from under it. It also re-rounds its right-side corners, because PrimeNG zeroes them assuming the standard side-by-side layout.

In PrimeNG 18 both rules were gated on .p-autocomplete-dd, a class PrimeNG applied only when [dropdown] was true. PrimeNG 20 removed that class. The first pass at this PR ported the rules but dropped the condition along with the dead selector, so they applied unconditionally.

That matters because the template gates the button on [dropdown]="isHostnamesExists()". With no saved hostnames the arrow is correctly absent, but the input still reserved 28px for it — dead space on first use, which is also the least-exercised path.

The fix re-scopes both rules under :has(.p-autocomplete-dropdown). That is PrimeNG 20's own replacement idiom; its base stylesheet uses the identical selector:

.p-autocomplete:has(.p-autocomplete-dropdown) .p-autocomplete-input { flex: 1 1 auto; width: 1%; }

It is also sturdier than what it replaced. A class name can be renamed again in a future major, but the structural relationship cannot silently disappear — if the button is not rendered, the selector stops matching, which is the correct behaviour by construction.

Measured on real compiled builds, before and after:

saved hostnames arrow padding-right before after
none absent 28px 0px
some present 28px 28px

Why the app stylesheet, not just the preset

providePrimeNG registers a cssLayer, and the app's own SCSS is unlayered. Unlayered styles beat any cascade layer, so the explicit .p-autocomplete-option colour rule is what actually guarantees the fix. The new autocomplete section in DvlPreset is defence in depth — it keeps the tokens correct for anything the app does not override, and its token names are compiler-checked (definePreset performs excess-property checking, confirmed by a deliberate bogus-token control test).

Note on the failure mode

Both defects in this PR were the same shape: a dead PrimeNG selector that was not merely inert leftover, but silently carrying behaviour. Worth keeping in mind at the next PrimeNG major — the risk of a version bump is not only the styles that stop applying, but the conditions that quietly stop being conditions.

Note

Human-tuned, LLM-assisted content.

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@CBenoit Benoît Cortier (CBenoit) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@CBenoit
Benoît Cortier (CBenoit) merged commit 1dd9b44 into master Aug 12, 2026
43 checks passed
@CBenoit
Benoît Cortier (CBenoit) deleted the kristahouse-dgw-337-autocomplete-theming branch August 12, 2026 03:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants