Skip to content

feat(toast): redesign toast notifications with dark mode design system - #1484

Merged
sven1103 merged 6 commits into
developmentfrom
chore/notification-style-update
Jul 30, 2026
Merged

feat(toast): redesign toast notifications with dark mode design system#1484
sven1103 merged 6 commits into
developmentfrom
chore/notification-style-update

Conversation

@sven1103

@sven1103 sven1103 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Redesign the toast notification UI to align with the data-manager style guide and improve visual design, accessibility, and consistency.

Bug fixes

  • Missing status icons: extract levelIcon as a separate field so subsequent withContent() calls cannot accidentally destroy the previously set icon
  • Routing/info links invisible: add CSS overrides for RouterLink/Button elements rendered inside the toast
  • Info icon missing glyph: lumo:info-circle does not exist — switch to vaadin:info (symbol-only)
  • Icons rendered as colored blobs: vaadin:*-circle icons have their own filled-circle path — switch to symbol-only variants vaadin:check, vaadin:close, vaadin:info
  • Pending task title rendered as raw HTML markup: withProgressBar() used Div(title) — replace with wrapped Html element
  • Trailing whitespace on HTML toast messages: replace <div style='display:contents'> with inline <span> wrapper

Visual design

  • Add 14 new CSS variables in variables.css: design tokens (--color-primary, --color-error, --color-success), dark-surface shade tokens, and toast-specific tokens (--toast-background-color, --toast-link-color, etc.)
  • Link color increased from #1676f3 to #66A8FF to meet WCAG AA contrast
  • Font sizes use Lumo variables (--lumo-font-size-*) instead of hardcoded px
  • Icon size reduced from 20×20 to 14×14 inside the 28×28 circle

Layout

  • Single-line approach with ellipsis-based text truncation via child-combinator selectors (only the text-bearing node has overflow: hidden; action buttons and close button are never clipped)
  • Routing container uses flexbox with flex: 1 1 0 so text shrinks and buttons stay at natural width
  • Toast overlay max-width removed so width adapts to content

Toast message catalog (toast-notifications.properties)

  • Added <key>.message.subtext documentation comment and sample entries for progress toasts
  • Added measurement.registration.in-progress.message.subtext ('This might take a few minutes')
  • Cleaned up project.updated.error.retry.message.text: removed inline <a> link in favor of the routed-link toast flow (the routing toast now renders the link as a styled button)

ComponentDemo (test route)

  • Complete overhaul of the ComponentDemo demo page with a two-column layout showing all toast types side by side (basic success/info/error, pending-task progress, routing, action-button, error with routing) — makes it easier to visually verify every toast variant in one place
  • New message keys added: experiment.created.success, task.failed.action, measurement.completed.success, measurement.completed.routing, measurement.failed.retry

Refactoring

  • pendingTaskToast: build progress layout directly instead of delegating to text-toast path
  • Add actionToast factory method for error toasts with a Try Again button

Files changed

File Description
toast.css Full rewrite — design tokens, flex layout, truncation, icon styling
variables.css 14 new design-token / toast-specific CSS variables
Toast.java Icon extraction, vaadin:\* icons, Html wrappers for progress title/subtext
MessageSourceNotificationFactory.java <span> wrapper for HTML content, new actionToast factory
toast-notifications.properties Subtext docs, new subtext entries, cleaned up inline links in retry message
ComponentDemo.java Two-column demo page exercising all toast variants

@sven1103
sven1103 requested a review from a team as a code owner July 22, 2026 08:22
@github-actions github-actions Bot added the chore label Jul 22, 2026
Redesign the toast notification UI to align with the data-manager style
guide and improve visual design, accessibility, and consistency.

Functional changes:
- Extract levelIcon as a separate field so subsequent withContent calls
  cannot accidentally destroy the status icon (fix for missing icons).
- Use official Vaadin icons (vaadin:check, vaadin:close, vaadin:info)
  rendered at 14×14px in white inside 28×28 colored circles, instead of
  the smaller Lumo iconset (no Lumo info icon exists).
- Rewrite toast CSS to use design tokens from the style guide instead of
  hardcoded hex colors; add 14 new CSS variables
  (--color-primary, --color-error, --color-success, --color-shade-*,
  --toast-background-color, --toast-text-color, etc.) in variables.css.
- Increase link color from #1676f3 to #66A8FF to meet WCAG AA contrast
  requirements against the dark navy background.
- Fix routing/info toast link visibility by adding CSS overrides for
  RouterLink/Button elements rendered inside the toast.
