skilld-labs / ui-patterns-kaizen

Development for https://www.drupal.org/project/ui_suite
3 stars 0 forks source link

Component's approach. Problems #20

Open koskinpark opened 1 year ago

koskinpark commented 1 year ago

As you know - we are reworking kaizen. And we have two big subjects regarding this:

  1. Technical rework of kaizen (faster build, new features, storybook v7, re-think components, and so on)
  2. Connection of storybook into drupal.

In this issue i would like to touch a little bit first subject in terms of component's approaches. And i would like to hear your opinions.

Basically we have two approaches available now regarding all this:

  1. Approach proposed by @iberdinsky-skilld - as much as possible independed components. No relation (or almost no relation) between components. Component have everything it need inside of it. a) css variables b) third party libraries lives inside of component c) no include or extend in twig templates. d) all images, icons affected by component - lives inside of component

  2. Approach proposed by me - slightly independent components and more drupal way a) global css variables stored outside of components (same as we had in kaizen all these years). b) third party libraries lives outside of components (collected in one place. Each library have own drupal library declaration. Can be connected to component as a dependency). c) Possible includes or extends in twig (with relation to other components) d) For the icons - still global svg sprite, stored outside of components.

The thing is - both approaches are technically different and not only that. And it's impossible to have both approaches integrated in theme at the same time, so:

  1. Build affected. Build should be just different for 1st and 2nd approach
  2. Weight of page affected. Let's say with first approach you have same third party library (or image, or css variables) called several times from different components. And all these components are displayed on the same page in one time. Means big part of code is not re-used by components, but instead - each component have own dependencies. So potentially it can damage page's weight.
  3. Some other problems, but i don't want to describe it here, since it's minor problems.

Please share your thoughts regarding this. Thanks

koskinpark commented 1 year ago

I also would like to add my 50 cents to the previous message.

So if we speaking today - from my point of view fully independent components are just impossible in drupal today.

I understand approach of Ivan, it's cool actually. It means that.. probably Ivan will be able with his approach just to copy/paste his components from drupal project to another drupal project with no extra code at all. Or even more - he can copy component and put it in some other framework (vue react wordpress - any actually). Because component contains everything inside already.

It's a simple twig for render, css and js (mostly). And some yml configuration file of component with explanation about what component is.

So maybe an option will be to split kaizen on two "kaizens". One kaizen is a drupal theme, built with drupal core standards (more or less, of course).

And another kaizen with fully independed components (which can be used where? Probably somewhere)

iberdinsky-skilld commented 1 year ago

First of all we have to know few facts:

  1. Components will created and supported by large uncentralised teams from different companies.
  2. Components will migrate between projects. Inside one websites ecosystem or wider.
  3. Developers will have different level of knowledge of Drupal, frontend and component libraries.
  4. Drupal (core and modules) may attach libraries. Lot of projects(even ours) use Javascript Utilities
  5. Components will be ready out of the box even after simple copypaste and not require local or CI build actions for usage. Only for local modifications.
  6. Ecosystem should provide ability to create/update/codelint/storybook_review components without Drupal.
  7. Frontender may have minimal knowledge of Drupal for creating component system. And work with technologies he/she is familar with (Also facts 1, 3)
  8. Only few (1-5) of components in current regular projects have external dependency. This amount may grow while we will switch to Webcomponents / Generic Drupal Web Components / etc in future.
  9. Components may receive new Drupal implementation technics in future. https://www.drupal.org/project/sdc etc. They may have another libraries logic. UI Patterns yaml config is just one of possible interfaces of component.
  10. Component is not a package manager for Drupal. And doesn't have to provide its inner dependencies for Drupal.
  11. Component is abstract and has no strict relation with any Drupal entity. It may have implementations via different technics.
  12. Both approaches may still exist in one theme. It only question of build configuration.

When we know facts we can review +/-

Plus

Minus

Let's review and compare with Drupal way then:

What about stability?

Drupal way

// regular drupal script

