ts4nfdi / terminology-service-suite

The Terminology Service Suite project is a collection of interactive widgets designed to ease the integration of terminology service functions into third-party applications.
https://ts4nfdi.github.io/terminology-service-suite/comp/latest/
MIT License
16 stars 2 forks source link

Widget styles override consuming app styles #144

Open jusa3 opened 1 month ago

jusa3 commented 1 month ago

Problem: The style issue stems from the global styles being injected by the EuiProvider from @elastic/eui and the Emotion CSS-in-JS library that it uses for theming and styling.

Temporary workaround: After the widget is rendered, remove the injected global styles. Specifically, the <style data-emotion="css-global" data-s=""></style> tag is dynamically generated by Emotion when global styles are applied, and it seems to override the app's existing styles, causing unintended changes. This happens because EuiProvider applies its own global styles which may conflict with or override the app’s styles. When we remove the style tag from the DOM, those global styles no longer apply.

jusa3 commented 1 month ago

Solution for React: To avoid using global styles in the widgets, we need to prevent or limit the application of the global CSS rules that EuiProvider and Emotion's CSS-in-JS solution inject into the document. We can do this by using a CacheProvider as described here: https://github.com/emotion-js/emotion/issues/1078

const styleElementId = "eui-local-style";

  const getOrCreateStyleElement = () => {
    let styleElement = document.querySelector(`#${styleElementId}`);
    if (!styleElement) {
      styleElement = document.createElement("style");
      styleElement.setAttribute("id", styleElementId);
      document.head.appendChild(styleElement);
    }
    return styleElement;
  };

  const createEmotionCache = () => {
    const styleElement = getOrCreateStyleElement();
    return createCache({
      key: "custom",
      container: styleElement
    });
  };

  const cache = createEmotionCache();

  return (
    <CacheProvider value={cache}>
      <EuiProvider colorMode="light">
        <EuiComboBox
        ...

This works well with React but not with the plainJS environment so the issue is still open.

jusa3 commented 1 month ago

More hints:

VincentKneip commented 1 month ago

Styling can be inspected via the tab "Stilbearbeitung". The styles overriding the global styles occur inside a website embedded style document. Normally, you can see which styles are applied by each style document, but for embedded style documents this does not seem to work.

Disabling this style document via the eye button on the left removes the unwanted styles, but also the styles for the imported widget:

embedded style document enabled embedded style document disabled
grafik grafik

If we could figure out a way to see which specific styles are applied by the styling document, we could manually include them in a proper styles file and discard the use of the EUI styles.

jusa3 commented 4 weeks ago

Disabling the global styles of the EuiProvider with the globalStyles={false} option is the solution to prevent global style injection, but results in a different look:

Before: image

After: image

johannes-darms commented 3 days ago

@jusa3 Are these issues still present in the latest version of elasticUI? The migration to emotion seems to be complete and since v96 https://github.com/elastic/eui/releases/v96.0.0 all css files are removed. Combined with the https://emotion.sh/docs/@emotion/cache#options and https://eui.elastic.co/v97.3.1/#/utilities/provider#theming-and-global-styles I would assume that the styling of elasticui does not interfere with other style definitions. If it does, we should open an issue with elasticui and try to resolve the problem.

If you plan to migrate away from elasticUI, please discuss this change with any downstream users of the widgets, as there are some implications.

johannes-darms commented 3 days ago

Regardless of the UI kit used, this problem will occur. Since each application has its own design language, it will most likely at some point conflict with the one used in a reused component. Therefore, as a user of a library, you can either accept the difference, if it is not so big that it affects the usability, or you have to adapt it to your liking. The latter requires a library that allows this kind of customisation and thus is bit more complex to create.