Frontend News from December 19, 2025

Dual price in Bulgaria

Please see related API news and the previous post. The JavaScript function will automatically switch the currencies according to the legal requirement by 1. 1. 2026.

Breaking change

♿️ Login widgets accessibility improvements (via Deferred Template Updates)

The login widgets and their triggers were updated to improve accessibility and structural consistency across templates. The fullscreen login was rebuilt using the native <dialog> element, login triggers were converted to semantic buttons, and focus handling was standardized to ensure predictable keyboard navigation and screen reader announcements.

Update management preview key: a11y_login

The change is scheduled for release on January 12, 2026. If you are an addon developer, please review the changes described below now and adjust your addons accordingly. On the release date, these improvements will be rolled out to the majority of customers. E-shops using Deferred Template Updates will have time until January 26, 2026 to modify their customizations and accept the update.

You can preview the changes by running the command shoptet.helpers.enableUpdatePreview('a11y_login') in the browser console.

Login in fullscreen modal (Techno, Tango, Step)

HTML structure changes

Fullscreen login is now implemented using the native <dialog> element. The dialog is labeled via aria-labelledby, which references the heading inside the dialog. Registration links inside the login form now redirect to a standalone registration page.

When the fullscreen login dialog is opened, the open attribute is added. After the dialog is opened via JavaScript, the is-visible class is added in the next animation frame to trigger the slide-in animation.

When closing the dialog, the is-visible class is removed and the is-closing class is added. This starts the closing animation. After the close animation finishes, the dialog is closed and both the open attribute and the is-closing class are removed.

Example structure:

<dialog id="login" class="dialog dialog--modal dialog--fullscreen js-dialog--modal is-visible" aria-labelledby="loginHeading" open>
  <div class="dialog__close dialog__close--arrow">
    <button type="button" class="btn toggle-window-arr" data-dialog-close>
      Zpět <span>do obchodu</span>
    </button>
  </div>

  <div class="dialog__wrapper">
    <div class="dialog__content dialog__content--form">
      <div class="dialog__header">
        <h2 id="loginHeading" class="dialog__heading dialog__heading--login">
          Přihlášení k vašemu účtu
        </h2>
      </div>

      <div id="customerLogin" class="dialog__body">
        <!-- <form> … </form> -->
      </div>
    </div>
  </div>
</dialog>

CSS changes

The CSS for the fullscreen login was rewritten to match the new HTML structure and dialog states. On the Techno template, the login modal now uses a light background, matching the visual style of the cart preview modal. Other templates received smaller visual changes.

JavaScript changes

The JavaScript handling the login dialogs was completely rewritten. The implementation now uses the native dialog API and adds custom behavior to control dialog states, animations, and lifecycle events.

When a login dialog is opened, it is opened via the native dialog API, which adds the open attribute. In the next animation frame, the is-visible class is added to start the opening animation. When the dialog is being closed, the is-visible class is removed and the is-closing class is added. After the closing animation finishes, the dialog is closed via the native API, the open attribute is removed, and the is-closing class is cleared. The element that triggered the dialog opening is tracked and used to restore focus after the dialog is closed.

Dialogs can be opened using the Enter or Space key on the triggering button. The Escape key closes the dialog. When the dialog opens, focus is moved into the dialog and, once it is closed, focus is restored to the element that triggered it. Modal dialogs use focus trapping to prevent focus from moving outside the dialog while it is open.

Login in popover (Waltz, Classic, Disco, Samba)

The login widget remains a smaller popover near the header. Accessibility and keyboard behavior were improved, while the visual appearance remains almost identical to the previous implementation.

HTML structure changes

The login popover keeps its existing wrapper structure and is implemented as a non-modal dialog. The container is now explicitly identified via a stable id and exposed as a dialog using role="dialog" and aria-labelledby. Its visibility for assistive technologies is controlled via the aria-hidden attribute.

Example wrapper:

<div id="login" class="user-action-login popup-widget login-widget" role="dialog" aria-labelledby="loginHeading" aria-hidden="false">

JavaScript and accessibility behavior

For non-modal login popovers, focus is not trapped inside the dialog. Instead, a controlled pass-through focus behavior is applied. When the popover is opened, focus moves into the dialog. When the user tabs past the last focusable element, the dialog closes and the user can continue navigating the page.

Trigger buttons in the header

Header login triggers were converted from links to semantic <button> elements to reflect that they open a dialog rather than navigate. Each trigger now exposes the dialog relationship via aria-controls (pointing to the dialog ID) and announces the intended behavior using aria-haspopup="dialog". For popover variants, the trigger also uses aria-expanded, which updates between true and false based on the open state.

Keyboard interaction follows the standard button behavior.

Registration triggers remain links and now lead to a standalone registration page.

The visual appearance of the header buttons remains unchanged, with only minor CSS adjustments applied.

Example HTML structure:


<!-- Fullscreen login dialog trigger -->
<button class="top-nav-button top-nav-button-login" type="button" data-dialog-id="login" aria-haspopup="dialog" aria-controls="login">
  <span>Přihlášení</span>
</button>

<!-- Login popover trigger -->
<button class="top-nav-button top-nav-button-login toggle-window" type="button" data-target="login" aria-haspopup="dialog" aria-controls="login" aria-expanded="false">
  <span>Přihlášení</span>
</button>

Improvements

Data Layer enhancements

Added information about freeshipping and page ID to the Data Layer.

♿️ Rating component on the product detail

The rating component on the product detail was reworked for better accessibility.

♿️ Question tooltip component accessibility

The question tooltip component now supports improved keyboard navigation and screen reader accessibility.


Bugfixes

Checkout price alignment

We fixed an issue with price alignment in the checkout, which was affected by some font and language combinations when the payment logo was displayed.

Disco template – product groups on mobile

We fixed an issue where the number of pieces for product groups could not be edited on phones in the Disco template.

Wholesale fields validation

We fixed validation warnings that appeared when the wholesale fields were made visible in the registration form.

Disco template – cart HTML classes

We unified the HTML classes for availability and quantity in the cart for the Disco template.

Flag display on 360 photos

We fixed an issue where the language/country flag was not showing correctly on 360 product photos.

Affiliate login and registration

We removed an unnecessary semicolon from the affiliate login and registration page.

Post navigation