(function (LIBRARYNAME, Drupal) {
  Drupal.behaviors.behaviorName = {
    attach() {
      // ....
    },
  };
})(LIBRARYNAME, Drupal);

It expects LIBRARYNAME from window scope what is lottery. It may:

Why it is bad to use browser Window? Let's ask expert.

ChatGPT(my favorite Junior Developer):

Image from Gyazo

In case of independent component it looks more stable?

// script type=module

import LIBRARYNAME from"./vendor.LIBRARYNAME.esm.js";
(function (LIBRARYNAME, Drupal) {
  Drupal.behaviors.behaviorName = {
    attach() {
      // ....
    },
  };
})(LIBRARYNAME, Drupal);

Component receive exact dependency. More details in this PR and in this blog article

More details about js modules

No restictions to use them on any project of current drupal browserlist:

So concluding stability Drupal way looks not so good.

What about perfomance?

In case of Drupal provided library we have no option to generate required bundle.

You cannot do it in Drupal.

// More detailes: https://glidejs.com/docs/setup/#using-es-modules
import Glide, { Controls, Breakpoints } from '@glidejs/glide/dist/glide.modular.esm'

And receive only Glide, Controls and Breakpoints. In Drupal you always will import ALL library bundle.

In this PR for example Slider has 59Kb. (22Kb minified, also vite has gzip from box) instead of 97Kb default Glide build.

So concluding perfomance Drupal way looks not so good.

Is it really NOT a Drupal way?

So when any of these ^ (or others) concept will applied it will be real Drupal way.

Let's conclude with another opinion

Image from Gyazo

koskinpark commented 1 year ago

5. Components will be ready out of the box even after simple copypaste and not require local or CI build actions for usage. Only for local modifications.

When you moving components between projects with different design system applied, like: a) Other media breakpoints b) Other css variables system c) Other build process d) Something else

You need to adapt to the new system, instead of just copying / pasting your component into the new theme and that's it. Two examples: a) Your component have the following css: padding: var(--space-2); -> which returns 10px. In new design system --space-2 variable returns 20px. Which means after copying your component into new design system you anyway have to edit it to follow new design system. b) What's most important to me are media breakpoints. It's different per project. Which means if in your component you have the reference to the "my-xl-super-custom-breakpoint", but on new project you don't have such breakpoint -> again! You have to edit your component.

Maybe i could write more examples, but still. Components will be ready out of the box even after simple copypaste - it's not true, because it will not be ready out of the box.

=====

7. Frontender may have minimal knowledge of Drupal for creating component system

It's ok until you building only simple components like buttons, text fields, checkboxes. But when you need to build "always-unclear-stuff-for-frontender" like cart block (provided by commerce module), or more common example - Menu. Menu is not simple component. You have to learn drupal first before doing menu component. And of course there can be lots of other drupal specific components, not related at all to the BEM, Atomic design, or something else -> totally related to Drupal and you just can't do anything with it, and you will not be able to apply "abstract" component for it.

=====

8. Only few (1-5) of components in current regular projects have external dependency.

It's true and not true. There is always projects with a lot of external dependencies. What's most important to me is that with modular javascript drupal can't aggregate those files.

So with the following library declaration:

    - o-slider:
        js:
          assets/vendor.glide.modular.esm.js: { attributes: { type: module } }
          assets/o-slider.js: { attributes: { type: module } }

Drupal will never aggregate & minify these. This is what drupal returns on DOM with aggregation enabled: <script src="/path/to/your/modular/asset" type="module"></script>

Once again - i could agree that it's not a problem if you use 1-5 dependencies to modular assets. But as a said - it's not true. Because you can have a lot of dependencies on some projects. And if they will be all modular, it's bad.

I can answer to your next question @iberdinsky-skilld right now: theme shouldn't do drupal's job -> which means we don't need to compress & minify modular assets by theme's build -> it should be on Drupal's shoulders.

=====

Why it is bad to use browser Window? Let's ask expert.

If to read ChatGPT answer fully - you will see the two options it suggests:

  1. Use modular javascript
  2. Or provide a good namespace in window so nothing will be overridden.

