Single-directory components in core

Created on 4 October 2022, over 1 year ago
Updated 19 June 2024, 7 days ago

Join #components for more.

Main objective

We want to simplify the front-end development workflow, and improve maintainability of core and contrib themes.

For that we will:

  • Reduce the number of framework implementation details required to put templated HTML, CSS, and JS in a Drupal page.
  • Define explicit component APIs, and provide a methodology to replace a component provided upstream (in a parent theme or module).

Goals

  1. HTML markup in core components can be changed without breaking backward compatibility (BC).
  2. CSS and JS for a component is scoped to the component and automatically attached to it.
  3. CSS and JS for a component provided by core can be changed without breaking BC.
  4. Components can be provided by any module and/or theme, and then overridden explicitly or using theme specificity rules.
  5. All the code necessary to render a component is in the component directory.
  6. Components declare their props explicitly. Component props are the API of the component.
  7. Rendering a component in Twig uses the familiar include/embed syntax, with support for Twig blocks (slots).
  8. Facilitate the implementation of component libraries for design systems of themes.
  9. Provide an optional way to document components.

Architecture

The main concept is the component directory. Everything related to the component will be stored in that directory.

A directory becomes a component directory if it meets the following criteria:

  • Contains a component definition file: my-component.component.yml
  • Contains, at least, a Twig file with the name of the component: my-component.twig.
  • It is stored in a module or a theme directory under "/components". Components directories can reside in nested directories for organization, but never inside another component directory.

β†’

Discovery

Components are plugins. The parsed component definition file (my-component.component.yml) constitutes the plugin definition. The plugin discovery will scan all modules and themes that contain a /components directory recursively using the existing YamlDirectoryDiscovery (using a recursive filtered iterator).

Automatic libraries

Each component will declare a library automatically with the CSS and JS assets contained in the component directory. This library will be attached to the page automatically when the component is rendered. This means that front-end developers no longer need to learn about declaring libraries, and how to attach them to the page.

It will be possible to override the automatically defined libraries when more control over its definition is needed. To do so you can add the library definition inside of the component metadata.

Component negotiation

Including a component in a Twig template looks like this:

{{ include('my-theme:my-component', {
  prop1: content.field_foo
}) }}

If the component is defined by a module then use 'my-module:my-component'. Components defined in modules cannot be replaced, and therefore the component will be found in the module folder.

When Drupal encounters 'my-theme:my-component' it will search for all the components with a machine name 'my-theme:my-component'. There might be more than one if a sub-theme is replacing that particular component (see Component replacement below). The negotiation will choose the component with the following priority rules.

  1. The component is in the active theme.
  2. The component is in one of the base themes of the active theme. The closer in the inheritance chain, the more priority.

This intentionally matches the priority rules used for templates sharing the same name.

Component replacement

Themes and modules can replace components provided by other themes and modules.

Themes can replace a component by using replaces: my-theme:my-component in the component metadata. Themes can replace components provided either by modules or by themes which they extend. Note that when Drupal encounters my-theme:my-component it my use a component defined in my-other-theme if it replaces the component.

Modules cannot replace components provided by themes or other modules.

In order to replace a component, the props and slots declared on the component replacement need to be compatible with the ones declared by the component being replaced. This will be enforced programmatically.

Edge cases

PHP code

Initially we will not allow components to declare PHP code to be executed in the context of a component.

Follow-up tasks

✨ Feature request
Status

Fixed

Version

11.0 πŸ”₯

Component
single-directory componentsΒ  β†’

Last updated about 21 hours ago

Created by