- Use flexbox-based routing container instead of CSS Grid, simplifying
  the DOM while preserving ellipsis truncation for long messages via
  child combinator selectors.
- Remove max-width for the toast overlay to let content determine width.
- Fix pending task titles/subtexts rendering literal HTML markup by using
  Html wrappers instead of Div for title/subtext in withProgressBar().
- Wrap HTML content in <span> instead of <div style='display:contents'>
  to eliminate trailing block-level whitespace in toast messages.
- Reimplement pendingTaskToast to build progress layout directly instead
  of delegating to the text-toast path.
- Add actionToast factory method for error toasts with a 'Try Again'
  button.
@sven1103
sven1103 force-pushed the chore/notification-style-update branch from 741069b to f983a8f Compare July 22, 2026 08:26
@sven1103 sven1103 self-assigned this Jul 23, 2026

@KochTobi KochTobi 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.

Hi Sven the new toasts look fancy. Please enable multiline messages again, use css classes and prevent duplicating css variables.


private Component content;

private Optional<Component> levelIcon = Optional.empty();

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.

Please avoid Optional as fields. Rather take a nullable Component in this case. With Optional as field or method parameter you always increase complexity as the Optional itself may be null. I know that you are assigning an empty Optional here, but the field itself can be set to null by accident.
Instead of using it in the field, you can provide a private method, that wraps the field in an Optional.ofNullable so you can access it as Optional in your code. This way the returned Optional can never be null.

Optional also can cause problems with serialization (as it is not Serializable). For this reason a nullable field is preferred as well.

private Component content;

private Optional<Component> levelIcon = Optional.empty();
private Optional<Component> actionButton = Optional.empty();

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.

Please do not use Optional as field.


private Optional<Component> levelIcon = Optional.empty();
private Optional<Component> actionButton = Optional.empty();
private Optional<String> title = Optional.empty();

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.

Please do not use Optional as field.

private Optional<Component> levelIcon = Optional.empty();
private Optional<Component> actionButton = Optional.empty();
private Optional<String> title = Optional.empty();
private Optional<String> subtext = Optional.empty();

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.

Please do not use Optional as field.

Comment thread datamanager-app/frontend/themes/datamanager/components/toast.css Outdated
Comment on lines +47 to +49
color: white !important;
width: 14px !important;
height: 14px !important;

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.

There should be no need for !important. Please use !important sparingly, as it cannot be overwritten by CSS selector specificity.
https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascade/Specificity

This also overwrites the size you set with .toast-icon which might be an undesired side-effect.

Comment on lines +121 to +123
* <li>Title (from message.text) in 18px bold</li>
* <li>Indeterminate progress bar</li>
* <li>Optional subtext (from message.subtext) in 16px regular</li>

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.

I think you should keep the information on how the key is integrated in the message properties. This was included as it was unclear to developers.

String messageText = parseMessage(key, parameters, locale);
Component content = switch (type) {
case HTML -> new Html("<div style=\"display:contents\">%s</div>".formatted(messageText));
case HTML -> new Html("<span>%s</span>".formatted(messageText));

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.

Same error as before. If you wrap in a span instead of an invisible (display content) div, you loose any formatting in the messageText for linebreaks. This makes multiline messages impossible

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.

addressed

@KochTobi

Copy link
Copy Markdown
Contributor

Toast have different text sizes.

Screen.Recording.2026-07-24.at.08.24.29.mov

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)
3.9% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@sven1103
sven1103 requested a review from KochTobi July 24, 2026 11:07
KochTobi
KochTobi previously approved these changes Jul 29, 2026

@KochTobi KochTobi 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.

LGTM. However still some getStyle().set(\width`, some-value)` exist. Please have a look if you can replace them with existing or speaking CSS classes. Regarding the icons, why not use the vaadin icon instead of manipulating the HTML

var button = new Button(LumoIcon.CROSS.create());
private Button createCloseButton() {
var icon = new Icon();
icon.getElement().setAttribute("icon", "vaadin:close-small");

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.

why not have a vaadin icon here?

KochTobi
KochTobi previously approved these changes Jul 29, 2026

@KochTobi KochTobi 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.

Approved 😄 9,5/10 for a 10/10 please fix the ambivalent comment

KochTobi
KochTobi previously approved these changes Jul 29, 2026

@KochTobi KochTobi 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.

lgtm

@sven1103
sven1103 merged commit 2e1032a into development Jul 30, 2026
3 of 4 checks passed
@sven1103
sven1103 deleted the chore/notification-style-update branch July 30, 2026 06:07
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.

3 participants