I already answered above why module javascript is bad, now i would like to touch second option we have (an option drupal core follow):

  1. Drupal core "Oliver" theme: ((Drupal, once) => {

Dependency on once library.

  1. Drupal core "Claro" theme: (($, Drupal, once) => {

Again once and jquery

  1. ajax.js

})(jQuery, window, Drupal, drupalSettings, window.tabbable);

  1. A lot of other examples in core and in contrib modules: Screenshot from 2023-03-09 11-58-13 Screenshot from 2023-03-09 11-56-56

If you are not sure your dependency will exist or not, just write

     if (!window.something) {
        return;
      }

So about namespace - check webform module (a nice example illustrating unique namespaces). For us - we can use something like:

window.themeNameLibraries = {
  library1: {},
  library2: {},
  library3: {},
}

Such namespace will never have collisions.

If library is huge, like slider, you still can do this:

import Glide, { Controls, Breakpoints } from '@glidejs/glide'
window.themeNameLibraries.library.Glide = Glide;
window.themeNameLibraries.library.Controls = Controls;
window.themeNameLibraries.library.Breakpoints = Breakpoints;

So in the compiled version of this javascript file you will get only necessary methods or properties in window. And your resulting file weight will be not 60kb, but 20 or 30 (which is good).

This is where we are, this is standards of today's drupal core. And it's good standards from my point of view, so it's not worsens or slows down frontend development. And it's pretty clear approach even for non-drupal developers.

Once again, drupal's approach is next:

  1. We store libraries in one place (libraries can live in scope of theme, but not in scope of component).
  2. Reusable components. (Claro and Olivero)
  3. No modular assets
  4. Centralized global css variables and global javascript
  5. Centralized images and icons (can be called from any component, but sources shouldn't be placed in component).

By the way @iberdinsky-skilld i have a question for you. If component can be fully independent, then what to do with such simple situation?

  1. You have component menu, where any menu link can have any svg icon from design system.
  2. You have button component, where button can have any svg icon from design system.

Where to store icons? In both components? If your answer is "no, icons can be centralized in one place" -> that means your components already can not be fully independent. So when you will copy your component and put it in new theme on another project - you also have to take care about:

  1. Assets affected by component (if it all exist on the new theme)
  2. Third party libraries (if it's exist on new theme)
  3. css variables
  4. media breakpoints
  5. build process (if your component will be broken on build or not, on new project).

So moving components between projects with no extra code - it's just a dream. It only will work on the "equal" themes, which is never a case for us.

andypost commented 1 year ago

I'm sure we need to keep approch compatible with core, which is going SDC way. It will help onboarding and support cheaper

Ref https://www.lullabot.com/articles/getting-single-directory-components-drupal-core

adomontovich-skilld commented 1 year ago

Hello everyone. I don't want to repeat all the points which (i agree with) @koskinpark has been providing during this discussion here and in #stack.drupal and want just share my thoughts - as we are talking about Drupal (in general meaning, Drupal theme meaning, etc.) we should take into account next things:

  1. any approach should be convenient for every team member (as front-end as back-end);
  2. optimisation does matter, cause if you don't care about it, this can provoke a lot of issues after;
  3. to move forward we should accept main idea which is Drupal way theme based on kaizen and then add needed optimisation, do changes, create/make independent components which can/should be independent, add global variables if needed so, etc.
  4. as main of our projects are Drupal projects we should consider any approach as a Drupal way and take this a a start point;

I can discuss technical things but there've been a lot of technical points which we still trying to consider so i think my thought is clear and understandable without technical details. Please let me know if you have something to ask.

itkachenkoizzy commented 1 year ago

Hello.

  1. Components should be independent as much as possible, but at the same time, it's good to have global variables, third-party libraries outside of components, global svg sprite etc (because of optimization).
  2. Need to use an approach that will work for FE as for BE, since the project is developed by the team. If fully independent components, not taking into account Drupal, will cause problems for BE, there is no reason to use them.
  3. Copy/paste of the components between the projects it's always about modification, no matter how independent they are. So, from my point of view, it's not a point to make them completely independent.

While we're working with Drupal, we should take it into account.

iberdinsky-skilld commented 1 year ago

Let's review some current logical misunderstandings deeper then.

From theme of this topic:

Both approaches are technically different

In this PR i created 5 slider components with both approaches and more:

  1. Using Js modules.
  2. Using full bundled css/js.
  3. Using Window.OSlider.Glide namespace.
  4. Using Window.caesarTheme.Glide namespace.
  5. Using Webcomponents(Lit). Best option ever! JS/CSS is encapsulated. Core css variables onboarded.

Using current build system with yarn workspaces(which gives us extra features like dependency install). Build speed not critical on proof-of-concept step. Later it can be optimised with parallel yarn etc. From my point of view it is better to keep all options on board because of reasons explained later in this comment.

Second approach is more Drupal way.

In traditional Drupal way core, modules and themes declare their libraries and dependencies. Now we meet situation when some new entities(SDC, patterns) may declare libraries aswell.

And if we define Drupal way as the way of stability should components control their dependencies in their own code?

In first approach third party libraries lives inside of component.

They are not third-party libraries but personalised bundles. Smaller size. Smart optimisations. Strict dependency.

From next comments:

Usage of Js modules is bad and not a Drupal way.

From documentation and recent changelog and some future plans we may see that this is not restricted by Drupal. And probably will adapted one day. More than this we have much more optimisations possible with vite/webpack because they are developed to manage javascript assets.

There is always projects with a lot of external dependencies.

Yes. But mostly theme related. I checked few recent projects and didn't found projects where we had more than ~3 components(not helpers or whatever!) with external dependencies. They are mostly slider, selectbox maybe tabs and menu. If anyone knows more please extend this list with them.

There will be no global css variables in first approach / Impossible to copypaste because different variables in new theme.

First approach assumes global css variables using failback values

.a-button {
  --a-button-padding: var(--space-value-from-theme, 10px);

  padding: var(--a-button-padding);
}

Impossible to copypaste because different breakpoints in new theme.

If we copypaste generated css it will contain code like:

@media (min-width: 1024px) {
  .a-button {
    ...
  }
}

So no problem will be with breakpoints OOB. "my-xl-super-custom-breakpoint" looks some fantastic value. Plan is to unify breakpoints somehow. Especially for shared components.

Impossible to copypaste because different design.

True. Evil designers never paint same design for all websites :)

But ready OOB for component with dependencies means that it is ready in terms of functional. Not in terms of design. Slider will slide. Selectbox will selectbox(or selectboxing).

Impossible to copypaste because other build process.

We need build process to copypaste? Also build process planned to be same.

To build cart block (provided by commerce module), or more common example > - Menu will be hard for Frontentder unfamilar with Drupal

Cart block(same as all commerce theming) is obviously out of scope of this improvement (covering 80% of components implementation work). Simpe menu is panned be in default components list after setup.

It is possible to put component related bundle on theme level.

If we generate bundle for simple slider like:

import Glide, { Controls, Breakpoints } from '@glidejs/glide'
window.themeNameLibraries.library.Glide = Glide;
window.themeNameLibraries.library.Controls = Controls;
window.themeNameLibraries.library.Breakpoints = Breakpoints;

Few questions appear:

To be sure that dependency exist just write if (!window.something) {return;}

It will just return broken component until developer will not add this dependency on !different(theme) level in second approach. (remember decentralised companies and level of developers facts ^).

In first approach we guarantee that component will always work because of:

Optimisation does matter

Holy true. But if we talk about 20Kb in 2023 of page loading(which is better to be tested before declared, maybe first approach will be faster who knows) it doesn't have sence if we compare with development processes optimisation and obvious stability.

global assets disqussion moved to own ticket. I didn't work on it.

andypost commented 1 year ago

@iberdinsky-skilld maybe breakpoints could be passed as css-vars?

iberdinsky-skilld commented 1 year ago

@andypost they are passed as css vars. but it is possible in postcss only, we use https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-media with variables based on theme.breakpoints.yml as usual ;)