e0ipso Can Picafort

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • e0ipso Can Picafort

    Updated issue summary with the corrected assumptions on the PoC β†’ .

  • πŸ‡«πŸ‡·France andypost

    Maybe instead of *.component.yaml we get in πŸ“Œ Create TemplateDiscovery for plugin managers to use Needs review and start use frontMatter for discovery?

  • πŸ‡©πŸ‡ͺGermany geek-merlin Freiburg, Germany

    @andypost: I opened ✨ Use FrontMatter for discovery Closed: won't fix , but see relevant points ageinst it.

  • πŸ‡©πŸ‡ͺGermany geek-merlin Freiburg, Germany
  • πŸ‡¦πŸ‡ΊAustralia amjad1233 Brisbane

    A huge +1, maybe we can also ship some base standard components(optional) as part of distributions-and-recipes... https://www.drupal.org/about/core/strategic-initiatives-distributions-an... β†’

  • e0ipso Can Picafort

    There is now an initial Merge Request to add a new experimental module into core for this endeavor ✨ Add Single Directory Components as a new experimental module Fixed . This took quite a lot of effort, but it will hopefully be a step forward! πŸŽ‰

  • πŸ‡·πŸ‡ΊRussia kostyashupenko Omsk

    It's all great ideas (i'm so happy to see so many initiatives regarding component's approach)
    My 50 cents are:
    1. You want to build UI components, right? Example: my-button. For which purposes we will be able to use it in drupal? Basically components are UI (at least they should, with no strict relation to the drupal's entities for example). That means - my-button component can be input--submit, or link, which is visually looking like a button (so i should be able to select my-button component in view display for example as a field formatter (or some similar technique). In terms of UI - both of these cases provides visual my-button component.
    2. How to deal with third-party libraries? Usually on the real projects we installing them with npm (or yarn or pnpm and so on) -> it downloads library sources into node_modules, and then we call it using import statement from js or css. What i want to say - is for example if your component requires slider library. Where this library should live? Inside of component folder (which is logical thing too), or outside, somewhere in the root of theme folder for example (if it's a theme library of course).
    3. How to deal with drupal libraries.

    Automatic libraries
    Each component will declare a library automatically with the CSS and JS assets contained in the component directory. This library will be attached to the page automatically when the component is rendered.

    I undestand it - but if to think more wide - we will need a component generator script (normally). Similar to drupal's script we using for starterkit theme. Maybe.. it's obvious. I don't know yet. On our real projects we using some custom approach and we have component generator script. By default this script can generate twig, js, css, yaml files. That means - if we have / we will have some template to generate component, we could consider it will have js file inside (which will be empty or almost empty inside by default). And if we don't need this js in drupal - makes sense to not load it in automatical way you proposed. Probably you can say "you can delete this unused js file", but as a developer i'm not sure if my js file will be used later on the project or not. Means - probably we could keep it, but to not use it in drupal. Somehow.

    Probably we could check UI Patterns approach - they have a definition of drupal's libraries in component-name.yml files, where you can call your css/js if required, also link dependencies to your component, and so on.

    4. For css and js we should have similar approach we have in drupal core -> source css file is `.pcss.css`, and for js files we also have to have 2 files -> one of these is source file, and another one is compiled. I'm not sure the naming of source file `.es6.js` we have in core - is good. Because usually we don't use babel or whatever else on the real projects already several years, since all major browsers supports es6 from the box https://caniuse.com/?search=es6
    So i'm thinking about maybe `component-name.behavior.js` or something.

    5. Styleguide. Please, somehow. At least let's discuss a global strategy for it. Projects needs in 2023 are - to have a very detailed styleguide, so any developer, or PM, or someone from customer's side can review all components existing on the web-site. On the huge projects we may have 10-20 different pages, so it's compilcated to reproduce one or another thing in drupal just to see if my component exist somewhere or not. The thing is - we need some place to review all components at once.

    For us (as a Skilld component) - we introduced storybook many years ago, and we still working with it. Basically it's just a tool where you can navigate through the components and build them from atoms (let's say from inputs, buttons, texts) till the full pages, containing views, header, footer, CTs, whatever else.

    Styleguide is very important

    6. Try to think about non-standard components, like image, or responsive image group components. Recently in Skilld i introduce the following - we'd better to document all image styles and responsive image groups in two different places somehow. It could be some 2 yml files for example, where all styles and groups are described. In that case drupal can somehow re-use this functionality, and also in storybook we can re-use same functionality.

    For now we are here - we described simple yml files, containing all image styles and responsive image groups, then we have a custom script for drupal, which can grab these yml files and generate configs for image styles and responsive image groups for drupal.

    I don't know (as i said, it's my 50 cents, at least you should keep these ideas in your head, since all these things can REALLY simplify lifes of developers & save time and money)

  • πŸ‡·πŸ‡ΊRussia kostyashupenko Omsk

    By the way - we (some people from Skilld, like me, https://www.drupal.org/u/finnsky β†’ , https://www.drupal.org/u/andypost β†’ , https://www.drupal.org/u/gaydabura β†’ ) are working on the similar stuff. But it's more custom stuff, than initiative for drupal core. Don't hesitate to ping us, to discuss all these beautiful ideas)

  • πŸ‡ΊπŸ‡ΈUnited States dalemoore

    Trying to read through these issues and understanding what this is doing a little better. This is confusing:

    Themes and modules can replace components provided by other themes and modules.

    Themes can replace a component by using replaces: my-theme:my-component in the component metadata. Themes can replace components provided either by modules or by themes which they extend. Note that when Drupal encounters my-theme:my-component it my use a component defined in my-other-theme if it replaces the component.

    Modules cannot replace components provided by themes or other modules.

    Can modules replace components from other themes or modules, or not? The first and last sentences contradict each other. Thanks!

  • πŸ‡³πŸ‡±Netherlands Martijn de Wit πŸ‡³πŸ‡± The Netherlands

    It is stored in a module or a theme directory under "templates/components". Components directories can reside in nested directories for organization, but never inside another component directory.

    Is it possible to change the locations of the components directory? My thought on this; it would better fit in the root of a theme or module. Because templates is "the old way" and not comprehensive for what is inside a component.

    Was also thinking of src, but there is also compiled stuff in there.

  • πŸ‡ΊπŸ‡ΈUnited States mherchel Gainesville, FL, US

    Is it possible to change the locations of the components directory? My thought on this; it would better fit in the root of a theme or module. Because templates is "the old way" and not comprehensive for what is inside a component.

    You are 100% correct. I had the same feedback and @e0ipso made the change, although we neglected to update the issue summary. Updating now.

  • πŸ‡ΊπŸ‡ΈUnited States mherchel Gainesville, FL, US

    Replying to @dalemoore comment in #83:

    Can modules replace components from other themes or modules, or not? The first and last sentences contradict each other. Thanks!

    My understanding is that modules cannot replace components from themes. That being said, I'd like to get confirmation from @e0ipso.

  • πŸ‡«πŸ‡·France pdureau Paris

    Hi Mateu, hi all,

    Wow! So much changes those last days. Thanks a lot for this hard work.

    So, I did some new tests today, from this git HEAD:

    $ git show 
    commit d35d23f9509b7f9802c95941064760139e012087 
    Author: Mateu AguilΓ³ Bosch (e0ipso) <mateu@mateuaguilo.com>
    Date:   Sun Apr 2 22:04:14 2023 +0200
    Merge branch '10.1.x' into 3340712-add-single-directory

    Here are the results.

    Twig template structure and template inheritance bias

    • Default attribute object: βœ… tested OK Tuesday 4th April
    • "normal" variables instead of block functions :βœ… tested OK Tuesday 4th April

    Definition YML file structure and overuse of JSON schema

    • Too deeply nested & required properties: βœ… the removal of "schema" level is considered as a good enough simplification. And it still the best way to express required props.
    • JSON schema for slots : βœ… tested OK Tuesday 4th April
    • JSON schema for props: In progress. Moved in an other issue: ✨ [PP-1] Allow schema references in Single Directory Component prop schemas Postponed

    "Cosmetic" feedbacks

    • Metadata : I didn't check, but not a big deal, so let's put this away
    • Libraries: βœ… libraryDependencies was merged into libraryOverrides as dependencies

    All this looks very good.

  • πŸ‡ΊπŸ‡ΈUnited States effulgentsia

    Is there currently any in-progress work on creating a contrib theme (or converting an existing one) to use components exclusively (as in, for all .html.twig files that aren't in the components directory to not render anything directly themselves and instead have their sole logic be to include and pass props to components)? If such work isn't already in-progress, is it part of the vision of SDC, or not really?

  • πŸ‡ΊπŸ‡ΈUnited States effulgentsia

    Related to #89, I just now opened ✨ Convert Umami's node.html.twig to use SDC components Needs work .

  • πŸ‡ΊπŸ‡ΈUnited States mherchel Gainesville, FL, US

    If such work isn't already in-progress, is it part of the vision of SDC, or not really?

    IMHO, not everything is or should be a component. That being said, I'm working on a USWDS theme that heavily leverages SDC.

  • πŸ‡§πŸ‡ͺBelgium DieterHolvoet Brussels
  • πŸ‡ΊπŸ‡ΈUnited States xjm

    I wonder if this issue should be credited and closed now that it's a stable part of the theme system? If we have post-stable roadmap work, it would make sense to file that as a separate issue.

  • πŸ‡ΊπŸ‡ΈUnited States xjm
  • Status changed to Fixed 7 days ago
  • πŸ‡ΊπŸ‡ΈUnited States mherchel Gainesville, FL, US

    I wonder if this issue should be credited and closed now that it's a stable part of the theme system?

    Makes sense to me. Closing and crediting, since SDC is now a part of core and stable (in 10.3). Thank you everyone!

Production build 0.69.0 2024