diff --git a/.env b/.env
deleted file mode 100644
index 0782604c..00000000
--- a/.env
+++ /dev/null
@@ -1 +0,0 @@
-PHP_VERSION=8.0
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 04f028a6..00000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-name: ci
-on: [push, pull_request_target]
-jobs:
-  automated-tests:
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        PHP_VERSION: ["7.4", "8.0", "8.1"]
-    env:
-      PHP_VERSION: ${{ matrix.php_version }}
-    steps:
-      - name: clone
-        uses: actions/checkout@v2
-      - run: docker-compose up -d
-      - name: composer install
-        run: docker-compose exec -T php composer install
-      - name: phpunit
-        run: docker-compose exec -T php ./vendor/bin/phpunit
-  code-sniffer:
-    runs-on: ubuntu-latest
-    env:
-      PHP_VERSION: "8.0"
-    steps:
-      - name: clone
-        uses: actions/checkout@v2
-      - run: docker-compose up -d
-      - name: composer install
-        run: docker-compose exec -T php composer install
-      - name: grumphp
-        run: docker-compose exec -T php ./vendor/bin/grumphp run
diff --git a/.github_changelog_generator b/.github_changelog_generator
deleted file mode 100644
index 6d437180..00000000
--- a/.github_changelog_generator
+++ /dev/null
@@ -1,5 +0,0 @@
-merge_prefix=**Merged Pull Requests**
-tag1='API change'
-include-labels='API change,Bug,New feature'
-enhancement_prefix=**New features**
-enhancement-labels='New feature'
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index d8633ada..00000000
--- a/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-/.idea
-/vendor
-/composer.lock
-/docs/_build
-/build/
-/phpunit.xml
-/docker-compose.override.yml
-/runner.yml
-/.editorconfig
-/.gitattributes
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ea637985..728e0aab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -255,4 +255,4 @@
 
 
 
-\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
\ No newline at end of file
+\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
diff --git a/LICENSE.txt b/LICENSE.txt
deleted file mode 100644
index d159169d..00000000
--- a/LICENSE.txt
+++ /dev/null
@@ -1,339 +0,0 @@
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                            Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-                            NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-                     END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License along
-    with this program; if not, write to the Free Software Foundation, Inc.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
diff --git a/README.md b/README.md
index 92f5baab..18def1bf 100644
--- a/README.md
+++ b/README.md
@@ -1,72 +1,29 @@
-# UI Patterns
+# UI Patterns 2.x
 
-[![Join the chat at https://gitter.im/nuvoleweb/ui_patterns](https://badges.gitter.im/nuvoleweb/ui_patterns.svg)](https://gitter.im/nuvoleweb/ui_patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[![Build Status](https://travis-ci.org/nuvoleweb/ui_patterns.svg?branch=8.x-1.x)](https://travis-ci.org/nuvoleweb/ui_patterns)
-[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nuvoleweb/ui_patterns/badges/quality-score.png?b=8.x-1.x)](https://scrutinizer-ci.com/g/nuvoleweb/ui_patterns/?branch=8.x-1.x)
-[![Documentation Status](https://readthedocs.org/projects/ui-patterns/badge/?version=8.x-1.x)](http://ui-patterns.readthedocs.io/en/8.x-1.x/?badge=8.x-1.x)
+Define and expose self-contained UI Components as Drupal plugins and use them seamlessly in Drupal development and site-building.
 
-Define and expose self-contained UI patterns as Drupal plugins and use them seamlessly as drop-in templates for 
-[panels](https://www.drupal.org/project/panels), [field groups](https://www.drupal.org/project/field_group), views,
-[Display Suite](https://www.drupal.org/project/ds) view modes and field templates. 
+Also called "components", UI patterns are reusable, nestable, guided by clear standards, and can be assembled together to build any number of applications. Examples: card, button, slider, pager, menu, toast...
 
-The UI Patterns module also integrates with with tools like [PatternLab](http://patternlab.io/) or modules like 
-[Component Libraries](https://www.drupal.org/project/components) thanks to 
-[definition overrides](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/patterns-definition.html#override-patterns-behavior).
-
-![Overview](https://raw.githubusercontent.com/nuvoleweb/ui_patterns/8.x-1.x/docs/images/patterns-overview.png)
+The UI Patterns module also integrates with with tools like [PatternLab](http://patternlab.io/)
+or modules like [Component Libraries](https://www.drupal.org/project/components)
+thanks to [definition overrides](https://www.drupal.org/docs/contributed-modules/ui-patterns/define-your-patterns#s-override-patterns-behavior).
 
 ## Project overview
 
-The UI Patterns project provides 6 modules:
+The UI Patterns project provides 5 modules:
 
-- **UI Patterns**: the main module, it exposes the UI Patterns system APIs and it does not do much more than that.
-- **UI Patterns Library**: allows to define patterns via YAML and generates a pattern library page available at `/patterns`
+- **UI Patterns**: the main module, based on Drupal Core SDC API, with additional powerful API and quality-of-life improvments
+- **UI Patterns Library**: generates a pattern library page available at `/patterns`
   to be used as documentation for content editors or as a showcase for business. Use this module if you don't plan to
-  use more advanced component library systems such as PatternLab or Fractal.  
-  [Learn more](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/patterns-definition.html)
-- **UI Patterns Field Group**: allows to use patterns to format field groups provided by the
-  [Field group](https://www.drupal.org/project/field_group) module.
-  [Learn more](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/field-group.html)
-- **UI Patterns Layouts**: allows to use patterns as layouts. This allows patterns to be used on
-  [Display Suite](https://www.drupal.org/project/ds) view modes or on [panels](https://www.drupal.org/project/panels) 
-  out of the box. [Learn more](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/layout-plugin.html)
-- **UI Patterns Display Suite**: allows to use patterns to format [Display Suite](https://www.drupal.org/project/ds)
-  field templates. [Learn more](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/field-templates.html)
-- **UI Patterns Views**: allows to use patterns as Views row templates.
-  [Learn more](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/views.html)
-
-## Try it out!
-
-Download and install the [Bootstrap Patterns](https://github.com/nuvoleweb/bootstrap_patterns) theme on a vanilla Drupal
-8 installation to quickly try out the UI Patterns module.
-
+  use more advanced component library systems such as Storybook, PatternLab or Fractal.
+  [Learn more](https://www.drupal.org/docs/contributed-modules/ui-patterns/define-your-patterns)
+- **UI Patterns Layouts**: allows to use patterns as layouts. This allows patterns to be used with Layout Builder,
+  [Display Suite](https://www.drupal.org/project/ds) or [Panels](https://www.drupal.org/project/panels)
+  out of the box. [Learn more](https://www.drupal.org/docs/contributed-modules/ui-patterns/use-patterns-as-layouts)
+- **UI Patterns Views**: allows to use patterns as Views styles or Views rows.
+  [Learn more](https://www.drupal.org/docs/contributed-modules/ui-patterns/use-patterns-with-views)
+- **UI Patterns Legacy**: Load your UI Patterns 1.x components inside UI Patterns 2.x
 
 ## Documentation
 
-Documentation is hosted on [Read the Docs](https://readthedocs.org/) and available [here](http://ui-patterns.readthedocs.io/en/8.x-1.x).
-
-To build the documentation make sure you setup your environment by following
-[these instructions](http://read-the-docs.readthedocs.io/en/latest/) first.
-
-After setting up your environment run:
-
-```
-$ cd docs
-$ make html
-```
-
-The documentation is then available at ``./docs/_build/html/index.html``.
-
-If you want to contribute documentation you can setup and auto-compile that will watch for documentation changes by running:
-
-```
-$ make livehtml
-```
-
-You can then preview the compiled documentation at ``http://127.0.0.1:8000``.
-
-To build the documentation using Docker run:
-
-```
-$ docker run -it -v $(pwd)/docs:/docs xeizmendi/docker-sphinx make --directory=/docs html
-```
+Documentation is available [here](https://www.drupal.org/docs/contributed-modules/ui-patterns).
diff --git a/composer.json b/composer.json
index fc932066..6a355514 100644
--- a/composer.json
+++ b/composer.json
@@ -1,77 +1,9 @@
 {
   "name": "drupal/ui_patterns",
+  "description": "Define and expose self-contained UI components and use them seamlessly in development and site-building.",
+  "license": "GPL-2.0-or-later",
   "type": "drupal-module",
-  "description": "UI Patterns.",
-  "keywords": ["drupal", "web", "ui"],
-  "license": "GPL-2.0+",
-  "minimum-stability": "dev",
-  "prefer-stable": true,
-  "authors": [
-    {
-      "name": "Nuvole Web",
-      "email": "info@nuvole.org"
-    }
-  ],
-  "require-dev": {
-    "composer/installers": "^1 || ^2",
-    "cweagans/composer-patches": "~1.4",
-    "drupal/core-composer-scaffold": "^8.8 || ^9",
-    "drupal/core-dev": "^8.8 || ^9",
-    "drupal/core-recommended": "^8.8 || ^9",
-    "drupal/ds": "~3",
-    "drupal/field_group": "~3",
-    "drupal/page_manager": "*",
-    "drupal/panels": "~4",
-    "drupal/paragraphs": "~1",
-    "drupal/token": "~1",
-    "drush/drush": "~10",
-    "openeuropa/task-runner-drupal-project-symlink": "^1.0-beta5",
-    "phpro/grumphp": "^1.5",
-    "phpspec/prophecy-phpunit": "^2"
-  },
-  "repositories": [
-    {
-      "type": "composer",
-      "url": "https://packages.drupal.org/8"
-    }
-  ],
-  "autoload": {
-    "psr-4": {
-      "Drupal\\ui_patterns\\": "./src"
-    }
-  },
-  "autoload-dev": {
-    "psr-4": {
-      "Drupal\\Tests\\ui_patterns\\": "./tests/src"
-    }
-  },
-  "scripts": {
-    "post-install-cmd": "./vendor/bin/run drupal:site-setup",
-    "post-update-cmd": "./vendor/bin/run drupal:site-setup"
-  },
-  "extra": {
-    "composer-exit-on-patch-failure": true,
-    "enable-patching": true,
-    "drupal-scaffold": {
-      "locations": {
-        "web-root": "build/"
-      }
-    },
-    "installer-paths": {
-      "build/core": ["type:drupal-core"],
-      "build/modules/contrib/{$name}": ["type:drupal-module"],
-      "build/profiles/contrib/{$name}": ["type:drupal-profile"],
-      "build/themes/contrib/{$name}": ["type:drupal-theme"]
-    }
-  },
-  "config": {
-    "sort-packages": true,
-    "allow-plugins": {
-      "composer/installers": true,
-      "cweagans/composer-patches": true,
-      "drupal/core-composer-scaffold": true,
-      "phpro/grumphp": true,
-      "dealerdirect/phpcodesniffer-composer-installer": true
-    }
+  "require": {
+    "drupal/token": "^1.0"
   }
 }
diff --git a/docker-compose.yml b/docker-compose.yml
deleted file mode 100644
index f64c80d0..00000000
--- a/docker-compose.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-version: "2"
-
-services:
-
-  mariadb:
-    image: wodby/mariadb:10.3-3.8.4
-    stop_grace_period: 30s
-    environment:
-      MYSQL_ROOT_PASSWORD: password
-      MYSQL_DATABASE: drupal
-      MYSQL_USER: drupal
-      MYSQL_PASSWORD: drupal
-
-  php:
-    image: wodby/drupal-php:${PHP_VERSION}
-    environment:
-      DB_HOST: mariadb
-      DB_USER: drupal
-      DB_PASSWORD: drupal
-      DB_NAME: drupal
-      DB_DRIVER: mysql
-      PHP_XDEBUG: 1
-      PHP_FPM_USER: wodby
-      PHP_FPM_GROUP: wodby
-      PHP_OPCACHE_PRELOAD_USER: wodby
-      PHP_XDEBUG_DEFAULT_ENABLE: 1
-      PHP_XDEBUG_REMOTE_CONNECT_BACK: 1
-      PHP_XDEBUG_REMOTE_HOST: "10.254.254.254"
-      PHP_XDEBUG_IDEKEY: "PHPSTORM"
-      PHP_IDE_CONFIG: "serverName=ui_patterns"
-    volumes:
-    - ./:/var/www/html
-
-  nginx:
-    image: wodby/nginx:1.15-5.0.0
-    depends_on:
-    - php
-    environment:
-      NGINX_STATIC_OPEN_FILE_CACHE: "off"
-      NGINX_ERROR_LOG_LEVEL: debug
-      NGINX_BACKEND_HOST: php
-      NGINX_SERVER_ROOT: /var/www/html/build
-      NGINX_VHOST_PRESET: drupal8
-    volumes:
-    - ./:/var/www/html
-    ports:
-    - "8080:80"
-
-  # If you would like to see what is going on you can run the following on your host:
-  # docker run --rm -p 4444:4444 -p 5900:5900 --network="host" selenium/standalone-chrome-debug:latest
-  # Newer version of this image might run into this issue:
-  # @link https://github.com/elgalu/docker-selenium/issues/20
-  selenium:
-    image: selenium/standalone-chrome-debug:3.11
-    expose:
-    - '4444'
-    environment:
-    - DISPLAY=:99
-    - SE_OPTS=-debug
-    - SCREEN_WIDTH=1280
-    - SCREEN_HEIGHT=800
-    - VNC_NO_PASSWORD=1
-    ports:
-    - '4444:4444'
-    - "5900:5900"
-    volumes:
-    - /dev/shm:/dev/shm
-
-volumes:
-  codebase:
diff --git a/docs/Makefile b/docs/Makefile
deleted file mode 100644
index 7dffbb8d..00000000
--- a/docs/Makefile
+++ /dev/null
@@ -1,195 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
-PAPER         =
-BUILDDIR      = _build
-
-# User-friendly check for sphinx-build
-ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
-$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
-endif
-
-# Internal variables.
-PAPEROPT_a4     = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
-
-help:
-	@echo "Please use \`make <target>' where <target> is one of"
-	@echo "  html       to make standalone HTML files"
-	@echo "  dirhtml    to make HTML files named index.html in directories"
-	@echo "  singlehtml to make a single large HTML file"
-	@echo "  pickle     to make pickle files"
-	@echo "  json       to make JSON files"
-	@echo "  htmlhelp   to make HTML files and a HTML help project"
-	@echo "  qthelp     to make HTML files and a qthelp project"
-	@echo "  applehelp  to make an Apple Help Book"
-	@echo "  devhelp    to make HTML files and a Devhelp project"
-	@echo "  epub       to make an epub"
-	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
-	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
-	@echo "  text       to make text files"
-	@echo "  man        to make manual pages"
-	@echo "  texinfo    to make Texinfo files"
-	@echo "  info       to make Texinfo files and run them through makeinfo"
-	@echo "  gettext    to make PO message catalogs"
-	@echo "  changes    to make an overview of all changed/added/deprecated items"
-	@echo "  xml        to make Docutils-native XML files"
-	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
-	@echo "  linkcheck  to check all external links for integrity"
-	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
-	@echo "  coverage   to run coverage check of the documentation (if enabled)"
-
-clean:
-	rm -rf $(BUILDDIR)/*
-
-html:
-	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
-	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
-	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
-	@echo
-	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
-	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
-	@echo
-	@echo "Build finished; now you can process the pickle files."
-
-json:
-	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
-	@echo
-	@echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
-	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
-	@echo
-	@echo "Build finished; now you can run HTML Help Workshop with the" \
-	      ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
-	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
-	@echo
-	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
-	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
-	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/UIPatterns.qhcp"
-	@echo "To view the help file:"
-	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/UIPatterns.qhc"
-
-applehelp:
-	$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
-	@echo
-	@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
-	@echo "N.B. You won't be able to view it unless you put it in" \
-	      "~/Library/Documentation/Help or install it in your application" \
-	      "bundle."
-
-devhelp:
-	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
-	@echo
-	@echo "Build finished."
-	@echo "To view the help file:"
-	@echo "# mkdir -p $$HOME/.local/share/devhelp/UIPatterns"
-	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/UIPatterns"
-	@echo "# devhelp"
-
-epub:
-	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
-	@echo
-	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo
-	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
-	@echo "Run \`make' in that directory to run these through (pdf)latex" \
-	      "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo "Running LaTeX files through pdflatex..."
-	$(MAKE) -C $(BUILDDIR)/latex all-pdf
-	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-latexpdfja:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo "Running LaTeX files through platex and dvipdfmx..."
-	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
-	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
-	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
-	@echo
-	@echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
-	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
-	@echo
-	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-texinfo:
-	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-	@echo
-	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
-	@echo "Run \`make' in that directory to run these through makeinfo" \
-	      "(use \`make info' here to do that automatically)."
-
-info:
-	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-	@echo "Running Texinfo files through makeinfo..."
-	make -C $(BUILDDIR)/texinfo info
-	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-gettext:
-	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
-	@echo
-	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-changes:
-	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
-	@echo
-	@echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
-	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
-	@echo
-	@echo "Link check complete; look for any errors in the above output " \
-	      "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
-	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
-	@echo "Testing of doctests in the sources finished, look at the " \
-	      "results in $(BUILDDIR)/doctest/output.txt."
-
-coverage:
-	$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
-	@echo "Testing of coverage in the sources finished, look at the " \
-	      "results in $(BUILDDIR)/coverage/python.txt."
-
-xml:
-	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
-	@echo
-	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
-
-pseudoxml:
-	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
-	@echo
-	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
-
-livehtml:
-	sphinx-autobuild -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
diff --git a/docs/conf.py b/docs/conf.py
deleted file mode 100644
index dc212ace..00000000
--- a/docs/conf.py
+++ /dev/null
@@ -1,291 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# UI Patterns documentation build configuration file, created by
-# sphinx-quickstart on Sun Jan  8 11:33:35 2017.
-#
-# This file is execfile()d with the current directory set to its
-# containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys
-import os
-import shlex
-
-# Check if this build is on readthedocs.org
-on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
-if not on_rtd:
-  import sphinx_rtd_theme
-  html_theme = "sphinx_rtd_theme"
-  html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration ------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
-# ones.
-extensions = []
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix(es) of source filenames.
-# You can specify multiple suffix as a list of string:
-# source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'UI Patterns'
-copyright = u'2017, Nuvole Web'
-author = u'Nuvole Web'
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = '1.x'
-# The full version, including alpha/beta/rc tags.
-release = '1.x'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#
-# This is also used if you do content translation via gettext catalogs.
-# Usually you set "language" from the command line for these cases.
-language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
-
-# The reST default role (used for this markup: `text`) to use for all
-# documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
-
-# If true, `todo` and `todoList` produce output, else they produce nothing.
-todo_include_todos = False
-
-
-# -- Options for HTML output ----------------------------------------------
-
-# The theme to use for HTML and HTML Help pages.  See the documentation for
-# a list of builtin themes.
-#html_theme = 'alabaster'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further.  For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# Add any extra paths that contain custom files (such as robots.txt or
-# .htaccess) here, relative to this directory. These files are copied
-# directly to the root of the documentation.
-#html_extra_path = []
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Language to be used for generating the HTML full-text search index.
-# Sphinx supports the following languages:
-#   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
-#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
-#html_search_language = 'en'
-
-# A dictionary with options for the search language support, empty by default.
-# Now only 'ja' uses this config value
-#html_search_options = {'type': 'default'}
-
-# The name of a javascript file (relative to the configuration directory) that
-# implements a search results scorer. If empty, the default will be used.
-#html_search_scorer = 'scorer.js'
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'UIPatternsdoc'
-
-# -- Options for LaTeX output ---------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-
-# Latex figure (float) alignment
-#'figure_align': 'htbp',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title,
-#  author, documentclass [howto, manual, or own class]).
-latex_documents = [
-  (master_doc, 'UIPatterns.tex', u'UI Patterns Documentation',
-   u'Nuvole Web', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output ---------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
-    (master_doc, 'uipatterns', u'UI Patterns Documentation',
-     [author], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output -------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-#  dir menu entry, description, category)
-texinfo_documents = [
-  (master_doc, 'UIPatterns', u'UI Patterns Documentation',
-   author, 'UIPatterns', 'One line description of project.',
-   'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
diff --git a/docs/content/developer-documentation.rst b/docs/content/developer-documentation.rst
deleted file mode 100644
index 23a01430..00000000
--- a/docs/content/developer-documentation.rst
+++ /dev/null
@@ -1,322 +0,0 @@
-Developer documentation
-=======================
-
-Render patterns programmatically
---------------------------------
-
-Patterns can be rendered programmatically by using the following syntax:
-
-.. code-block:: php
-
-   <?php
-   $elements['quote'] = [
-     '#type' => 'pattern',
-     '#id' => 'blockquote',
-     '#fields' => [
-       'quote' => 'You must do the things you think you cannot do.',
-       'attribution' => 'Eleanor Roosevelt'
-     ]
-   ];
-
-   \Drupal::service('renderer')->render($elements);
-
-The code above will produce the following result:
-
-.. image:: ../images/developer-1.png
-   :align: center
-   :width: 650
-
-To render a specific pattern variant use:
-
-.. code-block:: php
-
-   <?php
-   $elements['quote'] = [
-     '#type' => 'pattern',
-     '#id' => 'blockquote',
-     '#variant' => 'highlighted',
-     '#fields' => [
-       'quote' => 'You must do the things you think you cannot do.',
-       'attribution' => 'Eleanor Roosevelt'
-     ]
-   ];
-
-It is also possible to just render a pattern preview as displayed on the patterns overview page in the following way
-(since fields are already bundled within the pattern definition we don't need to re-declare them here):
-
-.. code-block:: php
-
-   <?php
-   $elements['quote'] = [
-     '#type' => 'pattern_preview',
-     '#id' => 'blockquote',
-   ];
-
-   \Drupal::service('renderer')->render($elements);
-
-
-Rendering the code above will produce the following output:
-
-.. image:: ../images/developer-2.png
-   :align: center
-   :width: 650
-
-To render a specific pattern preview variant use:
-
-.. code-block:: php
-
-   <?php
-   $elements['quote'] = [
-     '#type' => 'pattern_preview',
-     '#id' => 'blockquote',
-     '#variant' => 'highlighted',
-   ];
-
-
-Render patterns using Twig functions
-------------------------------------
-
-The UI Patterns module also exposes two Twig functions to easily render patterns into your Twig templates.
-
-The following two calls:
-
-.. code-block:: twig
-
-   {{ pattern('button', {title: 'Link title', url: 'http://example.com'}) }}
-   {{ pattern_preview('modal') }}
-
-Will print:
-
-.. image:: ../images/developer-3.png
-   :align: center
-   :width: 650
-
-To render pattern variants use:
-
-.. code-block:: twig
-
-   {{ pattern('button', {title: 'Link title', url: 'http://example.com'}, 'highlighted') }}
-   {{ pattern_preview('modal', 'highlighted') }}
-
-Since patterns are rendered using the render element described above all libraries and preprocess hooks will be ran when
-using Twig functions.
-
-Working with pattern suggestions
---------------------------------
-
-Modules that want to add theme hook suggestions to patterns can do that by implementing the following hook:
-
-.. code-block:: php
-
-   <?php
-   /**
-    * Provide hook theme suggestions for patterns.
-    *
-    * @see ui_patterns_theme_suggestions_alter()
-    */
-   function hook_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) {
-     if ($context->isOfType('views_row')) {
-       $hook = $variables['theme_hook_original'];
-       $view_name = $context->getProperty('view_name');
-       $display = $context->getProperty('display');
-
-       $suggestions[] = $hook . '__views_row__' . $view_name;
-       $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $display;
-     }
-   }
-
-The hook above is a ``hook_theme_suggestions_alter()`` specifically designed for patterns. The hook is invoked
-with a ``PatternContext`` object that describes information on where the current pattern is being used.
-
-Pattern suggestions can, for example, allow developers to use alternative pattern templates in specific contexts or to
-"massage" data before it sent to the pattern by implementing fine-grained preprocess hooks.
-
-The following suggestions are automatically exposed by the project's sub-modules:
-
-.. code-block:: php
-
-   <?php
-
-   // Suggestions for patterns used as layouts.
-   // @see ui_patterns_layouts_ui_patterns_suggestions_alter()
-   $suggestions[] = $hook . '__layout';
-   $suggestions[] = $hook . '__layout__' . $entity_type;
-   $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $bundle;
-   $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $view_mode;
-   $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $bundle . '__' . $view_mode;
-   $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $entity_id;
-
-   // Suggestions for patterns used as Display Suite field templates.
-   // @see ui_patterns_ds_ui_patterns_suggestions_alter()
-   $suggestions[] = $hook . '__ds_field_template';
-   $suggestions[] = $hook . '__ds_field_template__' . $field_name;
-   $suggestions[] = $hook . '__ds_field_template__' . $field_name . '__' . $entity_type;
-   $suggestions[] = $hook . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $bundle;
-   $suggestions[] = $hook . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $view_mode;
-   $suggestions[] = $hook . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode;
-
-   // Suggestions for patterns used as field groups templates.
-   // @see ui_patterns_field_group_ui_patterns_suggestions_alter()
-   $suggestions[] = $hook . '__field_group';
-   $suggestions[] = $hook . '__field_group__' . $group_name;
-   $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type;
-   $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle;
-   $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $view_mode;
-   $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode;
-
-   // Suggestions for patterns used as Views row templates.
-   // @see ui_patterns_views_ui_patterns_suggestions_alter()
-   $suggestions[] = $hook . '__views_row';
-   $suggestions[] = $hook . '__views_row__' . $view_name;
-   $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $display;
-
-When rendering a variant the following suggestions will be available too:
-
-.. code-block:: php
-
-   <?php
-
-   // Suggestions for patterns used as layouts.
-   // @see ui_patterns_layouts_ui_patterns_suggestions_alter()
-   $suggestions[] = $hook . '__variant_' . $variant . '__layout';
-   $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type;
-   $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $bundle;
-   $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $view_mode;
-   $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $bundle . '__' . $view_mode;
-   $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $entity_id;
-
-   // Suggestions for patterns used as Display Suite field templates.
-   // @see ui_patterns_ds_ui_patterns_suggestions_alter()
-   $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template';
-   $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name;
-   $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name . '__' . $entity_type;
-   $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $bundle;
-   $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $view_mode;
-   $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode;
-
-   // Suggestions for patterns used as field groups templates.
-   // @see ui_patterns_field_group_ui_patterns_suggestions_alter()
-   $suggestions[] = $hook . '__variant_' . $variant . '__field_group';
-   $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name;
-   $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type;
-   $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle;
-   $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $view_mode;
-   $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode;
-
-   // Suggestions for patterns used as Views row templates.
-   // @see ui_patterns_views_ui_patterns_suggestions_alter()
-   $suggestions[] = $hook . '__variant_' . $variant . '__views_row';
-   $suggestions[] = $hook . '__variant_' . $variant . '__views_row__' . $view_name;
-   $suggestions[] = $hook . '__variant_' . $variant . '__views_row__' . $view_name . '__' . $display;
-
-
-Expose source field plugins
----------------------------
-
-When using a pattern on a view or an entity display form we are provided with a set of possible patterns source fields
-that we can map to our pattern destination fields. Available source fields depends on the context in which a pattern is
-being configured.
-
-Pattern source fields are provided by plugins of type ``@UiPatternsSource``.
-
-For example, when a pattern is used as a Views row template then the ``UiPatternsSourceManager`` collects all plugins
-annotated with ``@UiPatternsSource`` and tagged by ``views_row``. A context array describing the current view is then
-passed to each of the ``@UiPatternsSource`` plugins.
-
-In the example below we can see the actual implementation of such a system:
-
-.. code-block:: php
-
-   <?php
-
-   namespace Drupal\ui_patterns_views\Plugin\UiPatterns\Source;
-
-   use Drupal\ui_patterns\Plugin\PatternSourceBase;
-
-   /**
-    * Defines Views row pattern source plugin.
-    *
-    * @UiPatternsSource(
-    *   id = "views_row",
-    *   label = @Translation("Views row"),
-    *   provider = "views",
-    *   tags = {
-    *     "views_row"
-    *   }
-    * )
-    */
-   class ViewsRowSource extends PatternSourceBase {
-
-     /**
-      * {@inheritdoc}
-      */
-     public function getSourceFields() {
-       $sources = [];
-       /** @var \Drupal\views\ViewExecutable $view */
-       $view = $this->getContextProperty('view');
-       foreach ($view->display_handler->getFieldLabels() as $name => $label) {
-         $sources[] = $this->getSourceField($name, $label);
-       }
-       return $sources;
-     }
-
-   }
-
-At the moment the available source plugin tags are the following:
-
-- ``entity_display``: provided by the ``ui_patterns`` module and triggered on an entity display configuration page.
-- ``ds_field_template``: provided by the ``ui_patterns_ds`` module and triggered when setting up a field template
-  on an entity display configuration page.
-- ``views_row``: provided by the ``ui_patterns_views`` module and triggered on a Views row setting pane.
-- ``test``: provided by the ``ui_patterns_test`` module and used in tests.
-
-
-Alter pattern configuration forms
----------------------------------
-
-You can alter UI Patterns configuration forms by implementing ``hook_ui_patterns_display_settings_form_alter()``.
-
-For example, the following implementation adds a CSS class input field to the pattern configuration:
-
-.. code-block:: php
-
-   <?php
-
-   /**
-    * Implements hook_ui_patterns_display_settings_form_alter().
-    *
-    * Add a css class name configuration option.
-    */
-    function my_module_ui_patterns_display_settings_form_alter(array &$form, array $configuration) {
-      $setting_value = isset($configuration['class_name']) ? $configuration[$key] : '';
-      $form['class_name'] = [
-        '#type' => 'input',
-        '#title' => t('Class name'),
-      ];
-    }
-
-This hook alter forms that are built using the ``PatternDisplayFormTrait`` trait, meaning:
-
-- Display Suite field templates
-- Field groups
-- Views
-
-If you want to alter an entity layout form that uses UI Patters for its layout use
-``hook_ui_patterns_layouts_display_settings_form_alter()`` instead, for example:
-
-.. code-block:: php
-
-   <?php
-
-   /**
-    * Implements hook_ui_patterns_layouts_display_settings_form_alter().
-    *
-    * Add a css class name configuration option.
-    */
-    function hook_ui_patterns_layouts_display_settings_form_alter(array &$form, PatternDefinition $definition, array $configuration) {
-      $class_name = isset($configuration['class_name']) ? $configuration['class_name'] : '';
-      $form['class_name'] = [
-        '#type' => 'input',
-        '#title' => t('Class name'),
-      ];
-    }
diff --git a/docs/content/field-group.rst b/docs/content/field-group.rst
deleted file mode 100644
index 090fd8e8..00000000
--- a/docs/content/field-group.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-Use patterns with Field Groups
-==============================
-
-Patterns can be used to style entities' `field groups <https://www.drupal.org/project/field_group>`_ thanks to the
-``ui_patterns_field_group`` module.
-
-For example, say we want to show some metadata associated with an article, such as author, post date and tags.
-
-After enabling the module we create a new field group of type **Pattern** and drag all fields you want to use in that
-group, as shown below:
-
-.. image:: ../images/fieldgroup-1.png
-   :align: center
-   :width: 450
-
-Once all fields are in place we access the field group settings and choose the **Metadata** pattern. At this point we
-map the fields to the pattern destination fields and save our settings:
-
-.. image:: ../images/fieldgroup-2.png
-   :align: center
-   :width: 450
-
-Articles will now always use the **Metadata** pattern to style that field group, as shown below:
-
-.. image:: ../images/fieldgroup-3.png
-   :align: center
-   :width: 550
-
diff --git a/docs/content/field-templates.rst b/docs/content/field-templates.rst
deleted file mode 100644
index 755925bb..00000000
--- a/docs/content/field-templates.rst
+++ /dev/null
@@ -1,47 +0,0 @@
-Use patterns with Field templates
-=================================
-
-Patterns can be used as Display Suite field templates by enabling the ``ui_patterns_ds`` module. This opens the
-following interesting possibilities:
-
-- Link fields can be styled as buttons by mapping their URL and link titles to specific pattern destinations.
-- Image fields can be styled as an "image with caption" by mapping a formatted image and title to specific pattern
-  destinations.
-
-Let's see how to implement the first example having the following pattern definition:
-
-.. code-block:: yaml
-
-   button:
-     label: Button
-     description: A simple button.
-     fields:
-       title:
-         type: text
-         label: Label
-         description: The button label
-         preview: Submit
-       url:
-         type: text
-         label: URL
-         description: The button URL
-         preview: http://example.com
-
-On the entity display setting page we access the link field setting by clicking on the gear icon:
-
-.. image:: ../images/field-template-1.png
-   :align: center
-   :width: 700
-
-Then, after selecting the **Pattern** field template and the **Button** pattern, we map the link field columns to the
-pattern's fields defined above:
-
-.. image:: ../images/field-template-2.png
-   :align: center
-   :width: 700
-
-Our multi-valued link field will then be formatted as follow:
-
-.. image:: ../images/field-template-3.png
-   :align: center
-   :width: 300
diff --git a/docs/content/layout-plugin.rst b/docs/content/layout-plugin.rst
deleted file mode 100644
index 188bfad3..00000000
--- a/docs/content/layout-plugin.rst
+++ /dev/null
@@ -1,56 +0,0 @@
-Use patterns as layouts
-=======================
-
-Patterns can be used as layouts thanks to the ``ui_patterns_layouts`` module.
-
-Once exposed as layouts patterns can be used to arrange fields on entities like nodes,
-`paragraphs <https://www.drupal.org/project/paragraphs>`_, etc. or to place blocks on a page using
-`Panels <https://www.drupal.org/project/panels>`_.
-
-In the example below we will style a **Jumbotron** paragraph using the Jumbotron paragraph.
-
-Once on the paragraph **Manage display** page we choose the **Jumbotron** pattern as layout:
-
-.. image:: ../images/layouts-1.png
-   :align: center
-   :width: 450
-
-After doing that the pattern fields will be exposed as layout regions, so given the following definition:
-
-.. code-block:: yaml
-
-   jumbotron:
-     label: Jumbotron
-     description: A lightweight, flexible component that can optionally extend the entire viewport to showcase key content on your site.
-     fields:
-       title:
-         type: text
-         label: Title
-         description: Jumbotron title.
-         preview: Hello, world!
-       subtitle:
-         type: text
-         label: Description
-         description: Jumbotron description.
-         preview: This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.
-
-We will get the following layout regions:
-
-.. image:: ../images/layouts-2.png
-   :align: center
-   :width: 450
-
-We can now arrange the paragraph fields on the layout and save our settings.
-
-The paragraph below:
-
-.. image:: ../images/layouts-3.png
-   :align: center
-   :width: 450
-
-will be now styled using the **Jumbotron** pattern as follows:
-
-.. image:: ../images/layouts-4.png
-   :align: center
-   :width: 550
-
diff --git a/docs/content/patterns-definition.rst b/docs/content/patterns-definition.rst
deleted file mode 100644
index 93562195..00000000
--- a/docs/content/patterns-definition.rst
+++ /dev/null
@@ -1,203 +0,0 @@
-Define your patterns
-====================
-
-Patterns can be exposed by both modules and themes by enabling the ``ui_patterns_library`` module.
-
-Defined patterns will be available at ``/patterns``, only accessible by roles having the ``access patterns page``
-permission. Below an example of a pattern library page styled using the `Bootstrap Patterns <https://github.com/nuvoleweb/bootstrap_patterns>`_
-theme:
-
-.. image:: ../images/pattern-library.png
-   :align: center
-
-Pattern definitions
--------------------
-
-To define your patterns simply create a YAML file named ``MY_MODULE.ui_patterns.yml`` or ``MY_THEME.ui_patterns.yml``
-and list them using the following format:
-
-.. code-block:: yaml
-
-   blockquote:
-     label: Blockquote
-     description: Display a quote with attribution information.
-     variants:
-       default:
-         label: Default
-         description: An ordinary quote.
-       highlighted:
-         label: Highlighted
-         description: A special quote.
-     fields:
-       quote:
-         type: text
-         label: Quote
-         description: Quote text.
-         preview: Life is like riding a bicycle. To keep your balance, you must keep moving.
-       attribution:
-         type: text
-         label: Attribution
-         description: Quote attribution.
-         preview: Albert Einstein
-     libraries:
-       - MY_MODULE/module_library_one
-       - MY_MODULE/module_library_two
-       - pattern_library_one:
-          css:
-            component:
-              css/my_component.css: {}
-              http://example.com/external.min.css: { type: external, minified: true }
-       - pattern_library_two:
-          js:
-            js/library_two.js: {}
-          dependencies:
-            - core/jquery
-
-Let's break this down:
-
-``id``
-    The root of a new pattern definition (``blockquote`` in the example above). It must contain only lowercase
-    characters, numbers, underscores and hyphens (i.e. it should validate against ``[^a-z0-9_-]+``).
-``label``
-    Pattern label, used on pattern library page.
-``description``
-    Pattern description (optional), used on pattern library page.
-``fields``
-    Hash defining the pattern fields (optional). Each field can have the following properties:
-
-    ``type``
-        Field type, can be ``text``, ``numeric``, etc. at the moment only used for documentation purposes. Optional.
-    ``label``
-        Field label, used on pattern library page.
-    ``description``
-        Field description, used on pattern library page. Optional.
-    ``preview``
-        Preview content, used on pattern library page. It can be either a string or a Drupal render array, in which case
-        we can use keys like ``type: processed_text`` or ``theme: image``. Optional.
-
-``variants``
-    Hash defining the pattern variants (optional). Each variant can have the following properties:
-
-    ``label``
-        Variant label, used on pattern library page.
-    ``description``
-        Variant description, used on pattern library page. Optional.
-
-``libraries``
-    List of libraries to be loaded when rendering the pattern (optional). UI patterns are supposed to be self-contained so they
-    should define along all needed libraries.
-
-Once the pattern is defined the module will expose them as standard Drupal theme definitions.
-
-For example, given the ``my_pattern`` pattern ID then a theme function ``pattern_my_pattern`` is created and,
-consequently, the module will look for a template file called  ``pattern-my-pattern.html.twig``.
-
-The Twig template can be placed it either under ``MY_MODULE/templates``, if the pattern is exposed by a module,
-or under ``MY_THEME/templates``, if it is exposed by a theme. As expected themes will override templates exposed by modules.
-
-For example, consider the following Twig template file ``pattern-blockquote.html.twig`` for the ``blockquote`` pattern
-defined above:
-
-.. code-block:: twig
-
-    <blockquote>
-      <p>{{ quote }}</p>
-      <footer>{{ attribution }}</footer>
-    </blockquote>
-
-
-The pattern will be rendered as shown below (styled using the `Bootstrap <https://www.drupal.org/project/bootstrap>`_ theme):
-
-.. image:: ../images/blockquote-preview.png
-   :align: center
-
-**Remember**: we can always visit the ``/pattern`` page in order to have access to a full preview of all our patterns.
-
-Organize your patterns in sub-folders
--------------------------------------
-
-Patterns can be defined using a single ``NAME.ui_patterns.yml`` file. However, in case of sites with a large number of
-patterns, this might quickly becomes difficult to manage.
-
-If that's the case pattern definitions can also be organised in sub-folders, as shown below:
-
-.. code-block:: bash
-
-    .
-    ├── templates
-    │   └── patterns
-    │       ├── button
-    │       │   ├── button.ui_patterns.yml
-    │       │   └── pattern-button.html.twig
-    │       ├── media
-    │       │   ├── media.ui_patterns.yml
-    │       │   └── pattern-media.html.twig
-    ...
-    │       └── pattern-jumbotron.html.twig
-    ├── ui_patterns_test_theme.info.yml
-    └── ui_patterns_test_theme.ui_patterns.yml
-
-**Note:** the example above is taken by the actual test target site that is used to test the module itself: have a look
-at ``./tests/README.md`` and at ``./tests/target/custom`` for working examples on how to use the UI Patterns module.
-
-Expose pattern assets as libraries
-----------------------------------
-
-In case you wish to bundle your assets within the pattern directory you can define libraries with the alternative syntax
-below:
-
-.. code-block:: yaml
-
-     blockquote:
-       label: Blockquote
-       ...
-       libraries:
-         ...
-         - pattern_library_one:
-            css:
-              component:
-                css/my_component.css: {}
-                http://example.com/external.min.css: { type: external, minified: true }
-         - pattern_library_two:
-            js:
-              js/library_two.js: {}
-
-Libraries defined as above will be automatically loaded when the pattern is rendered. They are also exposed as ordinary
-Drupal libraries as follows: ``ui_patterns/PATTERN_ID.LIBRARY_NAME``
-
-For example, the two local libraries above can be attached to your render arrays in the following way:
-
-.. code-block:: php
-
-   <?php
-   $build['#attached']['library'][] = 'ui_patterns/blockquote.pattern_library_one';
-   $build['#attached']['library'][] = 'ui_patterns/blockquote.pattern_library_two';
-
-Override patterns behavior
---------------------------
-
-The default behavior can be changed by using the following properties in you pattern definitions:
-
-``theme hook``
-    If specified it overrides the default ``pattern_[id]`` theme hook with the provided value; the template file will
-    change accordingly.
-``template``
-    If specified it overrides only the template file keeping the default ``pattern_[id]`` theme hook.
-``use``
-    If specified it will use a stand-alone Twig file as template. The value supports
-    `Twig namespaces <http://symfony.com/doc/current/templating/namespaced_paths.html>`_, so the following notations
-    are valid examples:
-
-.. code-block:: yaml
-
-   use: "@my_module/templates/my-template.html.twig"
-
-.. code-block:: yaml
-
-   use: "@molecules/media/media-block.twig"
-
-The possibility of using stand-alone Twig templates allows for a swift integration with tools like
-`PatternLab <http://patternlab.io/>`_ or modules like `Component Libraries <https://www.drupal.org/project/components>`_.
-
-**Attention:** always remember to double-quote ``use:`` values or some YAML parsers (including PatternLab's) will
-complain.
diff --git a/docs/content/tests.rst b/docs/content/tests.rst
deleted file mode 100644
index 2d5a7352..00000000
--- a/docs/content/tests.rst
+++ /dev/null
@@ -1,55 +0,0 @@
-Working with tests
-==================
-
-UI Patterns is tested using `PHPUnit in Drupal 8 <https://www.drupal.org/docs/8/phpunit>`_.
-
-To build the test site just run:
-
-.. code-block:: bash
-
-   $ composer install
-
-After that you'll find a fully functional Drupal 8 site under ``./build``, thanks to the integration with the
-`OpenEuropa Task Runner <https://github.com/openeuropa/task-runner>`_.
-
-Tu run tests use:
-
-.. code-block:: bash
-
-   $ ./vendor/bin/phpunit
-
-Working with coding standards
-=============================
-
-UI Patterns coding standards checks are ran using `GrumPHP <https://github.com/phpro/grumphp>`_.
-
-.. code-block:: bash
-
-   $ ./vendor/bin/grumphp run
-
-Docker Compose
-==============
-
-UI Patterns ships with a ``docker-compose.yml`` file which can be used to streamline local development and tests execution.
-
-Setup Docker Compose by copying ``docker-compose.yml`` to ``docker-compose.override.yml`` and replace ``${TRAVIS_PHP_VERSION}``
-with the desired PHP version (either "5.6" or "7.1").
-
-After that run:
-
-.. code-block:: bash
-
-   $ docker-compose up -d
-   $ docker-compose exec -u www-data php composer install
-   $ docker-compose exec -u www-data php ./vendor/bin/run drupal:site-setup
-   $ docker-compose exec -u www-data php ./vendor/bin/run drupal:site-install
-   $ docker-compose exec -u www-data php chown -R www-data:www-data build
-
-You'll then have a fully functional test site at `http://localhost:8080 <http://localhost:8080>`_.
-
-To run all tests use:
-
-.. code-block:: bash
-
-   $ docker-compose exec -u www-data php ./vendor/bin/grumphp run
-   $ docker-compose exec -u www-data php ./vendor/bin/phpunit
diff --git a/docs/content/views.rst b/docs/content/views.rst
deleted file mode 100644
index a0ac1db2..00000000
--- a/docs/content/views.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-Use patterns with Views
------------------------
-
-Patterns can be used as Views row templates thanks to the ``ui_patterns_views`` module, which exposes a **Patterns** row
-style plugin.
-
-.. image:: ../images/views-1.png
-   :align: center
-   :width: 550
-
-After choosing the **Pattern** row style plugin we can map the current Views display fields to the chosen pattern
-destinations, as shown below:
-
-.. image:: ../images/views-2.png
-   :align: center
-   :width: 550
-
-Views rows will now be styled using the selected pattern.
diff --git a/docs/images/blockquote-preview.png b/docs/images/blockquote-preview.png
deleted file mode 100644
index 0312bfab..00000000
Binary files a/docs/images/blockquote-preview.png and /dev/null differ
diff --git a/docs/images/developer-1.png b/docs/images/developer-1.png
deleted file mode 100644
index 35398edb..00000000
Binary files a/docs/images/developer-1.png and /dev/null differ
diff --git a/docs/images/developer-2.png b/docs/images/developer-2.png
deleted file mode 100644
index f59da9fa..00000000
Binary files a/docs/images/developer-2.png and /dev/null differ
diff --git a/docs/images/developer-3.png b/docs/images/developer-3.png
deleted file mode 100644
index 36caa2ea..00000000
Binary files a/docs/images/developer-3.png and /dev/null differ
diff --git a/docs/images/field-template-1.png b/docs/images/field-template-1.png
deleted file mode 100644
index 13285d96..00000000
Binary files a/docs/images/field-template-1.png and /dev/null differ
diff --git a/docs/images/field-template-2.png b/docs/images/field-template-2.png
deleted file mode 100644
index 9e6a7e23..00000000
Binary files a/docs/images/field-template-2.png and /dev/null differ
diff --git a/docs/images/field-template-3.png b/docs/images/field-template-3.png
deleted file mode 100644
index 05dfda9f..00000000
Binary files a/docs/images/field-template-3.png and /dev/null differ
diff --git a/docs/images/fieldgroup-1.png b/docs/images/fieldgroup-1.png
deleted file mode 100644
index 386bf0ce..00000000
Binary files a/docs/images/fieldgroup-1.png and /dev/null differ
diff --git a/docs/images/fieldgroup-2.png b/docs/images/fieldgroup-2.png
deleted file mode 100644
index 80a68966..00000000
Binary files a/docs/images/fieldgroup-2.png and /dev/null differ
diff --git a/docs/images/fieldgroup-3.png b/docs/images/fieldgroup-3.png
deleted file mode 100644
index a5637ddb..00000000
Binary files a/docs/images/fieldgroup-3.png and /dev/null differ
diff --git a/docs/images/layouts-1.png b/docs/images/layouts-1.png
deleted file mode 100644
index 096d2713..00000000
Binary files a/docs/images/layouts-1.png and /dev/null differ
diff --git a/docs/images/layouts-2.png b/docs/images/layouts-2.png
deleted file mode 100644
index 180d0b8e..00000000
Binary files a/docs/images/layouts-2.png and /dev/null differ
diff --git a/docs/images/layouts-3.png b/docs/images/layouts-3.png
deleted file mode 100644
index d9d0ec77..00000000
Binary files a/docs/images/layouts-3.png and /dev/null differ
diff --git a/docs/images/layouts-4.png b/docs/images/layouts-4.png
deleted file mode 100644
index be2d5a0d..00000000
Binary files a/docs/images/layouts-4.png and /dev/null differ
diff --git a/docs/images/pattern-library.png b/docs/images/pattern-library.png
deleted file mode 100644
index 994dd76b..00000000
Binary files a/docs/images/pattern-library.png and /dev/null differ
diff --git a/docs/images/patterns-overview.png b/docs/images/patterns-overview.png
deleted file mode 100644
index bbe945f9..00000000
Binary files a/docs/images/patterns-overview.png and /dev/null differ
diff --git a/docs/images/views-1.png b/docs/images/views-1.png
deleted file mode 100644
index 5132253a..00000000
Binary files a/docs/images/views-1.png and /dev/null differ
diff --git a/docs/images/views-2.png b/docs/images/views-2.png
deleted file mode 100644
index d5cbf0d0..00000000
Binary files a/docs/images/views-2.png and /dev/null differ
diff --git a/docs/index.rst b/docs/index.rst
deleted file mode 100644
index 90ff1669..00000000
--- a/docs/index.rst
+++ /dev/null
@@ -1,49 +0,0 @@
-Welcome to UI Patterns' documentation
-=====================================
-
-The UI Patterns module allows developers to define self-contained UI patterns as Drupal plugins and use them seamlessly
-in their `panels <https://www.drupal.org/project/panels>`_, `field groups <https://www.drupal.org/project/field_group>`_
-or `Display Suite <https://www.drupal.org/project/ds>`_  view modes.
-
-.. image:: images/patterns-overview.png
-   :align: center
-
-Project overview
-----------------
-
-The UI Patterns project provides 6 modules:
-
-* **UI Patterns**: the main module, it exposes the UI Patterns system APIs and it does not do much more than that.
-* **UI Patterns Library**: allows to define patterns via YAML and generates a pattern library page available at ``/patterns``
-  to be used as documentation for content editors or as a showcase for business. Use this module if you don't plan to
-  use more advanced component library systems such as PatternLab or Fractal.
-  `Learn more <http://ui-patterns.readthedocs.io/en/8.x-1.x/content/patterns-definition.html>`_
-* **UI Patterns Field Group**: allows to use patterns to format field groups
-  provided by the `Field group <https://www.drupal.org/project/field_group>`_ module.
-* **UI Patterns Layouts**: allows to use patterns as layouts. This allows patterns to be used on
-  `Display Suite <https://www.drupal.org/project/ds>`_  view modes or on `panels <https://www.drupal.org/project/panels>`_
-  out of the box.
-* **UI Patterns Display Suite**: allows to use patterns to format `Display Suite <https://www.drupal.org/project/ds>`_
-  field templates.
-* **UI Patterns Views**: allows to use patterns as Views row templates.
-
-By the way plugin definitions are handled the UI Patterns module also integrates with with tools like
-`PatternLab <http://patternlab.io/>`_ or modules like `Component Libraries <https://www.drupal.org/project/components>`_.
-
-Try it out
-^^^^^^^^^^
-
-Download and install the `Bootstrap Patterns <https://github.com/nuvoleweb/bootstrap_patterns>`_ theme on a vanilla
-Drupal 8 installation to quickly try out the UI Patterns module.
-
-.. toctree::
-   :caption: Table of Contents
-
-   content/patterns-definition
-   content/field-group
-   content/layout-plugin
-   content/field-templates
-   content/views
-   content/developer-documentation
-   content/tests
-
diff --git a/grumphp.drupal8.yml b/grumphp.drupal8.yml
deleted file mode 100644
index 7d9c0de2..00000000
--- a/grumphp.drupal8.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-parameters:
-  ascii:
-    failed: ~
-    succeeded: ~
-  tasks:
-    phpcs:
-      standard: vendor/drupal/coder/coder_sniffer/Drupal/
-      ignore_patterns:
-        - build/
-        - vendor/
-      triggered_by:
-        - php
-        - module
-        - install
-        - inc
-        - theme
diff --git a/grumphp.yml b/grumphp.yml
deleted file mode 100644
index 79311e22..00000000
--- a/grumphp.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-grumphp:
-  ascii:
-    failed: ~
-    succeeded: ~
-  tasks:
-    phpcs:
-      standard: vendor/drupal/coder/coder_sniffer/Drupal/
-      ignore_patterns:
-        - build/
-        - vendor/
-      triggered_by:
-        - php
-        - module
-        - install
-        - inc
-        - theme
diff --git a/modules/ui_patterns_ds/src/FieldTemplateProcessor.php b/modules/ui_patterns_ds/src/FieldTemplateProcessor.php
deleted file mode 100644
index cf54f7cd..00000000
--- a/modules/ui_patterns_ds/src/FieldTemplateProcessor.php
+++ /dev/null
@@ -1,170 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_ds;
-
-use Drupal\Core\Entity\ContentEntityBase;
-
-/**
- * Field template processor for Display Suite integration.
- *
- * @package Drupal\ui_patterns_ds
- */
-class FieldTemplateProcessor implements FieldTemplateProcessorInterface {
-
-  /**
-   * Variables array.
-   *
-   * @var array
-   */
-  protected $variables = [];
-
-  /**
-   * {@inheritdoc}
-   */
-  public function process(array &$variables) {
-    $this->variables = $variables;
-
-    $content = [];
-    foreach ($variables['items'] as $delta => $item) {
-      $fields = [];
-      foreach ($this->getMapping() as $mapping) {
-        $fields[$mapping['destination']][] = $this->getSourceValue($mapping, $delta);
-      }
-
-      $content['pattern_' . $delta] = [
-        '#type' => 'pattern',
-        '#id' => $this->getPatternId(),
-        '#variant' => $this->getVariant(),
-        '#fields' => $fields,
-        '#context' => $this->getContext(),
-        '#multiple_sources' => TRUE,
-      ];
-    }
-
-    $variables['pattern'] = $content;
-  }
-
-  /**
-   * Get source value.
-   *
-   * @param array $mapping
-   *   Mapping array.
-   * @param int $delta
-   *   Field delta.
-   *
-   * @return mixed
-   *   Source value.
-   */
-  public function getSourceValue(array $mapping, $delta) {
-    $value = $this->variables['items'][$delta]['content'];
-    if ($mapping['source'] != $this->getFieldName()) {
-      $column = $this->getColumnName($mapping['source']);
-      $value = $this->getEntity()->get($this->getFieldName())->getValue();
-      $value = $value[$delta][$column];
-    }
-    return $value;
-  }
-
-  /**
-   * Get field parent entity.
-   *
-   * @return \Drupal\Core\Entity\ContentEntityBase
-   *   Parent entity.
-   */
-  protected function getEntity() {
-    return $this->variables['element']['#object'];
-  }
-
-  /**
-   * Get Pattern ID.
-   *
-   * @return string
-   *   Pattern ID.
-   */
-  protected function getPatternId() {
-    return $this->getSetting('pattern');
-  }
-
-  /**
-   * Get mapping settings.
-   *
-   * @return array
-   *   Mapping settings.
-   */
-  protected function getMapping() {
-    return $this->getSetting('pattern_mapping', []);
-  }
-
-  /**
-   * Get mapping settings.
-   *
-   * @return string
-   *   Mapping settings.
-   */
-  protected function getVariant() {
-    return $this->getSetting('pattern_variant');
-  }
-
-  /**
-   * Get setting value or default to given value if none set.
-   *
-   * @param string $name
-   *   Setting name.
-   * @param string $default
-   *   Setting default value.
-   *
-   * @return mixed
-   *   Setting value.
-   */
-  protected function getSetting($name, $default = '') {
-    return $this->variables['ds-config']['settings'][$name] ?? $default;
-  }
-
-  /**
-   * Get field name.
-   *
-   * @return string
-   *   Field name.
-   */
-  protected function getFieldName() {
-    return $this->variables['field_name'];
-  }
-
-  /**
-   * Extract column name from a source name.
-   *
-   * @param string $source
-   *   Source name.
-   *
-   * @return string
-   *   Column name.
-   */
-  protected function getColumnName($source) {
-    return str_replace($this->getFieldName() . '__', '', $source);
-  }
-
-  /**
-   * Get pattern context.
-   *
-   * @return array
-   *   Pattern context.
-   */
-  protected function getContext() {
-    $element = $this->variables['element'];
-    $context = [
-      'type' => 'ds_field_template',
-      'field_name' => $this->getFieldName(),
-      'entity_type' => $element['#entity_type'],
-      'bundle' => $element['#bundle'],
-      'view_mode' => $element['#view_mode'],
-      'entity' => NULL,
-    ];
-
-    if (isset($element['#object']) && is_object($element['#object']) && $element['#object'] instanceof ContentEntityBase) {
-      $context['entity'] = $element['#object'];
-    }
-
-    return $context;
-  }
-
-}
diff --git a/modules/ui_patterns_ds/src/FieldTemplateProcessorInterface.php b/modules/ui_patterns_ds/src/FieldTemplateProcessorInterface.php
deleted file mode 100644
index e5f7f552..00000000
--- a/modules/ui_patterns_ds/src/FieldTemplateProcessorInterface.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_ds;
-
-/**
- * Field template processor interface.
- *
- * @package Drupal\ui_patterns_ds
- */
-interface FieldTemplateProcessorInterface {
-
-  /**
-   * Process field template variables.
-   *
-   * @param array $variables
-   *   Variables array.
-   *
-   * @see template_preprocess_field__pattern_ds_field_template()
-   */
-  public function process(array &$variables);
-
-}
diff --git a/modules/ui_patterns_ds/src/Plugin/DsFieldTemplate/Pattern.php b/modules/ui_patterns_ds/src/Plugin/DsFieldTemplate/Pattern.php
deleted file mode 100644
index c45e649e..00000000
--- a/modules/ui_patterns_ds/src/Plugin/DsFieldTemplate/Pattern.php
+++ /dev/null
@@ -1,190 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_ds\Plugin\DsFieldTemplate;
-
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\ds\Plugin\DsFieldTemplate\DsFieldTemplateBase;
-use Drupal\ui_patterns\Form\PatternDisplayFormTrait;
-use Drupal\ui_patterns\UiPatternsSourceManager;
-use Drupal\ui_patterns\UiPatternsManager;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\RequestStack;
-use Drupal\Core\Entity\EntityFieldManager;
-
-/**
- * Plugin for the expert field template.
- *
- * @DsFieldTemplate(
- *   id = "pattern",
- *   title = @Translation("Pattern"),
- *   theme = "pattern_ds_field_template",
- * )
- */
-class Pattern extends DsFieldTemplateBase implements ContainerFactoryPluginInterface {
-
-  use PatternDisplayFormTrait;
-
-  /**
-   * Module Handler.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler = NULL;
-
-  /**
-   * UI Patterns manager.
-   *
-   * @var \Drupal\ui_patterns\UiPatternsManager
-   */
-  protected $patternsManager;
-
-  /**
-   * UI Patterns manager.
-   *
-   * @var \Drupal\ui_patterns\UiPatternsSourceManager
-   */
-  protected $sourceManager;
-
-  /**
-   * Current $_POST parameters.
-   *
-   * @var \Symfony\Component\HttpFoundation\ParameterBag
-   */
-  protected $parameters;
-
-  /**
-   * Entity field manager service.
-   *
-   * @var \Drupal\Core\Entity\EntityFieldManager
-   */
-  protected $fieldManager;
-
-  /**
-   * Pattern constructor.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin_id for the plugin instance.
-   * @param mixed $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\ui_patterns\UiPatternsManager $patterns_manager
-   *   UI Patterns manager.
-   * @param \Drupal\ui_patterns\UiPatternsSourceManager $source_manager
-   *   UI Patterns source manager.
-   * @param \Symfony\Component\HttpFoundation\RequestStack $parameters
-   *   Current $_POST parameters.
-   * @param \Drupal\Core\Entity\EntityFieldManager $field_manager
-   *   Field manager.
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   Module handler.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, UiPatternsManager $patterns_manager, UiPatternsSourceManager $source_manager, RequestStack $parameters, EntityFieldManager $field_manager, ModuleHandlerInterface $module_handler) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->patternsManager = $patterns_manager;
-    $this->sourceManager = $source_manager;
-    $this->parameters = $parameters->getCurrentRequest()->request;
-    $this->fieldManager = $field_manager;
-    $this->moduleHandler = $module_handler;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('plugin.manager.ui_patterns'),
-      $container->get('plugin.manager.ui_patterns_source'),
-      $container->get('request_stack'),
-      $container->get('entity_field.manager'),
-      $container->get('module_handler')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function alterForm(&$form) {
-    $context = $this->getContext();
-    if ($this->isSupportedField($context)) {
-      $this->buildPatternDisplayForm($form, 'ds_field_template', $context, $this->getConfiguration());
-    }
-    else {
-      $form['#markup'] = $this->t("The current field is not supported.");
-    }
-  }
-
-  /**
-   * Get source field plugin context.
-   *
-   * @return array
-   *   Context array.
-   */
-  protected function getContext() {
-    $fields = $this->parameters->get('fields');
-    $field_name = $this->getCurrentField();
-
-    return [
-      'field_name' => $field_name,
-      'field_settings' => $fields[$field_name],
-      'entity_type' => $this->parameters->get('ds_entity_type'),
-      'bundle' => $this->parameters->get('ds_bundle'),
-      'view_mode' => $this->parameters->get('ds_view_mode'),
-    ];
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function defaultConfiguration() {
-    return [
-      'pattern' => '',
-      'pattern_variant' => '',
-      'pattern_mapping' => [],
-    ];
-  }
-
-  /**
-   * Get name of field currently being edited.
-   *
-   * @return string
-   *   Name of field currently being edited.
-   */
-  protected function getCurrentField() {
-    $fields = array_filter($this->parameters->get('fields', []), function ($field) {
-      return isset($field['settings_edit_form']['third_party_settings']['ds']['ft']['id']) && $field['settings_edit_form']['third_party_settings']['ds']['ft']['id'] == 'pattern';
-    });
-    $fields = array_keys($fields);
-    $field = reset($fields);
-
-    if (empty($field)) {
-      $trigger_element = $this->parameters->get('_triggering_element_name');
-      $field = str_replace('_plugin_settings_edit', '', $trigger_element);
-    }
-
-    return $field;
-  }
-
-  /**
-   * Pattern Display Suite field template plugin only supports actual fields.
-   *
-   * @param array $context
-   *   Current context.
-   *
-   * @return bool
-   *   TRUE if supported, FALSE otherwise.
-   */
-  protected function isSupportedField(array $context) {
-    /** @var \Drupal\field\Entity\FieldConfig $field */
-    if ($context['entity_type'] && $context['bundle']) {
-      $field = $this->fieldManager->getFieldDefinitions($context['entity_type'], $context['bundle']);
-      return isset($field[$context['field_name']]);
-    }
-    return FALSE;
-  }
-
-}
diff --git a/modules/ui_patterns_ds/src/Plugin/UiPatterns/Source/DsFieldSource.php b/modules/ui_patterns_ds/src/Plugin/UiPatterns/Source/DsFieldSource.php
deleted file mode 100644
index 54ebd5f2..00000000
--- a/modules/ui_patterns_ds/src/Plugin/UiPatterns/Source/DsFieldSource.php
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_ds\Plugin\UiPatterns\Source;
-
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\ds\Plugin\DsPluginManager;
-use Drupal\ui_patterns\Plugin\PatternSourceBase;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Defines Display Suite fields source plugin.
- *
- * @UiPatternsSource(
- *   id = "ds_field",
- *   label = @Translation("Display Suite"),
- *   provider = "ds",
- *   tags = {
- *     "entity_display"
- *   }
- * )
- */
-class DsFieldSource extends PatternSourceBase implements ContainerFactoryPluginInterface {
-
-  /**
-   * Entity manager service.
-   *
-   * @var \Drupal\ds\Plugin\DsPluginManager
-   */
-  protected $dsManager;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, DsPluginManager $ds_manager) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->dsManager = $ds_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('plugin.manager.ds')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getSourceFields() {
-    $sources = [];
-    $fields = $this->dsManager->getDefinitions();
-
-    foreach ($fields as $field) {
-      if (!$this->getContextProperty('limit')) {
-        $sources[] = $this->getSourceField($field['id'], $field['title']);
-      }
-      elseif (in_array($field['id'], $this->getContextProperty('limit'))) {
-        $sources[] = $this->getSourceField($field['id'], $field['title']);
-      }
-    }
-    return $sources;
-  }
-
-}
diff --git a/modules/ui_patterns_ds/src/Plugin/UiPatterns/Source/DsFieldTemplateSource.php b/modules/ui_patterns_ds/src/Plugin/UiPatterns/Source/DsFieldTemplateSource.php
deleted file mode 100644
index f391db1a..00000000
--- a/modules/ui_patterns_ds/src/Plugin/UiPatterns/Source/DsFieldTemplateSource.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_ds\Plugin\UiPatterns\Source;
-
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\ui_patterns\Plugin\PatternSourceBase;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Entity\EntityFieldManager;
-
-/**
- * Defines Display Suite field template source plugin.
- *
- * @UiPatternsSource(
- *   id = "ds_field_template",
- *   label = @Translation("Field template"),
- *   provider = "ds",
- *   tags = {
- *     "ds_field_template"
- *   }
- * )
- */
-class DsFieldTemplateSource extends PatternSourceBase implements ContainerFactoryPluginInterface {
-
-  /**
-   * Entity field manager service.
-   *
-   * @var \Drupal\Core\Entity\EntityFieldManager
-   */
-  protected $fieldManager;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityFieldManager $field_manager) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->fieldManager = $field_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('entity_field.manager')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getSourceFields() {
-    $sources = [];
-    $field_name = $this->getContextProperty('field_name');
-    $entity_type = $this->getContextProperty('entity_type');
-    $bundle = $this->getContextProperty('bundle');
-
-    /** @var \Drupal\field\Entity\FieldConfig $field */
-    $field = $this->fieldManager->getFieldDefinitions($entity_type, $bundle)[$field_name];
-    $label = $field->getLabel();
-
-    $sources[] = $this->getSourceField($field_name, $label);
-    foreach ($field->getFieldStorageDefinition()->getColumns() as $column_name => $column) {
-      $sources[] = $this->getSourceField($field_name . '__' . $column_name, $label . ': ' . $column_name);
-    }
-    return $sources;
-  }
-
-}
diff --git a/modules/ui_patterns_ds/templates/pattern-ds-field-template.html.twig b/modules/ui_patterns_ds/templates/pattern-ds-field-template.html.twig
deleted file mode 100644
index c46be2fd..00000000
--- a/modules/ui_patterns_ds/templates/pattern-ds-field-template.html.twig
+++ /dev/null
@@ -1,9 +0,0 @@
-{#
-/**
- * @file
- * Default UI Patterns for Display Suite field templates.
- *
- * @ingroup themeable
- */
-#}
-{{ pattern }}
diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/core.entity_view_display.node.article.default.yml b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/core.entity_view_display.node.article.default.yml
deleted file mode 100644
index 3188b7ba..00000000
--- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/core.entity_view_display.node.article.default.yml
+++ /dev/null
@@ -1,60 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.field.node.article.body
-    - node.type.article
-  module:
-    - ds
-    - text
-    - user
-third_party_settings:
-  ds:
-    layout:
-      id: ds_1col
-      library: null
-      disable_css: false
-      entity_classes: all_classes
-      settings:
-        wrappers:
-          ds_content: div
-        outer_wrapper: div
-        attributes: ''
-        link_attribute: ''
-        link_custom: ''
-        classes:
-          layout_class: {  }
-    regions:
-      ds_content:
-        - links
-        - body
-id: node.article.default
-targetEntityType: node
-bundle: article
-mode: default
-content:
-  body:
-    label: above
-    type: text_default
-    weight: 101
-    settings: {  }
-    third_party_settings:
-      ds:
-        ft:
-          id: pattern
-          settings:
-            pattern: field
-            pattern_variant: default
-            pattern_mapping:
-              'ds_field_template:body':
-                destination: value
-                weight: 0
-                plugin: ds_field_template
-                source: body
-    region: ds_content
-  links:
-    weight: 100
-    region: ds_content
-    settings: {  }
-    third_party_settings: {  }
-hidden: {  }
diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/field.field.node.article.body.yml b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/field.field.node.article.body.yml
deleted file mode 100644
index 8f3681d9..00000000
--- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/field.field.node.article.body.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.storage.node.body
-    - node.type.article
-  module:
-    - text
-id: node.article.body
-field_name: body
-entity_type: node
-bundle: article
-label: Body
-description: ''
-required: false
-translatable: true
-default_value: {  }
-default_value_callback: ''
-settings:
-  display_summary: true
-field_type: text_with_summary
diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/node.type.article.yml b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/node.type.article.yml
deleted file mode 100644
index f47f2add..00000000
--- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/node.type.article.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-langcode: en
-status: true
-dependencies: {  }
-name: Article
-type: article
-description: ''
-help: ''
-new_revision: true
-preview_mode: 1
-display_submitted: true
diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/templates/field.ui_patterns.yml b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/templates/field.ui_patterns.yml
deleted file mode 100644
index af8aeeb4..00000000
--- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/templates/field.ui_patterns.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-field:
-  label: "Field"
-  variants:
-    default:
-      label: "Default"
-    overridden:
-      label: "Overridden"
-  fields:
-    value:
-      type: "text"
-      label: "Value"
-      preview: "value"
-    format:
-      type: "text"
-      label: "Format"
-      preview: "format"
diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/templates/pattern-field.html.twig b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/templates/pattern-field.html.twig
deleted file mode 100644
index 17aa29ea..00000000
--- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/templates/pattern-field.html.twig
+++ /dev/null
@@ -1,8 +0,0 @@
-{#
-/**
- * @file
- * Test field pattern.
- */
-#}
-<p>{{ "Value:"|t }} {{ value }}</p>
-<p>{{ "Format:"|t }} {{ format }}</p>
diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/ui_patterns_ds_test.info.yml b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/ui_patterns_ds_test.info.yml
deleted file mode 100644
index 2142c359..00000000
--- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/ui_patterns_ds_test.info.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-name: 'UI Patterns Display Suite Test'
-type: module
-description: 'Test module for UI Patterns.'
-core: 8.x
-core_version_requirement: ^8 || ^9
-hidden: true
-package: 'User interface'
-dependencies:
-  - ui_patterns
-  - ui_patterns_ds
-  - ui_patterns_library
-
-config_devel:
-  install:
-  - core.entity_view_display.node.article.default
-  - field.field.node.article.body
-  - node.type.article
diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/ui_patterns_ds_test.install b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/ui_patterns_ds_test.install
deleted file mode 100644
index e1ebeb86..00000000
--- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/ui_patterns_ds_test.install
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains install file for ui_patterns_ds_test module.
- */
-
-/**
- * Implements hook_install().
- */
-function ui_patterns_ds_test_install() {
-  // Enable Display Suite field templates.
-  \Drupal::configFactory()
-    ->getEditable('ds.settings')
-    ->set('field_template', TRUE)
-    ->save();
-}
diff --git a/modules/ui_patterns_ds/tests/src/FunctionalJavascript/UiPatternsFieldRenderTest.php b/modules/ui_patterns_ds/tests/src/FunctionalJavascript/UiPatternsFieldRenderTest.php
deleted file mode 100644
index 72e931ed..00000000
--- a/modules/ui_patterns_ds/tests/src/FunctionalJavascript/UiPatternsFieldRenderTest.php
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns_ds\FunctionalJavascript;
-
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-use Drupal\Tests\ui_patterns\Traits\TwigDebugTrait;
-
-/**
- * Test Display Suite field template rendering.
- *
- * @group ui_patterns_ds
- */
-class UiPatternsFieldRenderTest extends WebDriverTestBase {
-
-  /**
-   * Default theme.
-   *
-   * @var string
-   */
-  protected $defaultTheme = 'stark';
-
-  /**
-   * Disable schema validation when running tests.
-   *
-   * @var bool
-   *
-   * @todo Fix this by providing actual schema validation.
-   */
-  protected $strictConfigSchema = FALSE;
-
-  use TwigDebugTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = [
-    'node',
-    'field',
-    'field_ui',
-    'text',
-    'ui_patterns',
-    'ui_patterns_ds',
-    'ui_patterns_library',
-    'ui_patterns_ds_test',
-  ];
-
-  /**
-   * Tests field template settings.
-   */
-  public function testUiPatternsFieldRendering() {
-    $assert_session = $this->assertSession();
-
-    $this->enableTwigDebugMode();
-
-    $user = $this->drupalCreateUser([], NULL, TRUE);
-    $this->drupalLogin($user);
-
-    $node = $this->drupalCreateNode([
-      'title' => 'Test article',
-      'body' => 'Test body',
-      'type' => 'article',
-    ]);
-
-    $this->drupalGet($node->toUrl());
-
-    // Assert correct variant suggestions.
-    $suggestions = [
-      'pattern-field--variant-default--ds-field-template--body--node--article--full.html.twig',
-      'pattern-field--variant-default--ds-field-template--body--node--full.html.twig',
-      'pattern-field--variant-default--ds-field-template--body--node--article.html.twig',
-      'pattern-field--variant-default--ds-field-template--body--node.html.twig',
-      'pattern-field--variant-default--ds-field-template--body.html.twig',
-      'pattern-field--variant-default--ds-field-template.html.twig',
-
-      'pattern-field--ds-field-template--body--node--article--full.html.twig',
-      'pattern-field--ds-field-template--body--node--full.html.twig',
-      'pattern-field--ds-field-template--body--node--article.html.twig',
-      'pattern-field--ds-field-template--body--node.html.twig',
-      'pattern-field--ds-field-template--body.html.twig',
-      'pattern-field--ds-field-template.html.twig',
-
-      'pattern-field--variant-default.html.twig',
-      'pattern-field.html.twig',
-    ];
-    foreach ($suggestions as $suggestion) {
-      $assert_session->responseContains($suggestion);
-    }
-
-    // Test content is rendered in the pattern.
-    $assert_session->pageTextContains('Value: Test body');
-  }
-
-}
diff --git a/modules/ui_patterns_ds/tests/src/FunctionalJavascript/UiPatternsFieldSettingsTest.php b/modules/ui_patterns_ds/tests/src/FunctionalJavascript/UiPatternsFieldSettingsTest.php
deleted file mode 100644
index cf9c1d1c..00000000
--- a/modules/ui_patterns_ds/tests/src/FunctionalJavascript/UiPatternsFieldSettingsTest.php
+++ /dev/null
@@ -1,113 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns_ds\FunctionalJavascript;
-
-use Drupal\Core\Entity\Entity\EntityViewDisplay;
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-
-/**
- * Test Display Suite field template settings UI.
- *
- * @group ui_patterns_ds
- */
-class UiPatternsFieldSettingsTest extends WebDriverTestBase {
-
-  /**
-   * Default theme.
-   *
-   * @var string
-   */
-  protected $defaultTheme = 'stark';
-
-  /**
-   * Disable schema validation when running tests.
-   *
-   * @var bool
-   *
-   * @todo Fix this by providing actual schema validation.
-   */
-  protected $strictConfigSchema = FALSE;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = [
-    'node',
-    'field',
-    'field_ui',
-    'text',
-    'ui_patterns',
-    'ui_patterns_ds',
-    'ui_patterns_library',
-    'ui_patterns_ds_test',
-  ];
-
-  /**
-   * Tests field template settings.
-   */
-  public function testUiPatternsFieldSettings() {
-    $session = $this->getSession();
-    $page = $session->getPage();
-    $assert_session = $this->assertSession();
-
-    $user = $this->drupalCreateUser([], NULL, TRUE);
-    $this->drupalLogin($user);
-
-    // Visit Article's default display settings page.
-    $this->drupalGet('/admin/structure/types/manage/article/display');
-
-    // Click on Body field display settings.
-    $page->pressButton('body_plugin_settings_edit');
-    $assert_session->assertWaitOnAjaxRequest();
-
-    // Select "Pattern" field template.
-    $page->selectFieldOption('Choose a Field Template', 'Pattern');
-    $assert_session->assertWaitOnAjaxRequest();
-
-    // Choose test pattern.
-    $page->selectFieldOption('fields[body][settings_edit_form][third_party_settings][ds][ft][settings][pattern]', 'Field');
-    $assert_session->assertWaitOnAjaxRequest();
-
-    // Choose test variant.
-    $page->selectFieldOption('Variant', 'Overridden');
-    $assert_session->assertWaitOnAjaxRequest();
-
-    // Map pattern fields.
-    $page->selectFieldOption('Destination for Body', '- Hidden -');
-    $page->selectFieldOption('Destination for Body: value', 'Value');
-    $page->selectFieldOption('Destination for Body: format', 'Format');
-
-    // Submit field settings.
-    // @todo Make sure values are persisted when re-editing the field settings.
-    $page->pressButton('Update');
-    $assert_session->assertWaitOnAjaxRequest();
-
-    // Save view mode setting page.
-    $page->pressButton('Save');
-
-    // Get default view mode for Article node bundle.
-    $display = EntityViewDisplay::load("node.article.default");
-
-    // Assert existence of third party settings.
-    $third_party_settings = $display->getComponent('body')['third_party_settings'];
-    $this->assertNotEmpty($third_party_settings['ds']['ft'], "Field template settings not found.");
-
-    // Assert settings value.
-    $settings = $third_party_settings['ds']['ft'];
-    $this->assertEquals($settings['id'], 'pattern');
-    $this->assertEquals($settings['settings']['pattern'], 'field');
-    $this->assertEquals($settings['settings']['pattern_variant'], 'overridden');
-
-    // Assert mappings.
-    $this->assertNotEmpty($settings['settings']['pattern_mapping'], "Pattern mapping is empty.");
-
-    $mapping = $settings['settings']['pattern_mapping'];
-    $this->assertArrayNotHasKey('ds_field_template:body', $mapping, "Body mapping found.");
-    $this->assertArrayHasKey('ds_field_template:body__value', $mapping, "Body value mapping not found.");
-    $this->assertArrayHasKey('ds_field_template:body__format', $mapping, "Body format mapping not found.");
-
-    $this->assertEquals($mapping['ds_field_template:body__value']['destination'], 'value', "Body value mapping not valid.");
-    $this->assertEquals($mapping['ds_field_template:body__format']['destination'], 'format', "Body format mapping not valid.");
-  }
-
-}
diff --git a/modules/ui_patterns_ds/ui_patterns_ds.info.yml b/modules/ui_patterns_ds/ui_patterns_ds.info.yml
deleted file mode 100644
index a37275e2..00000000
--- a/modules/ui_patterns_ds/ui_patterns_ds.info.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-name: UI Patterns Display Suite
-type: module
-description: Use patterns as Display Suite field templates. It also provides Display Suite pattern sources.
-core: 8.x
-core_version_requirement: ^8 || ^9
-package: User interface
-dependencies:
-  - ds:ds
-  - ui_patterns:ui_patterns
diff --git a/modules/ui_patterns_ds/ui_patterns_ds.module b/modules/ui_patterns_ds/ui_patterns_ds.module
deleted file mode 100644
index 07b22913..00000000
--- a/modules/ui_patterns_ds/ui_patterns_ds.module
+++ /dev/null
@@ -1,94 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains module file.
- */
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\ui_patterns\Form\PatternDisplayFormTrait;
-use Drupal\ui_patterns\Element\PatternContext;
-
-/**
- * Implements hook_theme().
- */
-function ui_patterns_ds_theme() {
-  return [
-    'pattern_ds_field_template' => [
-      'variables' => ['pattern' => NULL],
-    ],
-  ];
-}
-
-/**
- * Implements hook_form_FORM_ID_alter().
- */
-function ui_patterns_ds_form_entity_view_display_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {
-  array_unshift($form['actions']['submit']['#submit'], 'ui_patterns_ds_field_overview_submit');
-}
-
-/**
- * Form submit callback to fix the field_group configuration.
- *
- * @param array $form
- *   The form.
- * @param \Drupal\Core\Form\FormStateInterface $form_state
- *   The form state.
- */
-function ui_patterns_ds_field_overview_submit(array $form, FormStateInterface $form_state) {
-  /** @var \Drupal\Core\Entity\EntityFormInterface $entity_form */
-  /** @var \Drupal\Core\Entity\Display\EntityDisplayInterface $display */
-
-  $entity_form = $form_state->getFormObject();
-  $display = $entity_form->getEntity();
-  $components = array_filter($display->getComponents(), function ($component) {
-    return isset($component['third_party_settings']['ds']['ft']['settings']['pattern']);
-  });
-
-  foreach ($components as $name => $component) {
-    PatternDisplayFormTrait::processFormStateValues($component['third_party_settings']['ds']['ft']['settings']);
-    $display->setComponent($name, $component);
-  }
-
-  $display->save();
-}
-
-/**
- * Preprocess hook.
- *
- * @param array $variables
- *   Theme variables.
- */
-function template_preprocess_field__pattern_ds_field_template(array &$variables) {
-  \Drupal::service('ui_patterns_ds.field_template_processor')->process($variables);
-}
-
-/**
- * Implements hook_ui_patterns_suggestions_alter().
- */
-function ui_patterns_ds_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) {
-  if ($context->isOfType('ds_field_template')) {
-    $hook = $variables['theme_hook_original'];
-    $variant = $variables["variant"] ?? '';
-    $field_name = $context->getProperty('field_name');
-    $entity_type = $context->getProperty('entity_type');
-    $bundle = $context->getProperty('bundle');
-    $view_mode = $context->getProperty('view_mode');
-
-    $suggestions[] = $hook . '__ds_field_template';
-    $suggestions[] = $hook . '__ds_field_template__' . $field_name;
-    $suggestions[] = $hook . '__ds_field_template__' . $field_name . '__' . $entity_type;
-    $suggestions[] = $hook . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $bundle;
-    $suggestions[] = $hook . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $view_mode;
-    $suggestions[] = $hook . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode;
-
-    if (!empty($variant)) {
-      $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template';
-      $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name;
-      $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name . '__' . $entity_type;
-      $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $bundle;
-      $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $view_mode;
-      $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode;
-    }
-  }
-}
diff --git a/modules/ui_patterns_ds/ui_patterns_ds.services.yml b/modules/ui_patterns_ds/ui_patterns_ds.services.yml
deleted file mode 100644
index 1cea2e17..00000000
--- a/modules/ui_patterns_ds/ui_patterns_ds.services.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-services:
-  ui_patterns_ds.field_template_processor:
-    class: Drupal\ui_patterns_ds\FieldTemplateProcessor
diff --git a/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentOneForAllFormatter.php b/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentOneForAllFormatter.php
new file mode 100644
index 00000000..f0e16eb3
--- /dev/null
+++ b/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentOneForAllFormatter.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Drupal\ui_patterns_field_formatters\Plugin\Field\FieldFormatter;
+
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\FormatterBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\ui_patterns\Form\UiPatternsFormBuilderTrait;
+
+/**
+ * Plugin implementation of the 'component_all' formatter.
+ *
+ * Field types are altered in
+ * ui_patterns_field_formatters_field_formatter_info_alter().
+ *
+ * @FieldFormatter(
+ *   id = "component_all",
+ *   label = @Translation("Component (one for all)"),
+ *   field_types = {
+ *     "string"
+ *   },
+ * )
+ */
+class ComponentOneForAllFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
+
+  use UiPatternsFormBuilderTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultSettings() {
+    $settings = array_merge(
+      parent::defaultSettings(),
+      self::getComponentFormDefault(),
+    );
+    // Temp.
+    return $settings;
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, FormStateInterface $form_state) {
+    $form = parent::settingsForm($form, $form_state);
+    $plugin_manager = \Drupal::service('plugin.manager.sdc');
+    $component_id = "ui_patterns_test:card";
+    $component = $plugin_manager->find($component_id);
+    $context = [];
+    $form['ui_patterns'] = $this->buildComponentForm($form_state, $component, $context);
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+    $summary = parent::settingsSummary();
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function viewElements(FieldItemListInterface $items, $langcode) {
+    $component_id = "ui_patterns_test:card";
+    $variant_id = "";
+    $slots = $this->getSetting('slots');
+    $props = array_merge(
+    $this->getSetting('props'),
+    [
+      "variant" => $variant_id,
+    ]
+    );
+    return [
+      '#type' => 'component',
+      '#component' => $component_id,
+      '#slots' => $slots,
+      '#props' => $props,
+    ];
+  }
+
+}
diff --git a/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentOneForEachFormatter.php b/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentOneForEachFormatter.php
new file mode 100644
index 00000000..1c5be4f4
--- /dev/null
+++ b/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentOneForEachFormatter.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Drupal\ui_patterns_field_formatters\Plugin\Field\FieldFormatter;
+
+/**
+ * Plugin implementation of the 'component_each' formatter.
+ *
+ * Field types are altered in
+ * ui_patterns_field_formatters_field_formatter_info_alter().
+ *
+ * @FieldFormatter(
+ *   id = "component_each",
+ *   label = @Translation("Component (one for each)"),
+ *   field_types = {
+ *     "string"
+ *   },
+ * )
+ */
+class ComponentOneForEachFormatter extends ComponentOneForAllFormatter {
+
+}
diff --git a/modules/ui_patterns_field_formatters/src/Plugin/UiPatterns/Source/FieldMetaPropertiesSource.php b/modules/ui_patterns_field_formatters/src/Plugin/UiPatterns/Source/FieldMetaPropertiesSource.php
new file mode 100644
index 00000000..8c2f4d86
--- /dev/null
+++ b/modules/ui_patterns_field_formatters/src/Plugin/UiPatterns/Source/FieldMetaPropertiesSource.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\ui_patterns_field_formatters\Plugin\UiPatterns\Source;
+
+use Drupal\ui_patterns\Plugin\PatternSourceBase;
+
+/**
+ * Defines Field meta properties source plugin.
+ *
+ * @UiPatternsSource(
+ *   id = "field_meta_properties",
+ *   label = @Translation("Field meta properties"),
+ *   tags = {
+ *     "field_properties"
+ *   }
+ * )
+ */
+class FieldMetaPropertiesSource extends PatternSourceBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSourceFields() {
+    $sources = [];
+    $sources[] = $this->getSourceField('_label', 'Label');
+    $sources[] = $this->getSourceField('_formatted', 'Formatted values');
+    return $sources;
+  }
+
+}
diff --git a/modules/ui_patterns_field_formatters/src/Plugin/UiPatterns/Source/FieldRawPropertiesSource.php b/modules/ui_patterns_field_formatters/src/Plugin/UiPatterns/Source/FieldRawPropertiesSource.php
new file mode 100644
index 00000000..b58ddd03
--- /dev/null
+++ b/modules/ui_patterns_field_formatters/src/Plugin/UiPatterns/Source/FieldRawPropertiesSource.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\ui_patterns_field_formatters\Plugin\UiPatterns\Source;
+
+use Drupal\ui_patterns\Plugin\PatternSourceBase;
+
+/**
+ * Defines Field raw properties source plugin.
+ *
+ * @UiPatternsSource(
+ *   id = "field_raw_properties",
+ *   label = @Translation("Field raw properties"),
+ *   tags = {
+ *     "field_properties"
+ *   }
+ * )
+ */
+class FieldRawPropertiesSource extends PatternSourceBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSourceFields() {
+    $sources = [];
+    $storageDefinition = $this->getContextProperty('storageDefinition');
+    $fields = $storageDefinition->getPropertyNames();
+    foreach ($fields as $field) {
+      if (!$this->getContextProperty('limit')) {
+        $sources[] = $this->getSourceField($field, $storageDefinition->getPropertyDefinition($field)->getLabel());
+      }
+      elseif (in_array($field, $this->getContextProperty('limit'))) {
+        $sources[] = $this->getSourceField($field, $storageDefinition->getPropertyDefinition($field)->getLabel());
+      }
+    }
+    return $sources;
+  }
+
+}
diff --git a/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.info.yml b/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.info.yml
new file mode 100644
index 00000000..b30ab67d
--- /dev/null
+++ b/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.info.yml
@@ -0,0 +1,7 @@
+name: UI Patterns Field Formatters
+type: module
+description: Use UI components with field formatters.
+core_version_requirement: ^10
+package: User interface
+dependencies:
+  - ui_patterns:ui_patterns
diff --git a/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.module b/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.module
new file mode 100644
index 00000000..6eac378f
--- /dev/null
+++ b/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.module
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * @file
+ * UI Patterns Field Formatters module.
+ */
+
+use Drupal\Core\Routing\RouteMatchInterface;
+
+/**
+ * Implements hook_field_formatter_info_alter().
+ */
+function ui_patterns_field_formatters_field_formatter_info_alter(array &$info) {
+  /** @var \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager */
+  $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
+  $field_types = array_keys($field_type_manager->getDefinitions());
+  $info['component_all']['field_types'] = $field_types;
+  $info['component_each']['field_types'] = $field_types;
+}
diff --git a/modules/ui_patterns_field_group/README.md b/modules/ui_patterns_field_group/README.md
deleted file mode 100644
index 6e427526..00000000
--- a/modules/ui_patterns_field_group/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# UI Patterns Field Group
-
-Integrates UI Patterns with the [Field Group](https://www.drupal.org/project/field_group) module.  
diff --git a/modules/ui_patterns_field_group/src/Plugin/field_group/FieldGroupFormatter/PatternFormatter.php b/modules/ui_patterns_field_group/src/Plugin/field_group/FieldGroupFormatter/PatternFormatter.php
deleted file mode 100644
index 20970b0c..00000000
--- a/modules/ui_patterns_field_group/src/Plugin/field_group/FieldGroupFormatter/PatternFormatter.php
+++ /dev/null
@@ -1,186 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_field_group\Plugin\field_group\FieldGroupFormatter;
-
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\field_group\FieldGroupFormatterBase;
-use Drupal\ui_patterns\Form\PatternDisplayFormTrait;
-use Drupal\ui_patterns\UiPatternsSourceManager;
-use Drupal\ui_patterns\UiPatternsManager;
-use Drupal\ui_patterns_field_group\Utility\EntityFinder;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Plugin implementation of the 'paragraph' formatter.
- *
- * @FieldGroupFormatter(
- *   id = "pattern_formatter",
- *   label = @Translation("Pattern"),
- *   description = @Translation("Wrap fields as a pattern."),
- *   supported_contexts = {
- *     "view",
- *   }
- * )
- */
-class PatternFormatter extends FieldGroupFormatterBase implements ContainerFactoryPluginInterface {
-
-  use PatternDisplayFormTrait;
-
-  /**
-   * Module Handler.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler = NULL;
-
-  /**
-   * UI Patterns manager.
-   *
-   * @var \Drupal\ui_patterns\UiPatternsManager
-   */
-  protected $patternsManager;
-
-  /**
-   * UI Patterns manager.
-   *
-   * @var \Drupal\ui_patterns\UiPatternsSourceManager
-   */
-  protected $sourceManager;
-
-  /**
-   * Entity finder utility.
-   *
-   * @var \Drupal\ui_patterns_field_group\Utility\EntityFinder
-   */
-  protected $entityFinder;
-
-  /**
-   * Constructs a Drupal\Component\Plugin\PluginBase object.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin_id for the plugin instance.
-   * @param mixed $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\ui_patterns\UiPatternsManager $patterns_manager
-   *   UI Patterns manager.
-   * @param \Drupal\ui_patterns\UiPatternsSourceManager $source_manager
-   *   UI Patterns source manager.
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   Module handler.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, UiPatternsManager $patterns_manager, UiPatternsSourceManager $source_manager, ModuleHandlerInterface $module_handler) {
-    parent::__construct($plugin_id, $plugin_definition, $configuration['group'], $configuration['settings'], $configuration['label']);
-    $this->configuration = $configuration;
-    $this->patternsManager = $patterns_manager;
-    $this->sourceManager = $source_manager;
-    $this->entityFinder = new EntityFinder();
-    $this->moduleHandler = $module_handler;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('plugin.manager.ui_patterns'),
-      $container->get('plugin.manager.ui_patterns_source'),
-      $container->get('module_handler')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function preRender(&$element, $rendering_object) {
-
-    $fields = [];
-    $mapping = $this->getSetting('pattern_mapping');
-    foreach ($mapping as $field) {
-      $fields[$field['destination']][] = $element[$field['source']];
-    }
-
-    $element['#type'] = 'pattern';
-    $element['#id'] = $this->getSetting('pattern');
-    $element['#fields'] = $fields;
-    $element['#multiple_sources'] = TRUE;
-    $element['#variant'] = $this->getSetting('pattern_variant');
-
-    // Allow default context values to not override those exposed elsewhere.
-    $element['#context']['type'] = 'field_group';
-    $element['#context']['group_name'] = $this->configuration['group']->group_name;
-    $element['#context']['entity_type'] = $this->configuration['group']->entity_type;
-    $element['#context']['bundle'] = $this->configuration['group']->bundle;
-    $element['#context']['view_mode'] = $this->configuration['group']->mode;
-
-    // Pass current entity to pattern context, if any.
-    $element['#context']['entity'] = $this->entityFinder->findEntityFromFields($element['#fields']);
-  }
-
-  /**
-   * Get field group name.
-   *
-   * @return string
-   *   Field group name.
-   */
-  protected function getFieldGroupName() {
-    return $this->configuration['group']->group_name;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function settingsForm() {
-    $form = parent::settingsForm();
-    unset($form['id']);
-    unset($form['classes']);
-
-    if (isset($this->configuration['group']->children) && !empty($this->configuration['group']->children)) {
-      $context = [
-        'entity_type' => $this->configuration['group']->entity_type,
-        'entity_bundle' => $this->configuration['group']->bundle,
-        'limit' => $this->configuration['group']->children,
-      ];
-
-      $this->buildPatternDisplayForm($form, 'entity_display', $context, $this->configuration['settings']);
-    }
-    else {
-      $form['message'] = [
-        '#markup' => $this->t('<b>Attention:</b> you have to add fields to this field group and save the whole entity display before being able to to access the pattern display configuration.'),
-      ];
-    }
-
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function settingsSummary() {
-    $label = $this->t('None');
-    if (!empty($this->getSetting('pattern'))) {
-      $label = $this->patternsManager->getDefinition($this->getSetting('pattern'))->getLabel();
-    }
-
-    return [
-      $this->t('Pattern: @pattern', ['@pattern' => $label]),
-    ];
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function defaultContextSettings($context) {
-    return [
-      'pattern' => '',
-      'pattern_mapping' => [],
-      'pattern_variant' => '',
-    ] + parent::defaultContextSettings($context);
-  }
-
-}
diff --git a/modules/ui_patterns_field_group/src/Utility/EntityFinder.php b/modules/ui_patterns_field_group/src/Utility/EntityFinder.php
deleted file mode 100644
index 4e06b65a..00000000
--- a/modules/ui_patterns_field_group/src/Utility/EntityFinder.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_field_group\Utility;
-
-use Drupal\Core\Entity\ContentEntityBase;
-
-/**
- * Find a content entity in a render array if keyed by '#object'.
- */
-class EntityFinder {
-
-  /**
-   * Find content entity object if any.
-   *
-   * @param array $fields
-   *   Fields array.
-   *
-   * @return \Drupal\Core\Entity\ContentEntityBase|null
-   *   Entity object or NULL if none found.
-   */
-  public function findEntityFromFields(array $fields) {
-
-    $iterator = new \RecursiveIteratorIterator(
-      new \RecursiveArrayIterator($fields),
-      \RecursiveIteratorIterator::SELF_FIRST
-    );
-
-    foreach ($iterator as $key => $value) {
-      if ($key === '#object' && $value instanceof ContentEntityBase) {
-        return $value;
-      }
-    }
-  }
-
-}
diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/core.entity_form_display.node.article.default.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/core.entity_form_display.node.article.default.yml
deleted file mode 100644
index 4032847a..00000000
--- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/core.entity_form_display.node.article.default.yml
+++ /dev/null
@@ -1,76 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.field.node.article.body
-    - field.field.node.article.field_text
-    - node.type.article
-  module:
-    - text
-id: node.article.default
-targetEntityType: node
-bundle: article
-mode: default
-content:
-  body:
-    type: text_textarea_with_summary
-    weight: 121
-    settings:
-      rows: 9
-      summary_rows: 3
-      placeholder: ''
-    third_party_settings: {  }
-    region: content
-  created:
-    type: datetime_timestamp
-    weight: 10
-    region: content
-    settings: {  }
-    third_party_settings: {  }
-  field_text:
-    weight: 122
-    settings:
-      size: 60
-      placeholder: ''
-    third_party_settings: {  }
-    type: string_textfield
-    region: content
-  promote:
-    type: boolean_checkbox
-    settings:
-      display_label: true
-    weight: 15
-    region: content
-    third_party_settings: {  }
-  status:
-    type: boolean_checkbox
-    settings:
-      display_label: true
-    weight: 120
-    region: content
-    third_party_settings: {  }
-  sticky:
-    type: boolean_checkbox
-    settings:
-      display_label: true
-    weight: 16
-    region: content
-    third_party_settings: {  }
-  title:
-    type: string_textfield
-    weight: -5
-    region: content
-    settings:
-      size: 60
-      placeholder: ''
-    third_party_settings: {  }
-  uid:
-    type: entity_reference_autocomplete
-    weight: 5
-    settings:
-      match_operator: CONTAINS
-      size: 60
-      placeholder: ''
-    region: content
-    third_party_settings: {  }
-hidden: {  }
diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/core.entity_view_display.node.article.default.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/core.entity_view_display.node.article.default.yml
deleted file mode 100644
index b7461a28..00000000
--- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/core.entity_view_display.node.article.default.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.field.node.article.body
-    - field.field.node.article.field_text
-    - node.type.article
-  module:
-    - field_group
-    - text
-    - user
-third_party_settings:
-  field_group:
-    group_pattern_group:
-      children:
-        - field_text
-      parent_name: ''
-      weight: 20
-      format_type: pattern_formatter
-      format_settings:
-        label: 'Pattern group'
-        pattern_variant: first
-        pattern: metadata
-        pattern_mapping:
-          'fields:field_text':
-            destination: field_1
-            weight: 0
-            plugin: fields
-            source: field_text
-      label: 'Pattern group'
-id: node.article.default
-targetEntityType: node
-bundle: article
-mode: default
-content:
-  body:
-    label: hidden
-    type: text_default
-    weight: 101
-    settings: {  }
-    third_party_settings: {  }
-    region: content
-  field_text:
-    weight: 102
-    label: above
-    settings:
-      link_to_entity: false
-    third_party_settings: {  }
-    type: string
-    region: content
-  links:
-    weight: 100
-    region: content
-    settings: {  }
-    third_party_settings: {  }
-hidden: {  }
diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.field.node.article.body.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.field.node.article.body.yml
deleted file mode 100644
index 8f3681d9..00000000
--- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.field.node.article.body.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.storage.node.body
-    - node.type.article
-  module:
-    - text
-id: node.article.body
-field_name: body
-entity_type: node
-bundle: article
-label: Body
-description: ''
-required: false
-translatable: true
-default_value: {  }
-default_value_callback: ''
-settings:
-  display_summary: true
-field_type: text_with_summary
diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.field.node.article.field_text.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.field.node.article.field_text.yml
deleted file mode 100644
index b4ea928b..00000000
--- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.field.node.article.field_text.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.storage.node.field_text
-    - node.type.article
-id: node.article.field_text
-field_name: field_text
-entity_type: node
-bundle: article
-label: Text
-description: ''
-required: false
-translatable: false
-default_value: {  }
-default_value_callback: ''
-settings: {  }
-field_type: string
diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.storage.node.field_text.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.storage.node.field_text.yml
deleted file mode 100644
index 1aeeadc2..00000000
--- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.storage.node.field_text.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  module:
-    - node
-id: node.field_text
-field_name: field_text
-entity_type: node
-type: string
-settings:
-  max_length: 255
-  is_ascii: false
-  case_sensitive: false
-module: core
-locked: false
-cardinality: 1
-translatable: true
-indexes: {  }
-persist_with_no_fields: false
-custom_storage: false
diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/node.type.article.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/node.type.article.yml
deleted file mode 100644
index f47f2add..00000000
--- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/node.type.article.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-langcode: en
-status: true
-dependencies: {  }
-name: Article
-type: article
-description: ''
-help: ''
-new_revision: true
-preview_mode: 1
-display_submitted: true
diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/templates/metadata.ui_patterns.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/templates/metadata.ui_patterns.yml
deleted file mode 100644
index 4905e1bc..00000000
--- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/templates/metadata.ui_patterns.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-metadata:
-  label: "Metadata"
-  variants:
-    first:
-      label: "First"
-    second:
-      label: "Second"
-  fields:
-    field_1:
-      type: "text"
-      label: "Field 1"
-      preview: "Field 1"
-    field_2:
-      type: "text"
-      label: "Field 2"
-      preview: "Field 3"
-    field_3:
-      type: "text"
-      label: "Field 3"
-      preview: "Field 3"
diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/templates/pattern-metadata.html.twig b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/templates/pattern-metadata.html.twig
deleted file mode 100644
index 6866bf69..00000000
--- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/templates/pattern-metadata.html.twig
+++ /dev/null
@@ -1,9 +0,0 @@
-{#
-/**
- * @file
- * Test metadata pattern.
- */
-#}
-<p>{{ "Field 1:"|t }} {{ field_1 }}</p>
-<p>{{ "Field 2:"|t }} {{ field_2 }}</p>
-<p>{{ "Field 3:"|t }} {{ field_3 }}</p>
diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/ui_patterns_field_group_test.info.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/ui_patterns_field_group_test.info.yml
deleted file mode 100644
index a6226496..00000000
--- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/ui_patterns_field_group_test.info.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: 'UI Patterns Field Group Test'
-type: module
-description: 'Test module for UI Patterns.'
-core: 8.x
-core_version_requirement: ^8 || ^9
-hidden: true
-package: 'User interface'
-dependencies:
-- ui_patterns
-- ui_patterns_field_group
-- ui_patterns_library
-
-config_devel:
-  install:
-  - core.entity_form_display.node.article.default
-  - core.entity_view_display.node.article.default
-  - field.field.node.article.body
-  - field.field.node.article.field_text
-  - field.storage.node.field_text
-  - node.type.article
diff --git a/modules/ui_patterns_field_group/tests/src/FunctionalJavascript/UiPatternsFieldGroupRenderTest.php b/modules/ui_patterns_field_group/tests/src/FunctionalJavascript/UiPatternsFieldGroupRenderTest.php
deleted file mode 100644
index 066c1e16..00000000
--- a/modules/ui_patterns_field_group/tests/src/FunctionalJavascript/UiPatternsFieldGroupRenderTest.php
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns_field_group\FunctionalJavascript;
-
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-use Drupal\Tests\ui_patterns\Traits\TwigDebugTrait;
-
-/**
- * Test Field Group pattern rendering.
- *
- * @group ui_patterns_field_group
- */
-class UiPatternsFieldGroupRenderTest extends WebDriverTestBase {
-
-  /**
-   * Default theme.
-   *
-   * @var string
-   */
-  protected $defaultTheme = 'stark';
-
-  /**
-   * Disable schema validation when running tests.
-   *
-   * @var bool
-   *
-   * @todo Fix this by providing actual schema validation.
-   */
-  protected $strictConfigSchema = FALSE;
-
-  use TwigDebugTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = [
-    'node',
-    'field',
-    'field_group',
-    'field_ui',
-    'text',
-    'ui_patterns_field_group_test',
-  ];
-
-  /**
-   * Test that pattern field group settings are correctly saved.
-   */
-  public function testUiPatternsFieldGroupRendering() {
-    $assert_session = $this->assertSession();
-
-    $this->enableTwigDebugMode();
-
-    $user = $this->drupalCreateUser([], NULL, TRUE);
-    $this->drupalLogin($user);
-
-    $node = $this->drupalCreateNode([
-      'title' => 'Test article',
-      'field_text' => 'Test text field',
-      'type' => 'article',
-    ]);
-
-    $this->drupalGet($node->toUrl());
-
-    // Assert correct variant suggestions.
-    $suggestions = [
-      'pattern-metadata--variant-first--field-group--group-pattern-group--node--article--default.html.twig',
-      'pattern-metadata--variant-first--field-group--group-pattern-group--node--default.html.twig',
-      'pattern-metadata--variant-first--field-group--group-pattern-group--node--article.html.twig',
-      'pattern-metadata--variant-first--field-group--group-pattern-group--node.html.twig',
-      'pattern-metadata--variant-first--field-group--group-pattern-group.html.twig',
-      'pattern-metadata--variant-first--field-group.html.twig',
-
-      'pattern-metadata--field-group--group-pattern-group--node--article--default.html.twig',
-      'pattern-metadata--field-group--group-pattern-group--node--default.html.twig',
-      'pattern-metadata--field-group--group-pattern-group--node--article.html.twig',
-      'pattern-metadata--field-group--group-pattern-group--node.html.twig',
-      'pattern-metadata--field-group--group-pattern-group.html.twig',
-      'pattern-metadata--field-group.html.twig',
-
-      'pattern-metadata--variant-first.html.twig',
-      'pattern-metadata.html.twig',
-    ];
-    foreach ($suggestions as $suggestion) {
-      $assert_session->responseContains($suggestion);
-    }
-
-    // Test field content is rendered in field group pattern.
-    $assert_session->pageTextContains('Field 1: Text Test text field');
-  }
-
-}
diff --git a/modules/ui_patterns_field_group/tests/src/FunctionalJavascript/UiPatternsFieldGroupSettingsTest.php b/modules/ui_patterns_field_group/tests/src/FunctionalJavascript/UiPatternsFieldGroupSettingsTest.php
deleted file mode 100644
index b6f4fedc..00000000
--- a/modules/ui_patterns_field_group/tests/src/FunctionalJavascript/UiPatternsFieldGroupSettingsTest.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns_field_group\FunctionalJavascript;
-
-use Behat\Mink\Element\DocumentElement;
-use Drupal\Core\Entity\Entity\EntityViewDisplay;
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-
-/**
- * Test Field Group pattern settings.
- *
- * @group ui_patterns_field_group
- */
-class UiPatternsFieldGroupSettingsTest extends WebDriverTestBase {
-
-  /**
-   * Default theme.
-   *
-   * @var string
-   */
-  protected $defaultTheme = 'stark';
-
-  /**
-   * Disable schema validation when running tests.
-   *
-   * @var bool
-   *
-   * @todo Fix this by providing actual schema validation.
-   */
-  protected $strictConfigSchema = FALSE;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = [
-    'node',
-    'field',
-    'field_group',
-    'field_ui',
-    'text',
-    'ui_patterns_field_group_test',
-  ];
-
-  /**
-   * Make sure a warning message is displayed when using pattern formatter.
-   */
-  public function testWarningMessage() {
-    $page = $this->getSession()->getPage();
-    $assert_session = $this->assertSession();
-
-    $user = $this->drupalCreateUser([], NULL, TRUE);
-    $this->drupalLogin($user);
-
-    // Visit Article's field group creation page.
-    $this->drupalGet('/admin/structure/types/manage/article/display/add-group');
-
-    // Add new Pattern field group.
-    $page->selectFieldOption('Add a new group', 'Pattern');
-    $assert_session->assertWaitOnAjaxRequest();
-
-    // Select pattern and save.
-    $page->fillField('Label', 'Metadata');
-    $page->waitFor(10, function (DocumentElement $page) {
-      return $page->hasContent('Machine name: group_metadata');
-    });
-    $page->pressButton('Save and continue');
-
-    // Assert warning message.
-    $assert_session->pageTextContains("Attention: you have to add fields to this field group and save the whole entity display before being able to to access the pattern display configuration.");
-  }
-
-  /**
-   * Test that pattern field group settings are correctly saved.
-   */
-  public function testUiPatternsFieldGroupSettings() {
-    $page = $this->getSession()->getPage();
-    $assert_session = $this->assertSession();
-
-    $user = $this->drupalCreateUser([], NULL, TRUE);
-    $this->drupalLogin($user);
-
-    // Visit Article's default view mode page.
-    $this->drupalGet('/admin/structure/types/manage/article/display');
-
-    // Click on field group settings button.
-    $page->pressButton('group_pattern_group_group_settings_edit');
-    $assert_session->assertWaitOnAjaxRequest();
-
-    // Choose variant.
-    $page->selectFieldOption('Variant', 'Second');
-    $page->selectFieldOption('Destination for Text', 'Field 2');
-    $assert_session->assertWaitOnAjaxRequest();
-
-    // Submit field group settings.
-    $page->pressButton('Update');
-    $assert_session->assertWaitOnAjaxRequest();
-
-    // Save view mode setting page.
-    $page->pressButton('Save');
-
-    // Get default view mode for Article node bundle.
-    $display = EntityViewDisplay::load("node.article.default");
-
-    // Assert existence of third party settings.
-    $settings = $display->getThirdPartySetting('field_group', 'group_pattern_group');
-
-    // Assert settings value.
-    $this->assertEquals($settings['format_type'], 'pattern_formatter');
-    $this->assertEquals($settings['format_settings']['pattern'], 'metadata');
-    $this->assertEquals($settings['format_settings']['pattern_variant'], 'second');
-
-    // Assert mappings.
-    $this->assertNotEmpty($settings['format_settings']['pattern_mapping'], "Pattern mapping is empty.");
-
-    $mapping = $settings['format_settings']['pattern_mapping'];
-    $this->assertArrayHasKey('fields:field_text', $mapping, 'Mapping not found.');
-    $this->assertEquals($mapping['fields:field_text']['destination'], 'field_2', "Mapping not valid.");
-  }
-
-}
diff --git a/modules/ui_patterns_field_group/tests/src/Unit/EntityFinderTest.php b/modules/ui_patterns_field_group/tests/src/Unit/EntityFinderTest.php
deleted file mode 100644
index 8259468d..00000000
--- a/modules/ui_patterns_field_group/tests/src/Unit/EntityFinderTest.php
+++ /dev/null
@@ -1,113 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns_field_group\Unit;
-
-use Drupal\Core\Entity\ContentEntityBase;
-use Drupal\Tests\UnitTestCase;
-use Drupal\ui_patterns_field_group\Utility\EntityFinder;
-
-/**
- * Test entity finder utility.
- *
- * @group ui_patterns
- */
-class EntityFinderTest extends UnitTestCase {
-
-  /**
-   * Test find entity from fields.
-   *
-   * @dataProvider fieldsDataProvider
-   */
-  public function testFindEntityFromFields($fields, $expected) {
-    $finder = new EntityFinder();
-    $entity = $finder->findEntityFromFields($fields);
-    $this->assertEquals($expected, $entity);
-  }
-
-  /**
-   * Test data provider.
-   *
-   * @return array
-   *   Test data.
-   */
-  public function fieldsDataProvider() {
-    $good = $this->getMockBuilder(ContentEntityBase::class)
-      ->disableOriginalConstructor()
-      ->getMock();
-    $bad = new \stdClass();
-
-    return [
-      // Found with singe value per field.
-      [
-        'fields' => [
-          'foo' => ['#object' => $good],
-          'bar' => ['#object' => $bad],
-        ],
-        'expected' => $good,
-      ],
-
-      // Found with singe value per field.
-      [
-        'fields' => [
-          'bar' => ['#object' => $bad],
-          'foo' => ['#object' => $good],
-        ],
-        'expected' => $good,
-      ],
-
-      // Found with multiple values per field.
-      [
-        'fields' => [
-          'foo' => [['#object' => $good]],
-          'bar' => [['#object' => $bad]],
-        ],
-        'expected' => $good,
-      ],
-
-      // Found with multiple values per field.
-      [
-        'fields' => [
-          'bar' => [
-            ['#object' => $bad],
-            ['#object' => $good],
-          ],
-          'foo' => [
-            ['#object' => $bad],
-            ['#object' => $bad],
-          ],
-        ],
-        'expected' => $good,
-      ],
-
-      // Found with one empty array field and multiple values per field.
-      [
-        'fields' => [
-          'foo' => [
-            [],
-            ['#object' => $good],
-          ],
-        ],
-        'expected' => $good,
-      ],
-
-      // Found with one empty null field and multiple values per field.
-      [
-        'fields' => [
-          'foo' => [NULL, ['#object' => $good],
-          ],
-        ],
-        'expected' => $good,
-      ],
-
-      // Not found with one empty null field and multiple values per field.
-      [
-        'fields' => [
-          'foo' => [NULL, ['#object' => $bad],
-          ],
-        ],
-        'expected' => NULL,
-      ],
-    ];
-  }
-
-}
diff --git a/modules/ui_patterns_field_group/ui_patterns_field_group.info.yml b/modules/ui_patterns_field_group/ui_patterns_field_group.info.yml
deleted file mode 100644
index 2c5a4b0b..00000000
--- a/modules/ui_patterns_field_group/ui_patterns_field_group.info.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-name: UI Patterns Field Group
-type: module
-description: Use patterns as field groups templates.
-core: 8.x
-core_version_requirement: ^8 || ^9
-package: User interface
-dependencies:
-  - field_group:field_group
-  - ui_patterns:ui_patterns
diff --git a/modules/ui_patterns_field_group/ui_patterns_field_group.module b/modules/ui_patterns_field_group/ui_patterns_field_group.module
deleted file mode 100644
index 130ed17e..00000000
--- a/modules/ui_patterns_field_group/ui_patterns_field_group.module
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains module file.
- */
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\ui_patterns\Element\PatternContext;
-
-/**
- * Implements hook_form_FORM_ID_alter().
- */
-function ui_patterns_field_group_form_entity_view_display_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {
-  array_unshift($form['actions']['submit']['#submit'], 'ui_patterns_field_group_field_group_field_overview_submit');
-}
-
-/**
- * Form submit callback to fix the field_group configuration.
- *
- * @param array $form
- *   The form.
- * @param \Drupal\Core\Form\FormStateInterface $form_state
- *   The form state.
- */
-function ui_patterns_field_group_field_group_field_overview_submit(array $form, FormStateInterface $form_state) {
-
-  $field_group_form_state = $form_state->get('field_group');
-  if (!empty($field_group_form_state)) {
-    foreach ($form['#fieldgroups'] as $group_name) {
-      // Only save updated groups.
-      if (!isset($field_group_form_state[$group_name])) {
-        continue;
-      }
-
-      if (isset($field_group_form_state[$group_name]->format_settings)) {
-        // Call static processFormStateValues if the plugin implements it.
-        $plugin_definition = \Drupal::service('plugin.manager.field_group.formatters')->getDefinition($field_group_form_state[$group_name]->format_type, FALSE);
-        if (method_exists($plugin_definition['class'], 'processFormStateValues')) {
-          call_user_func_array([
-            $plugin_definition['class'],
-            'processFormStateValues',
-          ],
-            [&$field_group_form_state[$group_name]->format_settings]);
-        }
-      }
-    }
-
-    // Set the form_state so that the submit hook of field_groups can work.
-    $form_state->set('field_group', $field_group_form_state);
-  }
-}
-
-/**
- * Implements hook_ui_patterns_suggestions_alter().
- */
-function ui_patterns_field_group_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) {
-  if ($context->isOfType('field_group')) {
-    $hook = $variables['theme_hook_original'];
-    $variant = $variables["variant"] ?? '';
-    $group_name = $context->getProperty('group_name');
-    $entity_type = $context->getProperty('entity_type');
-    $bundle = $context->getProperty('bundle');
-    $view_mode = $context->getProperty('view_mode');
-
-    $suggestions[] = $hook . '__field_group';
-    $suggestions[] = $hook . '__field_group__' . $group_name;
-    $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type;
-    $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle;
-    $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $view_mode;
-    $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode;
-
-    if (!empty($variant)) {
-      $suggestions[] = $hook . '__variant_' . $variant . '__field_group';
-      $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name;
-      $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type;
-      $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle;
-      $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $view_mode;
-      $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode;
-    }
-  }
-}
-
-/**
- * Implements hook_ui_patterns_destination_suggestions_alter().
- */
-function ui_patterns_field_group_ui_patterns_destination_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) {
-  if ($context->isOfType('field_group')) {
-    $hook = $variables['theme_hook_original'];
-    $variant = $variables["variant"] ?? '';
-    $group_name = $context->getProperty('group_name');
-    $entity_type = $context->getProperty('entity_type');
-    $bundle = $context->getProperty('bundle');
-    $view_mode = $context->getProperty('view_mode');
-    $pattern = $context->getProperty('pattern');
-    $field = $context->getProperty('field');
-
-    $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $pattern . '__' . $field;
-    $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $pattern . '__' . $field;
-    $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $pattern . '__' . $field;
-    $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $view_mode . '__' . $pattern . '__' . $field;
-    $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode . '__' . $pattern . '__' . $field;
-
-    if (!empty($variant)) {
-      $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $pattern . '__' . $field;
-      $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $pattern . '__' . $field;
-      $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $pattern . '__' . $field;
-      $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $view_mode . '__' . $pattern . '__' . $field;
-      $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode . '__' . $pattern . '__' . $field;
-    }
-  }
-}
diff --git a/modules/ui_patterns_layouts/README.md b/modules/ui_patterns_layouts/README.md
deleted file mode 100644
index f133c8dc..00000000
--- a/modules/ui_patterns_layouts/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# UI Patterns Layouts
-
-Integrates UI Patterns with the **Layout Discovery** core module.
-
-- To use pattern layouts on view modes install the [Display Suite](https://www.drupal.org/project/ds) module.
-- To use pattern layouts with [Page Manager](https://www.drupal.org/project/page_manager) 
-  install the [Panels](https://www.drupal.org/project/panels) module.
-
-## Note
-
-Integration with the [Layout Plugin](https://www.drupal.org/project/layout_plugin) module is supported only until
-version 1.0-beta6 included.
diff --git a/modules/ui_patterns_layouts/src/Plugin/Layout/PatternLayout.php b/modules/ui_patterns_layouts/src/Plugin/Layout/PatternLayout.php
index 12ce3b14..9d1d16e3 100644
--- a/modules/ui_patterns_layouts/src/Plugin/Layout/PatternLayout.php
+++ b/modules/ui_patterns_layouts/src/Plugin/Layout/PatternLayout.php
@@ -4,12 +4,13 @@
 
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformState;
 use Drupal\Core\Layout\LayoutDefault;
 use Drupal\Core\Layout\LayoutDefinition;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Plugin\PluginFormInterface;
-use Drupal\ui_patterns\UiPatternsManager;
 use Drupal\Core\Render\ElementInfoManagerInterface;
+use Drupal\ui_patterns\Form\UiPatternsFormBuilderTrait;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -19,6 +20,8 @@
  */
 class PatternLayout extends LayoutDefault implements PluginFormInterface, ContainerFactoryPluginInterface {
 
+  use UiPatternsFormBuilderTrait;
+
   /**
    * Module Handler.
    *
@@ -26,13 +29,6 @@ class PatternLayout extends LayoutDefault implements PluginFormInterface, Contai
    */
   protected $moduleHandler = NULL;
 
-  /**
-   * Pattern manager service.
-   *
-   * @var \Drupal\ui_patterns\UiPatternsManager
-   */
-  protected $patternManager = NULL;
-
   /**
    * The element info.
    *
@@ -51,15 +47,12 @@ class PatternLayout extends LayoutDefault implements PluginFormInterface, Contai
    *   The plugin implementation definition.
    * @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info
    *   Element info object.
-   * @param \Drupal\ui_patterns\UiPatternsManager $pattern_manager
-   *   Pattern manager service.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   Module handler.
    */
-  public function __construct(array $configuration, $plugin_id, LayoutDefinition $plugin_definition, ElementInfoManagerInterface $element_info, UiPatternsManager $pattern_manager, ModuleHandlerInterface $module_handler) {
+  public function __construct(array $configuration, $plugin_id, LayoutDefinition $plugin_definition, ElementInfoManagerInterface $element_info, ModuleHandlerInterface $module_handler) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
     $this->elementInfo = $element_info;
-    $this->patternManager = $pattern_manager;
     $this->moduleHandler = $module_handler;
   }
 
@@ -72,7 +65,6 @@ public static function create(ContainerInterface $container, array $configuratio
       $plugin_id,
       $plugin_definition,
       $container->get('plugin.manager.element_info'),
-      $container->get('plugin.manager.ui_patterns'),
       $container->get('module_handler')
     );
   }
@@ -83,23 +75,19 @@ public static function create(ContainerInterface $container, array $configuratio
   public function build(array $regions) {
     $configuration = $this->getConfiguration();
 
-    // Remove default field template if "Only content" option has been selected.
-    if ($configuration['pattern']['field_templates'] == 'only_content') {
-      $this->processOnlyContentFields($regions);
+    // Components expect slots to be passed along in a render array fashion.
+    $slots = [];
+    foreach (array_keys($regions) as $region_name) {
+      $slots[$region_name] = $regions[$region_name];
     }
-
-    // Patterns expect regions to be passed along in a render array fashion.
-    $fields = [];
-    foreach ($regions as $region_name => $region) {
-      $fields[$region_name] = $regions[$region_name];
-    }
-
+    $props_configuration = $configuration['pattern']['settings'] ?? [];
     return [
-      '#type' => 'pattern',
-      '#id' => $this->getPluginDefinition()->get('additional')['pattern'],
-      '#fields' => $fields,
+      '#type' => 'component',
+      '#component' => $this->getPluginDefinition()->id(),
+      '#slots' => $slots,
+      '#props_configuration' => $props_configuration,
       '#variant' => $configuration['pattern']['variant'],
-    ] + $this->elementInfo->getInfo('pattern');
+    ];
   }
 
   /**
@@ -118,40 +106,22 @@ public function defaultConfiguration() {
    * {@inheritdoc}
    */
   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
     $configuration = $this->getConfiguration();
-    $form = [];
-
-    $form['pattern'] = [
+    $form['ui_patterns'] = [
       '#group' => 'additional_settings',
-      '#type' => 'details',
-      '#title' => $this->t('Pattern settings'),
+      '#type' => 'container',
+      '#title' => $this->t('Configuration'),
       '#tree' => TRUE,
     ];
-    $form['pattern']['field_templates'] = [
-      '#type' => 'select',
-      '#title' => $this->t('Field templates'),
-      '#options' => [
-        'default' => $this->t("Default"),
-        'only_content' => $this->t("Only content"),
-      ],
-      '#description' => implode('<br/>', [
-        $this->t("<b>Default</b>: use field templates to wrap field content."),
-        $this->t("<b>Only content</b>: only print field content, without field wrapping or label."),
-      ]),
-      '#default_value' => $configuration['pattern']['field_templates'],
-    ];
-
-    $pattern_id = $this->getPluginDefinition()->get('additional')['pattern'];
-    $definition = $this->patternManager->getDefinition($pattern_id);
-    if ($definition->hasVariants()) {
-      $form['pattern']['variant'] = [
-        '#type' => 'select',
-        '#title' => $this->t('Variant'),
-        '#options' => $definition->getVariantsAsOptions(),
-        '#default_value' => $configuration['pattern']['variant'],
-      ];
-    }
-    $this->moduleHandler->alter('ui_patterns_layouts_display_settings_form', $form['pattern'], $definition, $configuration);
+    $component_id = $this->getPluginDefinition()->id();
+    /** @var \Drupal\sdc\ComponentPluginManager $plugin_manager */
+    $plugin_manager = \Drupal::service('plugin.manager.sdc');
+    /** @var \Drupal\sdc\Component\ComponentMetadata[] $components */
+    $component = $plugin_manager->find($component_id);
+    $context = ['form_type' => 'layout', 'form' => $form, 'layout' => $this, 'form_values' => $this->configuration['ui_patterns'] ?? []];
+    $form['ui_patterns'] = $this->buildComponentForm($form_state, $component, $context);
+    $this->moduleHandler->alter('ui_patterns_layouts_display_configuration_form', $form['ui_patterns'], $component, $configuration);
     return $form;
   }
 
@@ -165,25 +135,8 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form
    * {@inheritdoc}
    */
   public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
-    $this->configuration = $form_state->getValues();
-  }
-
-  /**
-   * Remove default field template if "Only content" option has been selected.
-   *
-   * @param array $regions
-   *   Layout regions.
-   */
-  protected function processOnlyContentFields(array &$regions) {
-    foreach ($regions as $region_name => $region) {
-      if (is_array($region)) {
-        foreach ($regions[$region_name] as $field_name => $field) {
-          if (is_array($field) && isset($field['#theme']) && $field['#theme'] == 'field') {
-            $regions[$region_name][$field_name]['#theme'] = NULL;
-          }
-        }
-      }
-    }
+    $sub_form_values = $this->submitComponentForm($form, $form_state, []);
+    $this->configuration['ui_patterns'] = $sub_form_values;
+    parent::submitConfigurationForm($form, $form_state);
   }
-
 }
diff --git a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/components/one-column/one-column.component.yml b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/components/one-column/one-column.component.yml
new file mode 100644
index 00000000..c1b8e228
--- /dev/null
+++ b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/components/one-column/one-column.component.yml
@@ -0,0 +1,22 @@
+$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
+name: One columns
+description: Widget Test with One Column
+libraryOverrides:
+  dependencies:
+    - core/drupal
+props:
+  type: object
+  properties:
+    heading:
+      title: Heading
+      description: The title for the banner text.
+      examples:
+        - Join us at The Conference
+      type: string
+slots:
+  body:
+    title: Body
+    description: The contents of the banner.
+    examples:
+      - <p>Foo is <strong>NOT</strong> bar.</p>
+
diff --git a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/components/one-column/one-column.twig b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/components/one-column/one-column.twig
new file mode 100644
index 00000000..27e50fff
--- /dev/null
+++ b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/components/one-column/one-column.twig
@@ -0,0 +1,4 @@
+<div>
+{% block body %}
+{% endblock %}
+</div>
diff --git a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/config/install/core.entity_view_display.node.article.default.yml b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/config/install/core.entity_view_display.node.article.default.yml
index 20cffe3b..d595c4eb 100644
--- a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/config/install/core.entity_view_display.node.article.default.yml
+++ b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/config/install/core.entity_view_display.node.article.default.yml
@@ -11,11 +11,9 @@ dependencies:
     - user
 third_party_settings:
   field_layout:
-    id: pattern_one_column
+    id: ui_patterns_layouts_test:one_column
     settings:
-      pattern:
-        field_templates: default
-        variant: default
+      pattern: {}
 id: node.article.default
 targetEntityType: node
 bundle: article
diff --git a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/templates/one_column.ui_patterns.yml b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/templates/one_column.ui_patterns.yml
deleted file mode 100644
index c838098a..00000000
--- a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/templates/one_column.ui_patterns.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-one_column:
-  label: "One column"
-  variants:
-    default:
-      label: "Default"
-    highlighted:
-      label: "Highlighted"
-  fields:
-    body:
-      type: "text"
-      label: "Body"
-      preview: "Body"
diff --git a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/templates/pattern-one-column.html.twig b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/templates/pattern-one-column.html.twig
deleted file mode 100644
index af9d618c..00000000
--- a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/templates/pattern-one-column.html.twig
+++ /dev/null
@@ -1,7 +0,0 @@
-{#
-/**
- * @file
- * "One column" layout pattern.
- */
-#}
-<article><p>{{ body }}</p></article>
\ No newline at end of file
diff --git a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/ui_patterns_layouts_test.info.yml b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/ui_patterns_layouts_test.info.yml
index 9f2d26c6..9362391d 100644
--- a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/ui_patterns_layouts_test.info.yml
+++ b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/ui_patterns_layouts_test.info.yml
@@ -1,21 +1,17 @@
 name: 'UI Patterns Layouts Test'
 type: module
 description: 'Test module for UI Patterns.'
-core: 8.x
-core_version_requirement: ^8 || ^9
-hidden: true
-package: 'User interface'
+package: 'Testing'
 dependencies:
-- ui_patterns
-- ui_patterns_layouts
-- ui_patterns_library
-- node
-- text
-- field_layout
-- field_ui
-
+  - drupal:field_layout
+  - drupal:field_ui
+  - drupal:node
+  - drupal:text
+  - ui_patterns:ui_patterns
+  - ui_patterns:ui_patterns_layouts
+  - drupal:sdc
 config_devel:
   install:
-  - field.field.node.article.body
-  - node.type.article
-  - core.entity_view_display.node.article.default
+    - field.field.node.article.body
+    - node.type.article
+    - core.entity_view_display.node.article.default
diff --git a/modules/ui_patterns_layouts/tests/src/FunctionalJavascript/UiPatternsLayoutsRenderTest.php b/modules/ui_patterns_layouts/tests/src/FunctionalJavascript/UiPatternsLayoutsRenderTest.php
deleted file mode 100644
index 5402a1b9..00000000
--- a/modules/ui_patterns_layouts/tests/src/FunctionalJavascript/UiPatternsLayoutsRenderTest.php
+++ /dev/null
@@ -1,92 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns_layout\FunctionalJavascript;
-
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-use Drupal\Tests\ui_patterns\Traits\TwigDebugTrait;
-
-/**
- * Test Layouts template rendering.
- *
- * @group ui_patterns_layouts
- */
-class UiPatternsLayoutsRenderTest extends WebDriverTestBase {
-
-  /**
-   * Default theme.
-   *
-   * @var string
-   */
-  protected $defaultTheme = 'stark';
-
-  /**
-   * Disable schema validation when running tests.
-   *
-   * @var bool
-   *
-   * @todo Fix this by providing actual schema validation.
-   */
-  protected $strictConfigSchema = FALSE;
-
-  use TwigDebugTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = [
-    'node',
-    'field',
-    'field_ui',
-    'field_layout',
-    'text',
-    'ui_patterns',
-    'ui_patterns_layouts',
-    'ui_patterns_layouts_test',
-    'ui_patterns_library',
-  ];
-
-  /**
-   * Tests field template settings.
-   */
-  public function testUiPatternsLayoutsRendering() {
-    $this->enableTwigDebugMode();
-    $this->drupalLogin($this->drupalCreateUser([], NULL, TRUE));
-
-    $node = $this->drupalCreateNode([
-      'title' => 'Test article',
-      'body' => 'Test body',
-      'type' => 'article',
-    ]);
-
-    $this->drupalGet($node->toUrl());
-
-    $assert_session = $this->assertSession();
-
-    // Assert correct variant suggestions.
-    $suggestions = [
-      'pattern-one-column--variant-default--layout--node--1.html.twig',
-      'pattern-one-column--variant-default--layout--node--article--full.html.twig',
-      'pattern-one-column--variant-default--layout--node--full.html.twig',
-      'pattern-one-column--variant-default--layout--node--article.html.twig',
-      'pattern-one-column--variant-default--layout--node.html.twig',
-      'pattern-one-column--variant-default--layout.html.twig',
-
-      'pattern-one-column--layout--node--1.html.twig',
-      'pattern-one-column--layout--node--article--full.html.twig',
-      'pattern-one-column--layout--node--full.html.twig',
-      'pattern-one-column--layout--node--article.html.twig',
-      'pattern-one-column--layout--node.html.twig',
-      'pattern-one-column--layout.html.twig',
-
-      'pattern-one-column--variant-default.html.twig',
-      'pattern-one-column.html.twig',
-    ];
-    foreach ($suggestions as $suggestion) {
-      $assert_session->responseContains($suggestion);
-    }
-
-    // Test content is rendered in the pattern.
-    $assert_session->elementContains('css', 'article', 'Test body');
-  }
-
-}
diff --git a/modules/ui_patterns_layouts/tests/src/FunctionalJavascript/UiPatternsLayoutsSettingsTest.php b/modules/ui_patterns_layouts/tests/src/FunctionalJavascript/UiPatternsLayoutsSettingsTest.php
deleted file mode 100644
index 1db3df03..00000000
--- a/modules/ui_patterns_layouts/tests/src/FunctionalJavascript/UiPatternsLayoutsSettingsTest.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns_layout\FunctionalJavascript;
-
-use Drupal\Core\Entity\Entity\EntityViewDisplay;
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-
-/**
- * Test Layouts template settings UI.
- *
- * @group ui_patterns_layouts
- */
-class UiPatternsLayoutsSettingsTest extends WebDriverTestBase {
-
-  /**
-   * Default theme.
-   *
-   * @var string
-   */
-  protected $defaultTheme = 'stark';
-
-  /**
-   * Disable schema validation when running tests.
-   *
-   * @var bool
-   *
-   * @todo Fix this by providing actual schema validation.
-   */
-  protected $strictConfigSchema = FALSE;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = [
-    'node',
-    'field',
-    'field_ui',
-    'field_layout',
-    'text',
-    'ui_patterns',
-    'ui_patterns_layouts',
-    'ui_patterns_layouts_test',
-    'ui_patterns_library',
-  ];
-
-  /**
-   * Tests field template settings.
-   */
-  public function testUiPatternsLayoutsSettings() {
-    $page = $this->getSession()->getPage();
-
-    $user = $this->drupalCreateUser([], NULL, TRUE);
-    $this->drupalLogin($user);
-
-    // Visit Article's default display settings page.
-    $this->drupalGet('/admin/structure/types/manage/article/display');
-
-    // Click on Pattern settings.
-    $page->pressButton('Layout settings');
-    $page->pressButton('Pattern settings');
-
-    // Select "Highlighted" field template.
-    $page->selectFieldOption('Variant', 'Highlighted');
-
-    $page->pressButton('Save');
-
-    // Get default view mode for Article node bundle.
-    $display = EntityViewDisplay::load("node.article.default");
-
-    // Assert existence of third party settings.
-    $third_party_settings = $display->getThirdPartySettings('field_layout');
-
-    // Assert settings value.
-    $this->assertEquals($third_party_settings['settings']['pattern']['variant'], 'highlighted');
-  }
-
-}
diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.api.php b/modules/ui_patterns_layouts/ui_patterns_layouts.api.php
deleted file mode 100644
index 455b718d..00000000
--- a/modules/ui_patterns_layouts/ui_patterns_layouts.api.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
- * @file
- * API file.
- */
-
-use Drupal\ui_patterns\Definition\PatternDefinition;
-
-/**
- * Alter pattern layout form under "Manage display".
- *
- * @param array $form
- *   Pattern settings fieldset.
- * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition
- *   Pattern definition.
- * @param array $configuration
- *   Pattern configuration.
- */
-function hook_ui_patterns_layouts_display_settings_form_alter(array &$form, PatternDefinition $definition, array $configuration) {
-  $form['element'] = ['#type' => 'input'];
-}
diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.info.yml b/modules/ui_patterns_layouts/ui_patterns_layouts.info.yml
index d0f06b18..198c9b49 100644
--- a/modules/ui_patterns_layouts/ui_patterns_layouts.info.yml
+++ b/modules/ui_patterns_layouts/ui_patterns_layouts.info.yml
@@ -1,9 +1,8 @@
-name: UI Patterns Layouts
+name: "UI Patterns Layouts"
 type: module
-description: Use patterns as layouts via the Layout Discovery module.
-core: 8.x
-core_version_requirement: ^8 || ^9
-package: User interface
+description: "Use UI components as layouts plugins."
+core_version_requirement: ^10
+package: "User interface"
 dependencies:
   - drupal:layout_discovery
   - ui_patterns:ui_patterns
diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.install b/modules/ui_patterns_layouts/ui_patterns_layouts.install
deleted file mode 100644
index e510cd34..00000000
--- a/modules/ui_patterns_layouts/ui_patterns_layouts.install
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains install file.
- */
-
-/**
- * Uninstall layout_plugin and install layout_discovery.
- */
-function ui_patterns_layouts_update_8101() {
-  /** @var \Drupal\Core\Extension\ModuleInstaller $installer */
-  $installer = \Drupal::service('module_installer');
-
-  if (\Drupal::moduleHandler()->moduleExists('layout_plugin')) {
-    $installer->uninstall(['layout_plugin']);
-  }
-
-  if (!\Drupal::moduleHandler()->moduleExists('layout_discovery')) {
-    $installer->install(['layout_discovery']);
-  }
-}
diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.module b/modules/ui_patterns_layouts/ui_patterns_layouts.module
index 9d5e2786..5ee63f76 100644
--- a/modules/ui_patterns_layouts/ui_patterns_layouts.module
+++ b/modules/ui_patterns_layouts/ui_patterns_layouts.module
@@ -5,129 +5,30 @@
  * Contains module file.
  */
 
-use Drupal\ui_patterns\Element\PatternContext;
 use Drupal\Core\Layout\LayoutDefinition;
-use Drupal\ui_patterns\UiPatterns;
-use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\field_layout\Display\EntityDisplayWithLayoutInterface;
 
 /**
  * Implements hook_layout_alter().
  */
 function ui_patterns_layouts_layout_alter(&$definitions) {
-  /** @var \Drupal\ui_patterns\Definition\PatternDefinition[] $pattern_definitions */
-
-  // @todo Use layout deriver instead.
-  // @link https://github.com/nuvoleweb/ui_patterns/issues/94
-  foreach (UiPatterns::getPatternDefinitions() as $pattern_definition) {
+  /** @var \Drupal\sdc\ComponentPluginManager $plugin_manager */
+  $plugin_manager = \Drupal::service('plugin.manager.sdc');
+  /** @var \Drupal\sdc\Component\ComponentMetadata[] $components */
+  $components = $plugin_manager->getDefinitions();
+  foreach ($components as $component) {
     $definition = [
-      'label' => $pattern_definition->getLabel(),
-      'theme' => $pattern_definition->getThemeHook(),
-      'provider' => $pattern_definition->getProvider(),
-      'category' => 'Patterns',
+      'label' => $component['name'] ?? $component['id'],
+      'category' => 'sdc',
+      'provider' => $component['provider'],
       'class' => '\Drupal\ui_patterns_layouts\Plugin\Layout\PatternLayout',
-      'pattern' => $pattern_definition->id(),
-      'template' => 'pattern-' . $pattern_definition->id(),
+      'id' => $component['id'],
     ];
-    foreach ($pattern_definition->getFields() as $field) {
-      $definition['regions'][$field->getName()]['label'] = $field->getLabel();
-    }
-    $definitions['pattern_' . $pattern_definition->id()] = new LayoutDefinition($definition);
-  }
-}
-
-/**
- * Implements hook_preprocess_HOOK().
- */
-function ui_patterns_layouts_preprocess_ds_entity_view(&$variables) {
-  if (isset($variables['content']['#type']) && $variables['content']['#type'] == 'pattern') {
-
-    /** @var \Drupal\Core\Entity\EntityInterface $entity */
-    $entity = $variables['content']['#entity'];
-
-    // Allow default context values to not override those exposed elsewhere.
-    $variables['content']['#context']['type'] = 'layout';
-    $variables['content']['#context']['entity_type'] = $variables['content']['#entity_type'];
-    $variables['content']['#context']['bundle'] = $variables['content']['#bundle'];
-    $variables['content']['#context']['view_mode'] = $variables['content']['#view_mode'];
-    $variables['content']['#context']['entity_id'] = $entity->id();
-    $variables['content']['#context']['entity'] = $entity;
-  }
-}
-
-/**
- * Implements hook_entity_view_alter().
- */
-function ui_patterns_layouts_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
-  if ($display instanceof EntityDisplayWithLayoutInterface && isset($build['_field_layout']['#type']) && $build['_field_layout']['#type'] == 'pattern') {
-    $build['_field_layout']['#context']['type'] = 'layout';
-    $build['_field_layout']['#context']['entity_type'] = $build['#entity_type'];
-    $build['_field_layout']['#context']['bundle'] = $entity->bundle();
-    $build['_field_layout']['#context']['view_mode'] = $build['#view_mode'];
-    $build['_field_layout']['#context']['entity_id'] = $entity->id();
-    $build['_field_layout']['#context']['entity'] = $entity;
-  }
-}
-
-/**
- * Implements hook_ui_patterns_suggestions_alter().
- */
-function ui_patterns_layouts_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) {
-  if ($context->isOfType('layout')) {
-    $hook = $variables['theme_hook_original'];
-    $variant = $variables["variant"] ?? '';
-    $entity_type = $context->getProperty('entity_type');
-    $bundle = $context->getProperty('bundle');
-    $view_mode = $context->getProperty('view_mode');
-    $entity_id = $context->getProperty('entity_id');
-
-    $suggestions[] = $hook . '__layout';
-    $suggestions[] = $hook . '__layout__' . $entity_type;
-    $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $bundle;
-    $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $view_mode;
-    $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $bundle . '__' . $view_mode;
-    $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $entity_id;
-
-    if (!empty($variant)) {
-      $suggestions[] = $hook . '__variant_' . $variant . '__layout';
-      $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type;
-      $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $bundle;
-      $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $view_mode;
-      $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $bundle . '__' . $view_mode;
-      $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $entity_id;
+    if (isset($component['slots'])) {
+      foreach ($component['slots'] as $slot_id => $slot) {
+        $definition['regions'][$slot_id]['label'] = $slot['title'];
+      }
     }
-  }
-}
 
-/**
- * Implements hook_ui_patterns_destination_suggestions_alter().
- */
-function ui_patterns_layouts_ui_patterns_destination_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) {
-  if ($context->isOfType('layout')) {
-    $hook = $variables['theme_hook_original'];
-    $variant = $variables["variant"] ?? '';
-    $entity_type = $context->getProperty('entity_type');
-    $bundle = $context->getProperty('bundle');
-    $view_mode = $context->getProperty('view_mode');
-    $entity_id = $context->getProperty('entity_id');
-    $pattern = $context->getProperty('pattern');
-    $field = $context->getProperty('field');
-
-    $suggestions[] = $hook . '__layout__' . $pattern . '__' . $field;
-    $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $pattern . '__' . $field;
-    $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $bundle . '__' . $pattern . '__' . $field;
-    $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $view_mode . '__' . $pattern . '__' . $field;
-    $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $bundle . '__' . $view_mode . '__' . $pattern . '__' . $field;
-    $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $entity_id . '__' . $pattern . '__' . $field;
-
-    if (!empty($variant)) {
-      $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $pattern . '__' . $field;
-      $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $pattern . '__' . $field;
-      $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $bundle . '__' . $pattern . '__' . $field;
-      $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $view_mode . '__' . $pattern . '__' . $field;
-      $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $bundle . '__' . $view_mode . '__' . $pattern . '__' . $field;
-      $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $entity_id . '__' . $pattern . '__' . $field;
-    }
+    $definitions[str_replace('-', '_', $component['id'])] = new LayoutDefinition($definition);
   }
 }
diff --git a/modules/ui_patterns_legacy/src/Element/Pattern.php b/modules/ui_patterns_legacy/src/Element/Pattern.php
new file mode 100644
index 00000000..5edf9a1c
--- /dev/null
+++ b/modules/ui_patterns_legacy/src/Element/Pattern.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Drupal\ui_patterns_legacy\Element;
+
+use Drupal\Core\Render\Element;
+use Drupal\ui_patterns\Element\ComponentElement;
+
+/**
+ * Renders a pattern element as a SDC element.
+ *
+ * @RenderElement("pattern")
+ */
+class Pattern extends ComponentElement {
+
+  const COMMON_RENDER_PROPERTIES = [
+    "#type",
+    "#id",
+    "#settings",
+    "#fields",
+    "#printed",
+    "#input",
+    "#pre_render",
+    "#cache",
+    "#context",
+    "#attached",
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo(): array {
+    return [
+      '#pre_render' => [
+        [$this, 'convert'],
+        [$this, 'preRenderComponent'],
+      ],
+      '#component' => '',
+      '#props' => [],
+      '#slots' => [],
+      '#propsAlter' => [],
+      '#slotsAlter' => [],
+    ];
+  }
+
+  /**
+   *
+   */
+  private static function resolveCompactFormat(array $element): array {
+    foreach (Element::properties($element) as $property) {
+      if (in_array($property, self::COMMON_RENDER_PROPERTIES)) {
+        continue;
+      }
+      // @todo test if slot or prop, looking the component definition.
+    }
+    return $element;
+  }
+
+  /**
+   *
+   */
+  public function convert(array $element): array {
+    $element = self::resolveCompactFormat($element);
+    $element["#type"] = "component";
+    if (array_key_exists("#id", $element) && is_string($element["#id"])) {
+      $element["#id"] = \Drupal::service('plugin.manager.sdc')->getNamespacedId($element["#id"]);
+      $element["#component"] = $element["#id"];
+      unset($element["#id"]);
+    }
+    if (array_key_exists("#fields", $element) && is_array($element["#fields"])) {
+      $element["#slots"] = $element["#fields"];
+      unset($element["#fields"]);
+    }
+    if (array_key_exists("#settings", $element) && is_array($element["#settings"])) {
+      $element["#props"] = $element["#settings"];
+      unset($element["#settings"]);
+    }
+    if (array_key_exists("#variant", $element) && is_string($element["#variant"])) {
+      $element["#props"]["variant"] = $element["#variant"];
+      unset($element["#variant"]);
+    }
+    // @todo Translate message
+    \Drupal::logger('ui_patterns_legacy')->warning("Deprecated pattern render element or pattern Twig function: " . $element["#component"]);
+    // @todo Remove before shipping
+    $messenger = \Drupal::service('messenger');
+    $messenger->addWarning("Deprecated pattern render element or pattern Twig function: " . $element["#component"]);
+    return $element;
+  }
+
+}
diff --git a/modules/ui_patterns_legacy/src/Element/PatternPreview.php b/modules/ui_patterns_legacy/src/Element/PatternPreview.php
new file mode 100644
index 00000000..e54bd290
--- /dev/null
+++ b/modules/ui_patterns_legacy/src/Element/PatternPreview.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Drupal\ui_patterns_legacy\Element;
+
+/**
+ * Renders a pattern preview element.
+ *
+ * @RenderElement("pattern_preview")
+ */
+class PatternPreview extends Pattern {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo(): array {
+    return [
+      '#pre_render' => [
+        [$this, 'convert'],
+        [$this, 'loadPreviewStory'],
+        [$this, 'preRenderComponent'],
+      ],
+      '#component' => '',
+      '#props' => [],
+      '#slots' => [],
+      '#propsAlter' => [],
+      '#slotsAlter' => [],
+    ];
+  }
+
+  /**
+   * Load preview.
+   *
+   * @param array $element
+   *   Render array.
+   *
+   * @return array
+   *   Render array.
+   */
+  public function loadPreviewStory(array $element): array {
+    $manager = \Drupal::service('plugin.manager.sdc');
+    $component = $manager->getDefinition($element["#component"]);
+    if (!isset($component["stories"])) {
+      return $element;
+    }
+    if (empty($component["stories"])) {
+      return $element;
+    }
+    $story_id = self::getStoryId($component["stories"]);
+    $story = $component["stories"][$story_id];
+    $slots = $story["slots"] ?? [];
+    $props = $story["props"] ?? [];
+    $slots = array_merge($element["#slots"], $slots);
+    $element["#slots"] = $manager::processStoriesSlots($slots);
+    $element["#props"] = array_merge($element["#props"], $props);
+    return $element;
+  }
+
+  /**
+   *
+   */
+  private function getStoryId(array $stories): string {
+    // In UI Patterns 1.x, there was only one story by component, called "preview".
+    if (array_key_exists("preview", $stories)) {
+      return "preview";
+    }
+    return array_key_first($stories);
+  }
+
+}
diff --git a/modules/ui_patterns_legacy/src/Plugin/Discovery/UIPatternsLegacyDiscovery.php b/modules/ui_patterns_legacy/src/Plugin/Discovery/UIPatternsLegacyDiscovery.php
new file mode 100644
index 00000000..162a8672
--- /dev/null
+++ b/modules/ui_patterns_legacy/src/Plugin/Discovery/UIPatternsLegacyDiscovery.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Drupal\ui_patterns_legacy\Plugin\Discovery;
+
+use Drupal\Component\Discovery\YamlDirectoryDiscovery;
+use Drupal\Core\File\FileSystemInterface;
+use Drupal\sdc\Plugin\Discovery\RegexRecursiveFilterIterator;
+
+/**
+ * Does the actual finding ui patterns files.
+ *
+ * @internal
+ */
+final class UIPatternsLegacyDiscovery extends YamlDirectoryDiscovery {
+
+  /**
+   * Constructs a DirectoryWithMetadataDiscovery object.
+   *
+   * @param array $directories
+   *   An array of directories to scan, keyed by the provider. The value can
+   *   either be a string or an array of strings. The string values should be
+   *   the path of a directory to scan.
+   * @param string $file_cache_key_suffix
+   *   The file cache key suffix. This should be unique for each type of
+   *   discovery.
+   * @param \Drupal\Core\File\FileSystemInterface $fileSystem
+   *   The file system service.
+   */
+  public function __construct(array $directories, string $file_cache_key_suffix, protected FileSystemInterface $fileSystem) {
+    parent::__construct($directories, $file_cache_key_suffix);
+  }
+
+  /**
+   * Gets an iterator to loop over the files in the provided directory.
+   *
+   * This method exists so that it is easy to replace this functionality in a
+   * class that extends this one. For example, it could be used to make the scan
+   * recursive.
+   *
+   * @param string $directory
+   *   The directory to scan.
+   *
+   * @return \RecursiveIteratorIterator
+   *   A \RecursiveIteratorIterator object or array where the values are
+   *   \SplFileInfo objects.
+   */
+  protected function getDirectoryIterator($directory): \RecursiveIteratorIterator {
+    // Use FilesystemIterator to not iterate over the . and .. directories.
+    $flags = \FilesystemIterator::KEY_AS_PATHNAME
+      | \FilesystemIterator::CURRENT_AS_FILEINFO
+      | \FilesystemIterator::SKIP_DOTS;
+    $directory_iterator = new \RecursiveDirectoryIterator($directory, $flags);
+    // Detect "my_component.component.yml".
+    $regex = '/^([a-z0-9_-])+.ui_patterns.yml$/i';
+    $filter = new RegexRecursiveFilterIterator($directory_iterator, $regex);
+    return new \RecursiveIteratorIterator($filter, \RecursiveIteratorIterator::LEAVES_ONLY, $flags);
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * The IDs can collide in two different scenarios:
+   *
+   * 1. Because one component is overriding another one via "weight".
+   * 2. Because the same component exists in different themes.
+   */
+  protected function getIdentifier($file, array $data): string {
+    $id = $this->fileSystem->basename($file, '.ui_patterns.yml');
+    $provider_paths = array_flip($this->directories);
+    $provider = $this->findProvider($file, $provider_paths);
+    // We use the provider to dedupe components because it does not make sense
+    // for a single provider to fork itself.
+    return sprintf('%s:%s', $provider, $id);
+  }
+
+  /**
+   * Finds the provider of the discovered file.
+   *
+   * The approach here is suboptimal because the provider is actually set in
+   * the plugin definition after the getIdentifier is called. So we either do
+   * this, or we forego the base class.
+   *
+   * @param string $file
+   *   The discovered file.
+   * @param array $provider_paths
+   *   The associative array of the path to the provider.
+   *
+   * @return string
+   *   The provider
+   */
+  private function findProvider(string $file, array $provider_paths): string {
+    $parts = explode(DIRECTORY_SEPARATOR, $file);
+    array_pop($parts);
+    if (empty($parts)) {
+      return '';
+    }
+    $provider = $provider_paths[implode(DIRECTORY_SEPARATOR, $parts)] ?? '';
+    return empty($provider)
+      ? $this->findProvider(implode(DIRECTORY_SEPARATOR, $parts), $provider_paths)
+      : $provider;
+  }
+
+}
diff --git a/modules/ui_patterns_legacy/src/Plugin/Discovery/UiPatternsLegacyPluginDiscovery.php b/modules/ui_patterns_legacy/src/Plugin/Discovery/UiPatternsLegacyPluginDiscovery.php
new file mode 100644
index 00000000..15c999d5
--- /dev/null
+++ b/modules/ui_patterns_legacy/src/Plugin/Discovery/UiPatternsLegacyPluginDiscovery.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\ui_patterns_legacy\Plugin\Discovery;
+
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Core\File\FileSystemInterface;
+use Drupal\Core\Plugin\Discovery\YamlDiscovery;
+use Drupal\Core\Plugin\Discovery\YamlDiscoveryDecorator;
+
+/**
+ * Discover directories that contain a specific metadata file.
+ *
+ * @internal
+ */
+final class UiPatternsLegacyPluginDiscovery extends YamlDiscovery {
+
+  /**
+   * Constructs a YamlDirectoryDiscovery object.
+   *
+   * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $decorated
+   *   The decorated origin SDC Discovery Service.
+   * @param array $directories
+   *   An array of directories to scan, keyed by the provider. The value can
+   *   either be a string or an array of strings. The string values should be
+   *   the path of a directory to scan.
+   * @param string $file_cache_key_suffix
+   *   The file cache key suffix. This should be unique for each type of
+   *   discovery.
+   * @param \Drupal\Core\File\FileSystemInterface $file_system
+   *   The file system service.
+   */
+  public function __construct(array $directories, $file_cache_key_suffix, FileSystemInterface $file_system) {
+    // Intentionally does not call parent constructor as this class uses a
+    // different YAML discovery.
+    parent::__construct('ui_patterns_legacy', $directories);
+    $this->discovery = new UIPatternsLegacyDiscovery($directories, $file_cache_key_suffix, $file_system);
+  }
+
+}
diff --git a/modules/ui_patterns_legacy/src/Template/TwigExtension.php b/modules/ui_patterns_legacy/src/Template/TwigExtension.php
new file mode 100644
index 00000000..8bd3d102
--- /dev/null
+++ b/modules/ui_patterns_legacy/src/Template/TwigExtension.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Drupal\ui_patterns_legacy\Template;
+
+use Twig\Extension\AbstractExtension;
+use Twig\TwigFunction;
+
+/**
+ * Twig extension providing UI Patterns-specific functionalities.
+ *
+ * @package Drupal\ui_patterns_legacy\Template
+ */
+class TwigExtension extends AbstractExtension {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName(): string {
+    return 'ui_patterns_legacy';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFunctions(): array {
+    return [
+      new TwigFunction('pattern', [
+        $this,
+        'renderPattern',
+      ]),
+      new TwigFunction('pattern_preview', [
+        $this,
+        'renderPatternPreview',
+      ]),
+    ];
+  }
+
+  /**
+   * Render given pattern.
+   *
+   * @param string $id
+   *   Pattern ID.
+   * @param array $fields
+   *   Pattern fields.
+   * @param string $variant
+   *   Variant name.
+   *
+   * @return array
+   *   Pattern render array.
+   *
+   * @see \Drupal\ui_patterns_legacy\Element\Pattern
+   */
+  public function renderPattern(string $id, array $fields = [], $variant = ""): array {
+    return [
+      '#type' => 'pattern',
+      '#id' => $id,
+      '#variant' => $variant,
+      '#fields' => $fields,
+    ];
+  }
+
+  /**
+   * Render given pattern.
+   *
+   * @param string $id
+   *   Pattern ID.
+   * @param string $variant
+   *   Variant name.
+   *
+   * @return array
+   *   Pattern render array.
+   *
+   * @see \Drupal\ui_patterns_legacy\Element\PatternPreview
+   */
+  public function renderPatternPreview(string $id, string $variant = ""): array {
+    return [
+      '#type' => 'pattern_preview',
+      '#id' => $id,
+      '#variant' => $variant,
+    ];
+  }
+
+}
diff --git a/modules/ui_patterns_legacy/src/UiPatternsLegacyManager.php b/modules/ui_patterns_legacy/src/UiPatternsLegacyManager.php
new file mode 100644
index 00000000..2d9510ef
--- /dev/null
+++ b/modules/ui_patterns_legacy/src/UiPatternsLegacyManager.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Drupal\ui_patterns_legacy;
+
+/**
+ * UiPatternsLegacyManager provides static helper functions.
+ */
+class UiPatternsLegacyManager {
+
+  /**
+   * Returns SDC component by UI Pattern id.
+   *
+   * @param $ui_pattern_id
+   *   The ui_pattern_id id
+   *
+   * @return \Drupal\sdc\Component\ComponentMetadata|void
+   *   The SDC.
+   */
+  public static function getComponentByUiPatternId($ui_pattern_id) {
+    /** @var \Drupal\sdc\ComponentPluginManager $plugin_manager */
+    $plugin_manager = \Drupal::service('plugin.manager.sdc');
+    /** @var \Drupal\sdc\Component\ComponentMetadata[] $components */
+    $components = $plugin_manager->getDefinitions();
+    foreach ($components as $component) {
+      if (isset($component['ui_pattern_id']) && $component['ui_pattern_id'] === $ui_pattern_id) {
+        return $component;
+      }
+    }
+  }
+
+}
diff --git a/modules/ui_patterns_legacy/src/UiPatternsLegacyPluginManager.php b/modules/ui_patterns_legacy/src/UiPatternsLegacyPluginManager.php
new file mode 100644
index 00000000..54c83624
--- /dev/null
+++ b/modules/ui_patterns_legacy/src/UiPatternsLegacyPluginManager.php
@@ -0,0 +1,143 @@
+<?php
+
+namespace Drupal\ui_patterns_legacy;
+
+use Drupal\ui_patterns\Sdc\ComponentPluginManagerDecorator;
+use Drupal\ui_patterns_legacy\Plugin\Discovery\UiPatternsLegacyPluginDiscovery;
+use Drupal\ui_patterns\Sdc\ComponentNegotiator;
+
+/**
+ * Plugin Manager for *.ui_patterns.yml configuration files.
+ *
+ * @see plugin_api
+ *
+ * @internal
+ */
+class UiPatternsLegacyPluginManager extends ComponentPluginManagerDecorator {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getCacheKey() {
+    return 'ui_patterns_legacy';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getScanDirectories(): array {
+    $extension_directories = [
+      ...$this->moduleHandler->getModuleDirectories(),
+      ...$this->themeHandler->getThemeDirectories(),
+    ];
+    return array_map(
+      static fn(string $path) => rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'patterns',
+      $extension_directories
+    );
+  }
+  /**
+   * {@inheritdoc}
+   */
+  protected function getDiscovery() {
+    if (!isset($this->discovery)) {
+      $directories = $this->getScanDirectories();
+      $this->discovery = new UiPatternsLegacyPluginDiscovery(
+        $directories,
+        'ui_patterns_legacy_sdc',
+        $this->fileSystem
+      );
+    }
+    return $this->discovery;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function isUiPatternFile($definition) {
+    return isset($definition['_discovered_file_path']) && str_ends_with(
+        $definition['_discovered_file_path'],
+        'ui_patterns.yml'
+      );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function mapPatternToComponent($pattern, $component) {
+    $component['props'] = [
+      'type' => 'object',
+      'properties' => [
+        'context' => [
+        ],
+      ],
+    ];
+    if (isset($pattern['fields'])) {
+      foreach ($pattern['fields'] as $field_id => $field) {
+        $component['slots'][$field_id] = [
+          'title' => $field['label'],
+          'description' => $field['description'] ?? NULL,
+          'examples' => $field['preview'] ?? NULL,
+        ];
+      }
+    }
+    return $component;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterDefinitions(&$definitions) {
+    foreach ($definitions as & $definition) {
+      $id = $definition['id'] ?? NULL;
+      if ($id) {
+        $definition_patterns_id = explode(':', $id)[1] ?? NULL;
+        if ($this->isUiPatternFile($definition)) {
+          $patterns = [];
+          // First detect UI Patterns definitions.
+          foreach ($definition as $key => $ui_pattern_definition) {
+            // Simple check for sub key label?
+            // Not sure if there is something better.
+            if (isset($ui_pattern_definition['label'])) {
+              $patterns[$key] = $definition[$key];
+              unset($definition[$key]);
+            }
+          }
+
+          // Components only accept one component for one file
+          // To support multiple components for one file we clone them.
+          foreach ($patterns as $pattern_id => $pattern) {
+            if ($definition_patterns_id !== $pattern_id) {
+              $cloned_definition = $definition;
+              $cloned_definition_id = $cloned_definition['provider'] . ':' . $pattern_id;
+              $cloned_definition['id'] = $cloned_definition_id;
+              $definitions[$cloned_definition_id] = $this->mapPatternToComponent(
+                $pattern,
+                $cloned_definition
+              );
+            }
+            else {
+              $definitions[$id] = $this->mapPatternToComponent(
+                $pattern,
+                $definition
+              );
+            }
+            $definitions[$id]['ui_pattern_id'] = $pattern_id;
+          }
+        }
+      }
+    }
+    parent::alterDefinitions($definitions);
+    foreach ($definitions as & $definition) {
+      $pattern_directory = dirname($definition['_discovered_file_path']);
+      $template = $this->findAsset(
+        $pattern_directory,
+        'pattern-' . $definition['machineName'],
+        'html.twig'
+      );
+      $definition['template'] = basename($template);
+    }
+
+  }
+
+
+}
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/alert/alert.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/alert/alert.ui_patterns.yml
new file mode 100644
index 00000000..31cde6a4
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/alert/alert.ui_patterns.yml
@@ -0,0 +1,41 @@
+alert:
+  label: "Alert (Legacy)"
+  description: "Provide contextual feedback messages for typical user actions with the handful of available and flexible alert messages."
+  links:
+    - "https://getbootstrap.com/docs/5.3/components/alerts/"
+  variants:
+    primary:
+      label: "Primary"
+    secondary:
+      label: "Secondary"
+    success:
+      label: "Success"
+    danger:
+      label: "Danger"
+    warning:
+      label: "Warning"
+    info:
+      label: "Info"
+    light:
+      label: "Light"
+    dark:
+      label: "Dark"
+  settings:
+    dismissible:
+      type: "boolean"
+      label: "Dismissible?"
+      description: "It is possible to dismiss any alert inline."
+      preview: True
+      allow_expose: true
+      allow_token: true
+  fields:
+    heading:
+      type: "text"
+      label: "Heading"
+      description: "The alert heading. Optional."
+      preview: "Well done!"
+    message:
+      type: "render"
+      label: "Message"
+      description: "The alert message."
+      preview: "A simple alert. Check it out!"
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/alert/pattern-alert.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/alert/pattern-alert.html.twig
new file mode 100644
index 00000000..e482d41d
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/alert/pattern-alert.html.twig
@@ -0,0 +1,22 @@
+{% if variant and variant|lower != 'default' %}
+  {% set attributes = attributes.addClass('alert-' ~ variant|lower|replace({'_': '-'})) %}
+{% endif %}
+
+{% if dismissible %}
+  {% set attributes = attributes.addClass(['alert-dismissible', 'fade', 'show']) %}
+{% endif %}
+
+<div{{ attributes.addClass('alert').setAttribute('role', 'alert') }}>
+  {% if heading %}
+  <h4 class="alert-heading">{{ heading }}</h4>
+  {% endif %}
+  {{ message|add_class('alert-link') }}
+  {% if dismissible %}
+    {{ pattern('close_button', {
+      attributes: create_attribute({
+        'data-bs-dismiss': 'alert'
+      }),
+      aria_label: 'Close'|t,
+    }) }}
+  {% endif %}
+</div>
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/blockquote/blockquote.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/blockquote/blockquote.ui_patterns.yml
new file mode 100644
index 00000000..70e3b835
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/blockquote/blockquote.ui_patterns.yml
@@ -0,0 +1,31 @@
+blockquote:
+  label: "Blockquote (Legacy)22"
+  description: "For quoting blocks of content from another source within your document."
+  links:
+    - "https://getbootstrap.com/docs/5.3/content/typography/#blockquotes"
+  category: "Typography"
+  fields:
+    content:
+      type: "render"
+      label: "Content"
+      description: "The quote."
+      preview:
+        - type: "html_tag"
+          tag: "p"
+          value: "A well-known quote, contained in a blockquote element."
+    footer:
+      type: "render"
+      label: "Footer"
+      description: "For identifying the source. Wrap the name of the source work in <cite>."
+      preview:
+        - type: "markup"
+          markup: "Someone famous in "
+        - type: "html_tag"
+          tag: "cite"
+          value: "Source Title"
+          attributes:
+            title: "Source Title"
+  settings:
+    dismissible:
+      type: "boolean"
+      label: "Dismissible?"
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/blockquote/pattern-blockquote.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/blockquote/pattern-blockquote.html.twig
new file mode 100644
index 00000000..5721b60a
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/blockquote/pattern-blockquote.html.twig
@@ -0,0 +1,14 @@
+{% if footer %}
+  <figure{{ attributes }}>
+    <blockquote class="blockquote">
+      {{ content }}
+    </blockquote>
+    <figcaption class="blockquote-footer">
+      {{ footer }}
+    </figcaption>
+  </figure>
+{% else %}
+  <blockquote{{ attributes.addClass('blockquote') }}>
+    {{ content }}
+  </blockquote>
+{% endif %}
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/button/button.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/button/button.ui_patterns.yml
new file mode 100644
index 00000000..0e27a7d0
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/button/button.ui_patterns.yml
@@ -0,0 +1,140 @@
+button:
+  label: "Button (Legacy)"
+  description: "For actions in forms, dialogs, and more with support for multiple sizes, states, and more."
+  links:
+    - "https://getbootstrap.com/docs/5.3/components/buttons/"
+  category: "Button"
+  variants:
+    default:
+      label: "Default"
+      description: "No 'btn' class added."
+    primary__sm:
+      label: "Primary small"
+    secondary__sm:
+      label: "Secondary small"
+    success__sm:
+      label: "Success small"
+    danger__sm:
+      label: "Danger small"
+    warning__sm:
+      label: "Warning small"
+    info__sm:
+      label: "Info small"
+    light__sm:
+      label: "Light small"
+    dark__sm:
+      label: "Dark small"
+    link__sm:
+      label: "Link small"
+    primary:
+      label: "Primary"
+    secondary:
+      label: "Secondary"
+    success:
+      label: "Success"
+    danger:
+      label: "Danger"
+    warning:
+      label: "Warning"
+    info:
+      label: "Info"
+    light:
+      label: "Light"
+    dark:
+      label: "Dark"
+    link:
+      label: "Link"
+    primary__lg:
+      label: "Primary large"
+    secondary__lg:
+      label: "Secondary large"
+    success__lg:
+      label: "Success large"
+    danger__lg:
+      label: "Danger large"
+    warning__lg:
+      label: "Warning large"
+    info__lg:
+      label: "Info large"
+    light__lg:
+      label: "Light large"
+    dark__lg:
+      label: "Dark large"
+    link__lg:
+      label: "Link large"
+    outline_primary__sm:
+      label: "Outline Primary small"
+    outline_secondary__sm:
+      label: "Outline Secondary small"
+    outline_success__sm:
+      label: "Outline Success small"
+    outline_danger__sm:
+      label: "Outline Danger small"
+    outline_warning__sm:
+      label: "Outline Warning small"
+    outline_info__sm:
+      label: "Outline Info small"
+    outline_light__sm:
+      label: "Outline Light small"
+    outline_dark__sm:
+      label: "Outline Dark small"
+    outline_primary:
+      label: "Outline Primary"
+    outline_secondary:
+      label: "Outline Secondary"
+    outline_success:
+      label: "Outline Success"
+    outline_danger:
+      label: "Outline Danger"
+    outline_warning:
+      label: "Outline Warning"
+    outline_info:
+      label: "Outline Info"
+    outline_light:
+      label: "Outline Light"
+    outline_dark:
+      label: "Outline Dark"
+    outline_primary__lg:
+      label: "Outline Primary large"
+    outline_secondary__lg:
+      label: "Outline Secondary large"
+    outline_success__lg:
+      label: "Outline Success large"
+    outline_danger__lg:
+      label: "Outline Danger large"
+    outline_warning__lg:
+      label: "Outline Warning large"
+    outline_info__lg:
+      label: "Outline Info large"
+    outline_light__lg:
+      label: "Outline Light large"
+    outline_dark__lg:
+      label: "Outline Dark large"
+    dropdown_item:
+      label: "(Dropdown item)"
+  settings:
+    disabled:
+      type: "boolean"
+      label: "Disabled?"
+      description: "Is the button disabled?"
+      preview: false
+      allow_expose: true
+      allow_token: true
+    label_visually_hidden:
+      type: "boolean"
+      label: "Hide button label?"
+      description: "Is the button's label hidden?"
+      preview: false
+      allow_expose: true
+      allow_token: true
+    url:
+      type: "url"
+      label: "URL"
+      description: "The button URL. Optional."
+      preview: "https://example.com"
+  fields:
+    label:
+      type: "text"
+      label: "Label"
+      description: "The button label."
+      preview: "Submit"
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/button/pattern-button.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/button/pattern-button.html.twig
new file mode 100644
index 00000000..5fc3ddb9
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/button/pattern-button.html.twig
@@ -0,0 +1,31 @@
+{% if variant and variant|lower != 'default' and variant|lower != 'dropdown_item' %}
+  {% set variants = variant|split('__')|map(v => v|lower|replace({(v): 'btn-' ~ v})|replace({'_': '-'})) %}
+  {% set attributes = attributes.addClass(variants) %}
+  {% set attributes = attributes.addClass('btn') %}
+{% endif %}
+
+{% set attributes = (variant|lower == 'dropdown_item') ? attributes.addClass('dropdown-item') : attributes %}
+
+{% if label_visually_hidden %}
+  {% set label %}
+    <span class="visually-hidden">
+      {{ label }}
+    </span>
+  {% endset %}
+{% endif %}
+
+{% if url or attributes.href %}
+  {% set url = url|default(attributes.href) %}
+  {% set attributes = attributes.setAttribute('href', url) %}
+  {% if disabled %}
+    {% set attributes = attributes.setAttribute('href', false).setAttribute('tabindex', '-1').setAttribute('aria-disabled', 'true').addClass('disabled') %}
+  {% endif %}
+
+  <a{{ attributes.setAttribute('role', 'button') }}>{{ label }}</a>
+{% else %}
+  {% if disabled %}
+    {% set attributes = attributes.setAttribute('disabled', '') %}
+  {% endif %}
+
+  <button{{ attributes.setAttribute('type', 'button') }}>{{ label }}</button>
+{% endif %}
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/card.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/card.ui_patterns.yml
new file mode 100644
index 00000000..d5138f6a
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/card.ui_patterns.yml
@@ -0,0 +1,89 @@
+card:
+  label: "Card (Legacy)"
+  description: "A card is a flexible and extensible content container. It includes options for headers and footers, a wide variety of content, contextual background colors, and powerful display options."
+  links:
+    - "https://getbootstrap.com/docs/5.3/components/card/"
+  category: "Card"
+  variants:
+    default:
+      label: "Default"
+    horizontal:
+      label: "Horizontal"
+  settings:
+    image_position:
+      type: "select"
+      label: "Image position"
+      description: "Only for default variant."
+      options:
+        top: "Top (Default)"
+        bottom: "Bottom"
+      preview: "top"
+      allow_expose: true
+      allow_token: true
+    image_col_classes:
+      type: "textfield"
+      label: "Image column classes"
+      description: "Only for horizontal variant. Default value: col-md-4"
+      default_value: "col-md-4"
+      preview: "col-md-4"
+      allow_expose: true
+      allow_token: true
+    content_col_classes:
+      type: "textfield"
+      label: "Content column classes"
+      description: "Only for horizontal variant. Default value: col-md-8"
+      default_value: "col-md-8"
+      preview: "col-md-8"
+      allow_expose: true
+      allow_token: true
+  fields:
+    image:
+      type: "render"
+      label: "Image"
+      description: "Card image."
+      preview:
+        theme: "image"
+        uri: "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZm9udC1zaXplOiAxLjEyNXJlbTsgdGV4dC1hbmNob3I6IG1pZGRsZTsgdXNlci1zZWxlY3Q6IG5vbmU7IiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIyNTQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgc2xpY2UiIGZvY3VzYWJsZT0iZmFsc2UiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iUGxhY2Vob2xkZXI6IEltYWdlIGNhcCI+PHRpdGxlPlBsYWNlaG9sZGVyPC90aXRsZT48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjODY4ZTk2Ij48L3JlY3Q+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZpbGw9IiNkZWUyZTYiIGR5PSIuM2VtIj5JbWFnZSBjYXA8L3RleHQ+PC9zdmc+Cg=="
+        alt: "&copy; 2017 John Smith photography"
+    header:
+      type: "render"
+      label: "Header"
+      description: "Card header."
+      preview: "Featured"
+    content:
+      type: "render"
+      label: "Content"
+      description: "Card body."
+      preview:
+        - type: "pattern"
+          id: "card_body"
+          variant: "body"
+          fields:
+            title: "Card title"
+            subtitle: "Card subtitle"
+            text: "Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit."
+            content:
+              type: "pattern"
+              id: "button"
+              variant: "primary"
+              fields:
+                label: "Go somewhere"
+            links:
+              - type: "html_tag"
+                tag: "a"
+                value: "Card link"
+                attributes:
+                  href: "#"
+              - type: "html_tag"
+                tag: "a"
+                value: "Another link"
+                attributes:
+                  href: "#"
+    footer:
+      type: "render"
+      label: "Footer"
+      description: "Card footer."
+      preview:
+        type: "html_tag"
+        tag: "span"
+        value: "2 days ago"
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card--variant-horizontal--preview.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card--variant-horizontal--preview.html.twig
new file mode 100644
index 00000000..05e46072
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card--variant-horizontal--preview.html.twig
@@ -0,0 +1,4 @@
+{% set header = '' %}
+{% set footer = '' %}
+{% set attributes = attributes.setAttribute('style', 'max-width: 540px;') %}
+{% extends 'pattern-card--variant-horizontal.html.twig' %}
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card--variant-horizontal.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card--variant-horizontal.html.twig
new file mode 100644
index 00000000..9e579b35
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card--variant-horizontal.html.twig
@@ -0,0 +1,20 @@
+<div{{ attributes.addClass('card') }}>
+  <div class="row g-0">
+    <div class="{{ image_col_classes|default('col-md-4') }}">
+      {{ image|add_class('img-fluid rounded-start') }}
+    </div>
+    <div class="{{ content_col_classes|default('col-md-8') }}">
+      {% if header %}
+      <div class="card-header">
+        {{ header }}
+      </div>
+      {% endif %}
+      {{ content }}
+      {% if footer %}
+      <div class="card-footer">
+        {{ footer }}
+      </div>
+      {% endif %}
+    </div>
+  </div>
+</div>
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card.html.twig
new file mode 100644
index 00000000..e7e33a55
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card.html.twig
@@ -0,0 +1,19 @@
+<div{{ attributes.addClass('card') }}>
+  {% if image and image_position != 'bottom' %}
+    {{ image|add_class('card-img-top') }}
+  {% endif %}
+  {% if header %}
+  <div class="card-header">
+    {{ header }}
+  </div>
+  {% endif %}
+  {{ content }}
+  {% if footer %}
+  <div class="card-footer">
+    {{ footer }}
+  </div>
+  {% endif %}
+  {% if image and image_position == 'bottom' %}
+    {{ image|add_class('card-img-bottom') }}
+  {% endif %}
+</div>
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/card_body.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/card_body.ui_patterns.yml
new file mode 100644
index 00000000..ef737a0b
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/card_body.ui_patterns.yml
@@ -0,0 +1,60 @@
+card_body:
+  label: "(Card body) (Legacy)"
+  description: "Internal: to be used in the 'Card' component."
+  links:
+    - "https://getbootstrap.com/docs/5.3/components/card/"
+  category: "Card"
+  settings:
+    heading_level:
+      type: "select"
+      label: "Heading level"
+      options:
+        2: "h2"
+        3: "h3"
+        4: "h4"
+        5: "h5 (Default)"
+        6: "h6"
+      preview: 5
+      allow_expose: true
+      allow_token: true
+  fields:
+    title:
+      type: "text"
+      label: "Title"
+      description: "Card title. Plain text."
+      preview: "Card title"
+    subtitle:
+      type: "text"
+      label: "Subtitle"
+      description: "Card subtitle. Plain text."
+      preview: "Card subtitle"
+    text:
+      type: "text"
+      label: "Text"
+      description: "Card text. Plain text."
+      preview: "Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit."
+    content:
+      type: "render"
+      label: "Content"
+      description: "Free content outside of any wrapper."
+      preview:
+        type: "pattern"
+        id: "button"
+        variant: "primary"
+        fields:
+          label: "Go somewhere"
+    links:
+      type: "render"
+      label: "Links"
+      description: "Array of link elements"
+      preview:
+        - type: "html_tag"
+          tag: "a"
+          value: "Card link"
+          attributes:
+            href: "#"
+        - type: "html_tag"
+          tag: "a"
+          value: "Another link"
+          attributes:
+            href: "#"
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/css/library_one.css b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/pattern-card-body--preview.html.twig
similarity index 100%
rename from modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/css/library_one.css
rename to modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/pattern-card-body--preview.html.twig
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/pattern-card-body.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/pattern-card-body.html.twig
new file mode 100644
index 00000000..78908777
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/pattern-card-body.html.twig
@@ -0,0 +1,15 @@
+{% set heading_level = heading_level|default(5) %}
+
+<div{{ attributes.addClass('card-body') }}>
+  {% if title %}
+    <h{{ heading_level }} class="card-title">{{ title }}</h{{ heading_level }}>
+  {% endif %}
+  {% if subtitle %}
+    <h{{ heading_level + 1 }} class="card-subtitle">{{ subtitle }}</h{{ heading_level + 1 }}>
+  {% endif %}
+  {% if text %}
+    <p class="card-text">{{ text }}</p>
+  {% endif %}
+  {{ content }}
+  {{ links|add_class('card-link') }}
+</div>
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/close_button.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/close_button.ui_patterns.yml
new file mode 100644
index 00000000..cf10343f
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/close_button.ui_patterns.yml
@@ -0,0 +1,25 @@
+close_button:
+  label: "Close button (Legacy)"
+  description: "A generic close button for dismissing content like modals and alerts."
+  links:
+    - "https://getbootstrap.com/docs/5.3/components/close-button"
+  category: "Button"
+  variants:
+    default:
+      label: "Default"
+    white:
+      label: "White (deprecated)"
+  settings:
+    disabled:
+      type: "boolean"
+      label: "Disabled?"
+      description: "Is the button disabled?"
+      preview: false
+      allow_expose: true
+      allow_token: true
+    aria_label:
+      type: "textfield"
+      label: "Aria label"
+      description: "Name of the close button for assistive technology."
+      preview: "Close"
+      allow_token: true
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/css/library_two.css b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/pattern-close-button--variant-white--preview.html.twig
similarity index 100%
rename from modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/css/library_two.css
rename to modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/pattern-close-button--variant-white--preview.html.twig
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/js/library_two_1.js b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/pattern-close-button.html.twig
similarity index 100%
rename from modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/js/library_two_1.js
rename to modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/pattern-close-button.html.twig
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/figure/figure.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/figure/figure.ui_patterns.yml
new file mode 100644
index 00000000..27881a25
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/figure/figure.ui_patterns.yml
@@ -0,0 +1,26 @@
+figure:
+  label: "Figure (Legacy)"
+  description: "Used to display a piece of self-contained content (illustrations, diagrams, photos, code, etc) along with an optional caption. This content can be removed from the document without affecting the meaning of the document."
+  links:
+    - "https://getbootstrap.com/docs/5.3/content/figures/"
+  settings:
+    figcaption_attributes:
+      type: "attributes"
+      label: "Figcaption attributes"
+      description: "The attributes to customize the figcaption tag."
+      preview: 'class="text-end"'
+      allow_expose: true
+  fields:
+    image:
+      type: "render"
+      label: "Image"
+      description: "The content of the figure."
+      preview:
+        theme: "image"
+        uri: "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZm9udC1zaXplOiAxLjEyNXJlbTsgdGV4dC1hbmNob3I6IG1pZGRsZTsgdXNlci1zZWxlY3Q6IG5vbmU7IiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIyNTQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgc2xpY2UiIGZvY3VzYWJsZT0iZmFsc2UiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iUGxhY2Vob2xkZXI6IEltYWdlIGNhcCI+PHRpdGxlPlBsYWNlaG9sZGVyPC90aXRsZT48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjODY4ZTk2Ij48L3JlY3Q+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZpbGw9IiNkZWUyZTYiIGR5PSIuM2VtIj5JbWFnZSBjYXA8L3RleHQ+PC9zdmc+Cg=="
+        alt: "&copy; 2017 John Smith photography"
+    caption:
+      type: "text"
+      label: "Caption"
+      description: "The caption that appears under the content."
+      preview: "A caption for the above image."
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/figure/pattern-figure.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/figure/pattern-figure.html.twig
new file mode 100644
index 00000000..21dd87df
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/figure/pattern-figure.html.twig
@@ -0,0 +1,6 @@
+<figure{{ attributes.addClass('figure') }}>
+  {{ image|add_class('figure-img') }}
+  <figcaption{{ figcaption_attributes.addClass('figure-caption') }}>
+    {{ caption }}
+  </figcaption>
+</figure>
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/progress/pattern-progress.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/progress/pattern-progress.html.twig
new file mode 100644
index 00000000..b7bc09f6
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/progress/pattern-progress.html.twig
@@ -0,0 +1,31 @@
+{% if variant and variant|lower != 'default' %}
+  {% set variants = variant|split('__')|map(v => v|lower|replace({(v): 'progress-bar-' ~ v})|replace({'_': '-'})) %}
+  {% set attributes = attributes.addClass(variants) %}
+{% endif %}
+
+{% set wrapper_attributes = create_attribute() %}
+{# Handle wrapper ID. #}
+{% if attributes.hasAttribute('id') %}
+  {% set wrapper_attributes = wrapper_attributes.setAttribute('id', attributes.offsetGet('id')) %}
+  {% set attributes = attributes.removeAttribute('id') %}
+{% endif %}
+
+{% set wrapper_attributes = bar_height ? wrapper_attributes.setAttribute('style', 'height:' ~ bar_height ~ 'px') : wrapper_attributes %}
+{% set percent = percent|default(0) %}
+{% set min = min|default(0) %}
+{% set max = max|default(100) %}
+{% set width = (percent * 100) // max %}
+
+<div{{ wrapper_attributes.addClass('progress')}}>
+  <div{{ attributes
+      .addClass('progress-bar')
+      .setAttribute('role', 'progressbar')
+      .setAttribute('aria-label', aria_label|default('Progress bar'|t))
+      .setAttribute('aria-valuenow', percent)
+      .setAttribute('aria-valuemin', min)
+      .setAttribute('aria-valuemax', max)
+      .setAttribute('style', 'width: ' ~ width ~ '%')
+    }}>
+    {{ label }}
+  </div>
+</div>
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/progress/progress.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/progress/progress.ui_patterns.yml
new file mode 100644
index 00000000..fe06ac6f
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/progress/progress.ui_patterns.yml
@@ -0,0 +1,47 @@
+progress:
+  label: "Progress (Legacy)"
+  description: "The progress element displays an indicator showing the completion progress of a task, typically in the form of a bar. Progress components are built with two HTML elements, some CSS to set the width, and a few attributes. Bootstrap does not use the HTML5 <progress> element, ensuring you can stack progress bars, animate them, and place text labels over them."
+  links:
+    - "https://getbootstrap.com/docs/5.3/components/progress/"
+  variants:
+    default:
+      label: "Default"
+    striped:
+      label: "Striped"
+    striped__animated:
+      label: "Animated stripes"
+  settings:
+    aria_label:
+      type: "textfield"
+      label: "Aria label"
+      description: "Name of the progress bar for assistive technology."
+      allow_token: true
+    percent:
+      type: "number"
+      label: "Total progress (%)"
+      description: "Width of the progress element representing total progress (25%, 50%, etc.)."
+      allow_token: true
+      preview: 50
+    min:
+      type: "number"
+      label: "Minimum value"
+      description: "Minimum value of the progress element (default is 0). Used for an aria attribute."
+      allow_token: true
+      preview: 0
+    max:
+      type: "number"
+      label: "Maximum value"
+      description: "Maximum value of the progress element (default is 100). Used for an aria attribute."
+      allow_token: true
+      preview: 100
+    bar_height:
+      type: "number"
+      label: "Height"
+      description: "Height of progress element in pixels (px). Leave empty for default height."
+      allow_token: true
+  fields:
+    label:
+      type: "text"
+      label: "Label"
+      description: "Text shown inside the progress bar."
+      preview: "Label"
diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/ui_patterns_legacy_test.info.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/ui_patterns_legacy_test.info.yml
new file mode 100644
index 00000000..e06299d8
--- /dev/null
+++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/ui_patterns_legacy_test.info.yml
@@ -0,0 +1,6 @@
+name: "UI Patterns Legacy Test"
+type: module
+description: "Provides test plugin."
+package: "Testing"
+dependencies:
+  - ui_patterns_legacy
diff --git a/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml b/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml
new file mode 100644
index 00000000..6221865c
--- /dev/null
+++ b/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml
@@ -0,0 +1,7 @@
+name: "UI Patterns Legacy"
+type: module
+description: "Compatibility layer for UI Patterns 1.x components."
+core_version_requirement: ^10
+package: "User interface"
+dependencies:
+  - ui_patterns:ui_patterns
diff --git a/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml b/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml
new file mode 100644
index 00000000..37c9e11f
--- /dev/null
+++ b/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml
@@ -0,0 +1,26 @@
+parameters:
+  ui_patterns_legacy.file_extensions:
+    - ".component.yml"
+services:
+  ui_patterns_legacy.twig.extension:
+    class: Drupal\ui_patterns_legacy\Template\TwigExtension
+    tags:
+      - { name: twig.extension }
+#  ui_patterns_legacy.plugin.manager.sdc.decorator:
+#    class: Drupal\ui_patterns_legacy\UiPatternsLegacyPluginManager
+#    decorates: plugin.manager.sdc
+#    decoration_priority: 9
+#    public: false
+#    arguments:
+#      - '@ui_patterns_legacy.plugin.manager.sdc.decorator.inner'
+#      - '@plugin.manager.ui_patterns_prop_type'
+#      - '@module_handler'
+#      - '@theme_handler'
+#      - '@cache.discovery'
+#      - '@config.factory'
+#      - '@theme.manager'
+#      - '@Drupal\sdc\ComponentNegotiator'
+#      - '@file_system'
+#      - '@Drupal\sdc\Component\SchemaCompatibilityChecker'
+#      - '@Drupal\sdc\Component\ComponentValidator'
+#      - '%app.root%'
diff --git a/modules/ui_patterns_library/README.md b/modules/ui_patterns_library/README.md
deleted file mode 100644
index 3fa42511..00000000
--- a/modules/ui_patterns_library/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# UI Patterns Library
-
-The UI Patterns Library module allows developers to expose patterns via YAML definitions and to display them via a
-pattern library page to be used as documentation for content editors or as a showcase for business, available at `/patterns`.
-
-For more information please refer to the [official documentation](http://ui-patterns.readthedocs.io).
diff --git a/modules/ui_patterns_library/src/Controller/LibraryController.php b/modules/ui_patterns_library/src/Controller/LibraryController.php
new file mode 100644
index 00000000..5a32066a
--- /dev/null
+++ b/modules/ui_patterns_library/src/Controller/LibraryController.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Drupal\ui_patterns_library\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\sdc\ComponentPluginManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class LibraryController.
+ *
+ * @package Drupal\ui_patterns_library\Controller
+ */
+class LibraryController extends ControllerBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(protected ComponentPluginManager $componentPluginManager) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.sdc')
+    );
+  }
+
+  /**
+   * Title callback.
+   *
+   * @return string
+   *   Pattern label.
+   */
+  public function title($name) {
+    $definition = $this->componentPluginManager->getDefinition($name);
+    return $definition["name"];
+  }
+
+  /**
+   * Render a single component page.
+   *
+   * @param string $name
+   *   Plugin ID.
+   *
+   * @return array
+   *   Return render array.
+   */
+  public function single($name) {
+    $definition = $this->componentPluginManager->getDefinition($name);
+    return [
+      '#theme' => 'ui_patterns_single_page',
+      '#component' => $definition,
+    ];
+  }
+
+  /**
+   * Render the components overview page.
+   *
+   * @return array
+   *   Patterns overview page render array.
+   */
+  public function overview() {
+    $groups = $this->componentPluginManager->getGroupedDefinitions();
+    return [
+      '#theme' => 'ui_patterns_overview_page',
+      '#groups' => $groups,
+    ];
+  }
+
+}
diff --git a/modules/ui_patterns_library/src/Controller/PatternsLibraryController.php b/modules/ui_patterns_library/src/Controller/PatternsLibraryController.php
deleted file mode 100644
index e6494cb9..00000000
--- a/modules/ui_patterns_library/src/Controller/PatternsLibraryController.php
+++ /dev/null
@@ -1,150 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_library\Controller;
-
-use Drupal\Core\Controller\ControllerBase;
-use Drupal\ui_patterns\Definition\PatternDefinition;
-use Drupal\ui_patterns\UiPatternsManager;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Class PatternLibraryController.
- *
- * @package Drupal\ui_patterns\Controller
- */
-class PatternsLibraryController extends ControllerBase {
-
-  /**
-   * Patterns manager service.
-   *
-   * @var \Drupal\ui_patterns\UiPatternsManager
-   */
-  protected $patternsManager;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(UiPatternsManager $ui_patterns_manager) {
-    $this->patternsManager = $ui_patterns_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static($container->get('plugin.manager.ui_patterns'));
-  }
-
-  /**
-   * Title callback.
-   *
-   * @return string
-   *   Pattern label.
-   */
-  public function title($name) {
-    return $this->patternsManager->getDefinition($name)->getLabel();
-  }
-
-  /**
-   * Render pattern library page.
-   *
-   * @param string $name
-   *   Plugin ID.
-   *
-   * @return array
-   *   Return render array.
-   */
-  public function single($name) {
-    $definition = $this->patternsManager->getDefinition($name);
-
-    return [
-      '#theme' => 'patterns_single_page',
-      '#pattern' => [
-        'meta' => [
-          '#theme' => 'patterns_meta_information',
-          '#pattern' => $definition->toArray(),
-        ],
-        'rendered' => $this->getPatternRenderArray($definition),
-        'definition' => $definition->toArray(),
-      ],
-    ];
-  }
-
-  /**
-   * Render pattern library page.
-   *
-   * @return array
-   *   Patterns overview page render array.
-   */
-  public function overview() {
-
-    $patterns = [];
-    foreach ($this->patternsManager->getDefinitions() as $definition) {
-      $patterns[$definition->id()] = $definition->toArray() + [
-        'meta' => [
-          '#theme' => 'patterns_meta_information',
-          '#pattern' => $definition->toArray(),
-        ],
-        'rendered' => $this->getPatternRenderArray($definition),
-        'definition' => $definition->toArray(),
-      ];
-    }
-
-    return [
-      '#theme' => 'patterns_overview_page',
-      '#patterns' => $patterns,
-    ];
-  }
-
-  /**
-   * Get pattern preview render array, handling variants.
-   *
-   * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition
-   *   Pattern definition object.
-   *
-   * @return array
-   *   Render array.
-   */
-  protected function getPatternRenderArray(PatternDefinition $definition) {
-    $render = [];
-
-    // If pattern has variants then render them all adding meta information
-    // on top of each one, or simply render pattern preview otherwise.
-    if ($definition->hasVariants()) {
-      foreach ($definition->getVariants() as $variant) {
-        $render[$definition->id() . '_' . $variant->getName()] = [
-          'meta' => [
-            '#theme' => 'patterns_variant_meta_information',
-            '#variant' => $variant->toArray(),
-          ],
-          'pattern' => [
-            '#type' => 'pattern_preview',
-            '#id' => $definition->id(),
-            '#variant' => $variant->getName(),
-            '#theme_wrappers' => [
-              'container' => [
-                '#attributes' => ['class' => 'pattern-preview__markup pattern-preview__markup--variant_' . $variant->getName()],
-              ],
-            ],
-          ],
-        ];
-      }
-    }
-    else {
-      $render[$definition->id()] = [
-        'pattern' => [
-          '#type' => 'pattern_preview',
-          '#id' => $definition->id(),
-          '#theme_wrappers' => [
-            'container' => [
-              '#attributes' => ['class' => 'pattern-preview__markup'],
-            ],
-          ],
-        ],
-      ];
-    }
-
-    return $render;
-  }
-
-}
diff --git a/modules/ui_patterns_library/src/Element/ComponentStory.php b/modules/ui_patterns_library/src/Element/ComponentStory.php
new file mode 100644
index 00000000..255efddb
--- /dev/null
+++ b/modules/ui_patterns_library/src/Element/ComponentStory.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Drupal\ui_patterns_library\Element;
+
+use Drupal\ui_patterns\Element\ComponentElement;
+
+/**
+ * Renders a component story.
+ *
+ * @RenderElement("component_story")
+ */
+class ComponentStory extends ComponentElement {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo(): array {
+    return [
+      '#pre_render' => [
+        [$this, 'loadStory'],
+        [$this, 'preRenderComponent'],
+      ],
+      '#component' => '',
+      '#story' => '',
+      '#props' => [],
+      '#slots' => [],
+      '#propsAlter' => [],
+      '#slotsAlter' => [],
+    ];
+  }
+
+  /**
+   *
+   */
+  public function loadStory(array $element): array {
+    $manager = \Drupal::service('plugin.manager.sdc');
+    if (!isset($element["#story"])) {
+      return $element;
+    }
+    $story_id = $element["#story"];
+    $component = $manager->getDefinition($element["#component"]);
+    if (!isset($component["stories"])) {
+      return $element;
+    }
+    if (!isset($component["stories"][$story_id])) {
+      return $element;
+    }
+    $story = $component["stories"][$story_id];
+    $slots = $story["slots"] ?? [];
+    $props = $story["props"] ?? [];
+    $slots = array_merge($element["#slots"], $slots);
+    $element["#slots"] = $manager::processStoriesSlots($slots);
+    $element["#props"] = array_merge($element["#props"], $props);
+    return $element;
+  }
+
+}
diff --git a/modules/ui_patterns_library/src/Plugin/Deriver/LibraryDeriver.php b/modules/ui_patterns_library/src/Plugin/Deriver/LibraryDeriver.php
deleted file mode 100644
index 520ca076..00000000
--- a/modules/ui_patterns_library/src/Plugin/Deriver/LibraryDeriver.php
+++ /dev/null
@@ -1,218 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_library\Plugin\Deriver;
-
-use Drupal\Component\Serialization\Yaml;
-use Drupal\Core\Extension\ExtensionDiscovery;
-use Drupal\Core\File\FileSystemInterface;
-use Drupal\Core\Messenger\MessengerInterface;
-use Drupal\Core\TypedData\TypedDataManager;
-use Drupal\ui_patterns\Plugin\Deriver\AbstractYamlPatternsDeriver;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Extension\ThemeHandlerInterface;
-
-/**
- * Plugin deriver for UI Patterns library.
- *
- * @package Drupal\ui_patterns_library\Deriver
- */
-class LibraryDeriver extends AbstractYamlPatternsDeriver {
-
-  /**
-   * The base plugin ID.
-   *
-   * @var string
-   */
-  protected $basePluginId;
-
-  /**
-   * The app root.
-   *
-   * @var string
-   */
-  protected $suffixes;
-
-  /**
-   * The app root.
-   *
-   * @var string
-   */
-  protected $root;
-
-  /**
-   * The theme handler.
-   *
-   * @var \Drupal\Core\Extension\ThemeHandlerInterface
-   */
-  protected $themeHandler;
-
-  /**
-   * The module handler to invoke the alter hook.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler;
-
-  /**
-   * Extension discovery class.
-   *
-   * @var \Drupal\Core\Extension\ExtensionDiscovery
-   */
-  protected $extensionDiscovery;
-
-  /**
-   * List of extension locations.
-   *
-   * @var array
-   */
-  protected $extensionLocations = [];
-
-  /**
-   * List of valid definition file extensions.
-   *
-   * @var array
-   */
-  protected $fileExtensions = [];
-
-  /**
-   * Constructor.
-   *
-   * @param string $base_plugin_id
-   *   The base plugin ID.
-   * @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
-   *   Typed data manager service.
-   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
-   *   Messenger.
-   * @param \Drupal\Core\File\FileSystemInterface $file_system
-   *   File system service.
-   * @param string $root
-   *   Application root directory.
-   * @param array $extensions
-   *   File extensions.
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   Module handler service.
-   * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
-   *   Theme handler service.
-   */
-  public function __construct($base_plugin_id, TypedDataManager $typed_data_manager, MessengerInterface $messenger, FileSystemInterface $file_system, $root, array $extensions, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
-    parent::__construct($base_plugin_id, $typed_data_manager, $messenger, $file_system);
-    $this->root = $root;
-    $this->fileExtensions = $extensions;
-    $this->moduleHandler = $module_handler;
-    $this->themeHandler = $theme_handler;
-    $this->extensionDiscovery = new ExtensionDiscovery($root);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, $base_plugin_id) {
-    return new static(
-      $base_plugin_id,
-      $container->get('typed_data_manager'),
-      $container->get('messenger'),
-      $container->get('file_system'),
-      $container->get('app.root'),
-      $container->getParameter('ui_patterns_library.file_extensions'),
-      $container->get('module_handler'),
-      $container->get('theme_handler')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFileExtensions() {
-    return $this->fileExtensions;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getPatterns() {
-    $patterns = [];
-    foreach ($this->getDirectories() as $provider => $directory) {
-      foreach ($this->fileScanDirectory($directory) as $file_path => $file) {
-        $host_extension = $this->getHostExtension($file_path);
-        if ($host_extension == FALSE || $host_extension == $provider) {
-          $content = file_get_contents($file_path);
-          foreach (Yaml::decode($content) as $id => $definition) {
-            $definition['id'] = $id;
-            $definition['base path'] = dirname($file_path);
-            $definition['file name'] = basename($file_path);
-            $definition['provider'] = $provider;
-            $patterns[] = $this->getPatternDefinition($definition);
-          }
-        }
-      }
-    }
-
-    return $patterns;
-  }
-
-  /**
-   * Create a list of all directories to scan.
-   *
-   * This includes all module directories and directories of the default theme
-   * and all of its possible base themes.
-   *
-   * @return array
-   *   An array containing directory paths keyed by their extension name.
-   */
-  protected function getDirectories() {
-    $default_theme = $this->themeHandler->getDefault();
-    $base_themes = $this->themeHandler->getBaseThemes($this->themeHandler->listInfo(), $default_theme);
-    $theme_directories = $this->themeHandler->getThemeDirectories();
-
-    $directories = [];
-    if (isset($theme_directories[$default_theme])) {
-      $directories[$default_theme] = $theme_directories[$default_theme];
-      foreach ($base_themes as $name => $theme) {
-        $directories[$name] = $theme_directories[$name];
-      }
-    }
-
-    return $directories + $this->moduleHandler->getModuleDirectories();
-  }
-
-  /**
-   * Get extension name that hosts the given YAML definition file.
-   *
-   * @param string $pathname
-   *   YAML definition file full path.
-   *
-   * @return bool|string
-   *   Either extension machine name or FALSE if not found.
-   */
-  protected function getHostExtension($pathname) {
-    $extensions = $this->getExtensionLocations();
-    $parts = explode(DIRECTORY_SEPARATOR, $pathname);
-    while (!empty($parts)) {
-      $path = implode(DIRECTORY_SEPARATOR, $parts);
-      if (isset($extensions[$path])) {
-        return $extensions[$path];
-      }
-      array_pop($parts);
-    }
-    return FALSE;
-  }
-
-  /**
-   * Get extension locations.
-   *
-   * @return array
-   *   Array of extensions keyed by their path location.
-   */
-  protected function getExtensionLocations() {
-    /** @var \Drupal\Core\Extension\Extension[] $extensions */
-    if (empty($this->extensionLocations)) {
-      $extensions = $this->extensionDiscovery->scan('theme') + $this->extensionDiscovery->scan('module');
-      foreach ($extensions as $name => $extension) {
-        $this->extensionLocations[$this->root . DIRECTORY_SEPARATOR . $extension->getPath()] = $name;
-      }
-    }
-    return $this->extensionLocations;
-  }
-
-}
diff --git a/modules/ui_patterns_library/src/Plugin/UiPatterns/Pattern/LibraryPattern.php b/modules/ui_patterns_library/src/Plugin/UiPatterns/Pattern/LibraryPattern.php
deleted file mode 100644
index f57aad21..00000000
--- a/modules/ui_patterns_library/src/Plugin/UiPatterns/Pattern/LibraryPattern.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_library\Plugin\UiPatterns\Pattern;
-
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Extension\ThemeHandlerInterface;
-use Drupal\ui_patterns\Definition\PatternDefinition;
-use Drupal\ui_patterns\Plugin\PatternBase;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * The UI Pattern plugin.
- *
- * ID is set to "yaml" for backward compatibility reasons.
- *
- * @UiPattern(
- *   id = "yaml",
- *   label = @Translation("Library Pattern"),
- *   description = @Translation("Pattern defined using a YAML file."),
- *   deriver = "\Drupal\ui_patterns_library\Plugin\Deriver\LibraryDeriver"
- * )
- */
-class LibraryPattern extends PatternBase {
-
-  /**
-   * Theme handler.
-   *
-   * @var \Drupal\Core\Extension\ThemeHandlerInterface
-   */
-  protected $themeHandler;
-
-  /**
-   * UiPatternsManager constructor.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, $root, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition, $root, $module_handler);
-    $this->themeHandler = $theme_handler;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('app.root'),
-      $container->get('module_handler'),
-      $container->get('theme_handler')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getThemeImplementation() {
-    $item = parent::getThemeImplementation();
-    $definition = $this->getPluginDefinition();
-    $item[$definition['theme hook']] += $this->processTemplateProperty($definition);
-    $item[$definition['theme hook']] += $this->processCustomThemeHookProperty($definition);
-    return $item;
-  }
-
-  /**
-   * Process 'custom hook theme' definition property.
-   *
-   * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition
-   *   Pattern definition array.
-   *
-   * @return array
-   *   Processed hook definition portion.
-   */
-  protected function processCustomThemeHookProperty(PatternDefinition $definition) {
-    /** @var \Drupal\Core\Extension\Extension $module */
-    $return = [];
-    if (!$definition->hasCustomThemeHook() && $this->moduleHandler->moduleExists($definition->getProvider())) {
-      $module = $this->moduleHandler->getModule($definition->getProvider());
-      $return['path'] = $module->getPath() . '/templates';
-      if ($this->templateExists($definition->getBasePath(), $definition->getTemplate())) {
-        $return['path'] = str_replace($this->root, '', $definition->getBasePath());
-      }
-    }
-    return $return;
-  }
-
-  /**
-   * Weather template exists in given directory.
-   *
-   * @param string $directory
-   *   Directory full path.
-   * @param string $template
-   *   Template name, without default Twig extension.
-   *
-   * @return bool
-   *   Weather template exists in given directory.
-   */
-  protected function templateExists($directory, $template) {
-    return file_exists($directory . DIRECTORY_SEPARATOR . $template . '.html.twig');
-  }
-
-  /**
-   * Process 'template' definition property.
-   *
-   * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition
-   *   Pattern definition array.
-   *
-   * @return array
-   *   Processed hook definition portion.
-   */
-  protected function processTemplateProperty(PatternDefinition $definition) {
-    $return = [];
-
-    if ($definition->hasTemplate()) {
-      $return = ['template' => $definition->getTemplate()];
-    }
-    return $return;
-  }
-
-}
diff --git a/modules/ui_patterns_library/src/Template/TwigExtension.php b/modules/ui_patterns_library/src/Template/TwigExtension.php
new file mode 100644
index 00000000..becdb5e5
--- /dev/null
+++ b/modules/ui_patterns_library/src/Template/TwigExtension.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Drupal\ui_patterns_library\Template;
+
+use Twig\Extension\AbstractExtension;
+use Twig\TwigFunction;
+
+/**
+ * Twig extension providing UI Patterns Legacy functionalities.
+ *
+ * @package Drupal\ui_patterns_library\Template
+ */
+class TwigExtension extends AbstractExtension {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName(): string {
+    return 'ui_patterns_library';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFunctions(): array {
+    return [
+      new TwigFunction('component_story', [
+        $this,
+        'renderComponentStory',
+      ]),
+    ];
+  }
+
+  /**
+   * Render given component story.
+   *
+   * @param string $component_id
+   *   Component ID.
+   * @param string $story_id
+   *   Story ID.
+   * @param array $slots
+   *   Component slots to override.
+   * @param array $props
+   *   Component props to override.
+   *
+   * @return array
+   *   Pattern render array.
+   *
+   * @see \Drupal\sdc\Element\ComponentElement
+   */
+  public function renderComponentStory(string $component_id, string $story_id, array $slots = [], array $props = []) {
+    return [
+      '#type' => 'component_story',
+      '#component' => $component_id,
+      '#story' => $story_id,
+      '#slots' => $slots,
+      '#props' => $props,
+    ];
+  }
+
+}
diff --git a/modules/ui_patterns_library/templates/patterns-meta-information.html.twig b/modules/ui_patterns_library/templates/patterns-meta-information.html.twig
deleted file mode 100644
index 95dba04a..00000000
--- a/modules/ui_patterns_library/templates/patterns-meta-information.html.twig
+++ /dev/null
@@ -1,36 +0,0 @@
-{#
-/**
- * @file
- * UI Pattern meta information.
- */
-#}
-
-{% if pattern is not empty %}
-
-  {# Pattern name and desciption. #}
-  <h3 class="pattern-preview__label">{{ pattern.label }}</h3>
-  <p class="pattern-preview__description">{{ pattern.description }}</p>
-
-  {# Pattern fields descriptions. #}
-  <table class="pattern-preview__fields">
-    <thead>
-    <tr>
-      <th>{{ "Field"|t }}</th>
-      <th>{{ "Label"|t }}</th>
-      <th>{{ "Type"|t }}</th>
-      <th>{{ "Description"|t }}</th>
-    </tr>
-    </thead>
-    <tbody>
-    {% for field in pattern.fields %}
-      <tr>
-        <td><code>{{ field.name }}</code></td>
-        <td>{{ field.label }}</td>
-        <td><code>{{ field.type }}</code></td>
-        <td>{{ field.description }}</td>
-      </tr>
-    {% endfor %}
-    </tbody>
-  </table>
-
-{% endif %}
diff --git a/modules/ui_patterns_library/templates/patterns-overview-page.html.twig b/modules/ui_patterns_library/templates/patterns-overview-page.html.twig
deleted file mode 100644
index 261398f0..00000000
--- a/modules/ui_patterns_library/templates/patterns-overview-page.html.twig
+++ /dev/null
@@ -1,48 +0,0 @@
-{#
-/**
- * @file
- * UI Pattern library page template, override this in your theme.
- */
-#}
-
-{% if patterns is not empty %}
-  <h2>{{ "Available patterns"|t }}</h2>
-
-  {# List of available patterns with anchor links. #}
-  <ul>
-    {% for pattern_name, pattern in patterns %}
-      <li>
-        <a href="#{{ pattern_name }}">{{ pattern.label }}</a>
-      </li>
-    {% endfor %}
-  </ul>
-
-  <hr>
-
-  {% for pattern_name, pattern in patterns %}
-
-    <div class="pattern-preview pattern-preview__{{ pattern.definition.id }}">
-      {# Pattern name and desciption. #}
-      <a name="{{ pattern_name }}"></a>
-      {{ pattern.meta }}
-
-      {# Rendered pattern preview. #}
-      <fieldset class="pattern-preview__preview">
-        <legend>{{ "Preview"|t }}</legend>
-        {{ pattern.rendered }}
-      </fieldset>
-
-      {# Link to standalone pattern preview page.#}
-      <p>
-        <a href="{{ url('ui_patterns.patterns.single', {'name': pattern_name}) }}" class="pattern-preview__view">
-          {% trans %}View {{ pattern.label }} as stand-alone{% endtrans %}
-        </a>
-      </p>
-    </div>
-
-    <hr>
-  {% endfor %}
-{% endif %}
-
-
-
diff --git a/modules/ui_patterns_library/templates/patterns-single-page.html.twig b/modules/ui_patterns_library/templates/patterns-single-page.html.twig
deleted file mode 100644
index 6d6294ce..00000000
--- a/modules/ui_patterns_library/templates/patterns-single-page.html.twig
+++ /dev/null
@@ -1,18 +0,0 @@
-{#
-/**
- * @file
- * UI Pattern library standalone page, override this in your theme.
- */
-#}
-
-{% if pattern is not empty %}
-  <div class="pattern-preview pattern-preview__{{ pattern.definition.id }}">
-    {{ pattern.meta }}
-
-    {# Rendered pattern preview. #}
-    <fieldset class="pattern-preview__preview">
-      <legend>{{ "Preview"|t }}</legend>
-      {{ pattern.rendered }}
-    </fieldset>
-  </div>
-{% endif %}
diff --git a/modules/ui_patterns_library/templates/patterns-variant-meta-information.html.twig b/modules/ui_patterns_library/templates/patterns-variant-meta-information.html.twig
deleted file mode 100644
index f1f692d0..00000000
--- a/modules/ui_patterns_library/templates/patterns-variant-meta-information.html.twig
+++ /dev/null
@@ -1,25 +0,0 @@
-{#
-/**
- * @file
- * UI Pattern variant meta information.
- */
-#}
-
-{% if variant is not empty %}
-  <table class="pattern-preview__variants pattern-preview__variants--{{ variant.name }}">
-    <thead>
-    <tr>
-      <th>{{ "Variant"|t }}</th>
-      <th>{{ "Name"|t }}</th>
-      <th>{{ "Description"|t }}</th>
-    </tr>
-    </thead>
-    <tbody>
-    <tr>
-      <td>{{ variant.label }}</td>
-      <td><code>{{ variant.name }}</code></td>
-      <td>{{ variant.description }}</td>
-    </tr>
-    </tbody>
-  </table>
-{% endif %}
diff --git a/modules/ui_patterns_library/templates/ui-patterns-component-metadata.html.twig b/modules/ui_patterns_library/templates/ui-patterns-component-metadata.html.twig
new file mode 100644
index 00000000..96777e15
--- /dev/null
+++ b/modules/ui_patterns_library/templates/ui-patterns-component-metadata.html.twig
@@ -0,0 +1,59 @@
+{#
+/**
+ * @file
+ * UI Pattern meta information.
+ */
+#}
+
+{% if component is not empty %}
+  <p class="ui_patterns_component__id">ID: <code>{{ component.id }}</code></p>
+  {% if component.description %}
+  <p class="ui_patterns_component__description">{{ component.description }}</p>
+  {% endif %}
+  {% if component.tags %}
+  <div class="ui_patterns_component__tags">
+    {{ "Tags:"|t }}
+    <ul>
+    {% for tag in component.tags %}
+       <li class="ui_patterns_component__tag">{{ tag }}</li>
+    {% endfor %}
+    </ul>
+  </div>
+  {% endif %}
+
+  {% if component.slots or component.props %}
+    <table class="ui_patterns_component__model views-table" style="width: 100%;">
+      <thead>
+        <tr>
+          <th>{{ "Name"|t }}</th>
+          <th>{{ "Label"|t }}</th>
+          <th>{{ "Type"|t }}</th>
+          <th>{{ "Description"|t }}</th>
+        </tr>
+      </thead>
+      <tbody>
+      {% for slot_id, slot in component.slots %}
+        <tr class="ui_patterns_component__slot">
+          <td><code>{{ slot_id }}</code></td>
+          <td>{{ slot.title }}</td>
+          <td><code>slot</code></td>
+          <td>{{ slot.description }}</td>
+        </tr>
+      {% endfor %}
+      {% for prop_id, prop in component.props.properties %}
+        <tr class="ui_patterns_component__prop">
+          <td><code>{{ prop_id }}</code></td>
+          <td>{{ prop.title }}</td>
+          <td><code>{{ prop.ui_patterns.type_definition.pluginId }}</code></td>
+          <td>{{ prop.description }}
+          <small><pre>{{ prop|filter((v, k) => k != 'title')|filter((v, k) => k != 'description')
+          |filter((v, k) => k != 'examples')|filter((v, k) => k != 'default')|filter((v, k) => k != 'ui_patterns')
+          |json_encode(constant('JSON_PRETTY_PRINT') b-or constant('JSON_UNESCAPED_SLASHES')) }}</pre></small>
+          </td>
+        </tr>
+      {% endfor %}
+      </tbody>
+    </table>
+  {% endif %}
+
+{% endif %}
diff --git a/modules/ui_patterns_library/templates/ui-patterns-overview-page.html.twig b/modules/ui_patterns_library/templates/ui-patterns-overview-page.html.twig
new file mode 100644
index 00000000..1f1dc86f
--- /dev/null
+++ b/modules/ui_patterns_library/templates/ui-patterns-overview-page.html.twig
@@ -0,0 +1,69 @@
+{#
+/**
+ * @file
+ * UI Pattern library page template, override this in your theme.
+ */
+#}
+
+{% if groups is not empty %}
+  <h2>{{ "Available components"|t }}</h2>
+
+  {% if groups|length > 1 %}
+    {% for group_name, components in groups %}
+      <h3>{{ group_name }}</h3>
+      <ul>
+        {% for component in components %}
+          <li>
+            <a href="#{{ component.id }}">{{ component.name }}</a>
+          </li>
+        {% endfor %}
+      </ul>
+    {% endfor %}
+  {% else %}
+    <ul>
+      {% for component in components %}
+        <li>
+          <a href="#{{ component.id }}">{{ component.name }}</a>
+        </li>
+      {% endfor %}
+    </ul>
+  {% endif %}
+
+  <hr>
+
+  {% for components in groups %}
+    {% for component in components %}
+      <div class="ui_patterns_component" id="{{ component.id }}">
+        <h2 class="ui_patterns_component__title">{{ component.name }}</h2>
+        {{ include('ui-patterns-component-metadata.html.twig', {'component': component}, with_context = false) }}
+        <p class="ui_patterns_component__link">
+          <a href="{{ url('ui_patterns_library.single', {'name': component.id}) }}">
+            {% trans %}View {{ component.name }} as stand-alone{% endtrans %}
+          </a>
+        </p>
+        {% if component.variants is defined and component.variants|length > 0 %}
+        <div class="ui_patterns_component__variants">
+          {% for variant_id, variant in component.variants %}
+          <div class="ui_patterns_variant">
+            {% for story_id, story in component.stories %}
+              <div class="ui_patterns_story">
+              {{ component_story(component.id, story_id, {}, {'variant': variant_id}) }}
+              </div>
+            {% endfor %}
+          </div>
+          {% endfor %}
+        </div>
+        {% else %}
+        <div class="ui_patterns_component__stories">
+          {% for story_id, story in component.stories %}
+            <div class="ui_patterns_story">
+            {{ component_story(component.id, story_id, {}, {'variant': variant_id}) }}
+            </div>
+          {% endfor %}
+        </div>
+        {% endif %}
+      </div>
+      <hr>
+    {% endfor %}
+  {% endfor %}
+{% endif %}
diff --git a/modules/ui_patterns_library/templates/ui-patterns-single-page.html.twig b/modules/ui_patterns_library/templates/ui-patterns-single-page.html.twig
new file mode 100644
index 00000000..c4626508
--- /dev/null
+++ b/modules/ui_patterns_library/templates/ui-patterns-single-page.html.twig
@@ -0,0 +1,42 @@
+{#
+/**
+ * @file
+ * UI Pattern library standalone page, override this in your theme.
+ */
+#}
+
+{% if component is not empty %}
+<div class="ui_patterns_component">
+  {{ include('ui-patterns-component-metadata.html.twig', {'component': component}, with_context = false) }}
+  {% if component.variants is defined and component.variants|length > 0 %}
+  <div class="ui_patterns_component__variants">
+    {% for variant_id, variant in component.variants %}
+      <div class="ui_patterns_variant">
+      <h2 class="ui_patterns_variant__title">{{ variant.title }} (<code>{{ variant_id }}</code>)</h2>
+      {% if variant.description %}
+      <p class="ui_patterns_variant__description">{{ variant.description }}</p>
+      {% endif %}
+      {% for story_id, story in component.stories %}
+        <div class="ui_patterns_story">
+          <h3 class="ui_patterns_story__title">{{ story.title }}</h3>
+          {% if story.description %}
+          <p class="ui_patterns_story__description">{{ story.description }}</p>
+          {% endif %}
+          {{ component_story(component.id, story_id, {}, {'variant': variant_id}) }}
+        </div>
+      {% endfor %}
+    {% endfor %}
+  </div>
+   {% else %}
+    {% for story_id, story in component.stories %}
+      <div class="ui_patterns_story">
+        <h3 class="ui_patterns_story__title">{{ story.title }}</h3>
+        {% if story.description %}
+        <p class="ui_patterns_story__description">{{ story.description }}</p>
+        {% endif %}
+        {{ component_story(component.id, story_id) }}
+      </div>
+    {% endfor %}
+  {% endif %}
+</div>
+{% endif %}
diff --git a/modules/ui_patterns_library/tests/fixtures/overview-page-patterns.yml b/modules/ui_patterns_library/tests/fixtures/overview-page-patterns.yml
deleted file mode 100644
index a08b7cf4..00000000
--- a/modules/ui_patterns_library/tests/fixtures/overview-page-patterns.yml
+++ /dev/null
@@ -1,99 +0,0 @@
-- name: 'simple'
-  label: 'Simple'
-  description: 'A simple pattern'
-  has_variants: false
-  preview: '<div class="pattern-simple">Simple pattern field</div>'
-  fields:
-  - name: 'field'
-    type: 'string'
-    label: 'Field'
-    description: 'Field description'
-
-- name: 'with_variants'
-  label: 'With variants'
-  description: 'Pattern with variants'
-  has_variants: true
-  preview: ~
-  fields:
-  - name: 'field'
-    type: 'string'
-    label: 'Field'
-    description: 'Field description'
-  variants:
-  - meta:
-      name: 'one'
-      label: 'One'
-      description: 'First variant'
-    preview: '<div class="pattern-with-variant-one">With variants pattern field</div>'
-  - meta:
-      name: 'two'
-      label: 'Two'
-      description: 'Second variant'
-    preview: '<div class="pattern-with-variant-two">With variants pattern field</div>'
-
-- name: 'with_custom_theme_hook'
-  theme hook: 'custom_theme_hook'
-  label: 'With custom theme hook'
-  description: 'Pattern with custom theme hook.'
-  has_variants: false
-  preview: 'With custom theme hook: Pattern field value'
-  fields:
-  - name: 'field'
-    type: 'string'
-    label: 'Field'
-    description: 'Field description'
-
-- name: 'button'
-  label: 'Button'
-  description: 'A simple button.'
-  has_variants: true
-  preview: ~
-  fields:
-  - name: 'title'
-    type: 'text'
-    label: 'Label'
-    description: 'The button label'
-    preview: 'Submit'
-  - name: 'url'
-    type: 'text'
-    label: 'URL'
-    description: 'The button URL'
-    preview: 'http://example.com'
-  variants:
-  - meta:
-      name: 'default'
-      label: 'Default'
-      description: 'A default button, nothing to see here.'
-    preview: '<a href="http://example.com" target="_blank" class="btn btn-default button">Submit</a>'
-  - meta:
-      name: 'primary'
-      label: 'Primary'
-      description: 'A primary button.'
-    preview: '<a href="http://example.com" target="_blank" class="btn btn-primary button">Submit</a>'
-  - meta:
-      name: 'danger'
-      label: 'Danger'
-      description: 'A button for dangerous operations.'
-    preview: '<a href="http://example.com" target="_blank" class="btn btn-danger button">Delete</a>'
-
-- name: 'with_local_libraries'
-  label: 'With local libraries'
-  description: 'Pattern defining local libraries'
-  has_variants: false
-  preview: 'With local libraries: Pattern field value'
-  fields:
-  - name: 'field'
-    type: 'string'
-    label: 'Field'
-    description: 'Field description'
-
-- name: 'with_raw_template'
-  label: 'With raw template'
-  description: 'Pattern using raw Twig template.'
-  has_variants: false
-  preview: 'With raw template: Pattern field value'
-  fields:
-  - name: 'field'
-    type: 'string'
-    label: 'Field'
-    description: 'Field description'
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_bad_definition_test/ui_patterns_library_bad_definition_test.info.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_bad_definition_test/ui_patterns_library_bad_definition_test.info.yml
deleted file mode 100644
index 5c6900d0..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_bad_definition_test/ui_patterns_library_bad_definition_test.info.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-name: 'UI Patterns bad definition test'
-type: module
-core: 8.x
-core_version_requirement: ^8 || ^9
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_bad_definition_test/ui_patterns_library_bad_definition_test.ui_patterns.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_bad_definition_test/ui_patterns_library_bad_definition_test.ui_patterns.yml
deleted file mode 100644
index b411c1e6..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_bad_definition_test/ui_patterns_library_bad_definition_test.ui_patterns.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-bad_definition:
-  foo: 'bar'
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/button/button.ui_patterns.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/button/button.ui_patterns.yml
deleted file mode 100644
index 987c56a5..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/button/button.ui_patterns.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-button:
-  label: Button
-  description: A simple button.
-  variants:
-    default:
-      label: Default
-      description: A default button, nothing to see here.
-    primary:
-      label: Primary
-      description: A primary button.
-    danger:
-      label: Danger
-      description: A button for dangerous operations.
-  fields:
-    title:
-      type: text
-      label: Label
-      description: The button label
-      preview: Submit
-    url:
-      type: text
-      label: URL
-      description: The button URL
-      preview: http://example.com
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/button/pattern-button.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/button/pattern-button.html.twig
deleted file mode 100644
index 656e5544..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/button/pattern-button.html.twig
+++ /dev/null
@@ -1 +0,0 @@
-<a href="{{ url }}" target="_blank" class="btn btn-{{ variant }} button">{{ title }}</a>
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/pattern-with-local-libraries.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/pattern-with-local-libraries.html.twig
deleted file mode 100644
index 90dac666..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/pattern-with-local-libraries.html.twig
+++ /dev/null
@@ -1 +0,0 @@
-With local libraries: {{ field }}
\ No newline at end of file
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/with_local_libraries.ui_patterns.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/with_local_libraries.ui_patterns.yml
deleted file mode 100644
index 2ac48716..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/with_local_libraries.ui_patterns.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-with_local_libraries:
-  label: 'With local libraries'
-  description: 'Pattern defining local libraries'
-  fields:
-    field:
-      type: 'string'
-      label: 'Field'
-      description: 'Field description'
-      preview: 'Pattern field value'
-  libraries:
-    - library_one:
-        css:
-          component:
-            css/library_one.css: {}
-    - library_two:
-        css:
-          theme:
-            css/library_two.css: {}
-        js:
-          js/library_two_1.js: {}
-          js/library_two_2.js: {}
-        dependencies:
-          - core/drupal.tabledrag
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/ui_patterns_library_module_test.info.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/ui_patterns_library_module_test.info.yml
deleted file mode 100644
index 8fbd6cc8..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/ui_patterns_library_module_test.info.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-name: 'UI Patterns library module test'
-type: module
-core: 8.x
-core_version_requirement: ^8 || ^9
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/ui_patterns_library_module_test.ui_patterns.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/ui_patterns_library_module_test.ui_patterns.yml
deleted file mode 100644
index b2d176df..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/ui_patterns_library_module_test.ui_patterns.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-with_raw_template:
-  label: 'With raw template'
-  description: 'Pattern using raw Twig template.'
-  use: "@ui_patterns_library_theme_test/raw/raw-template.twig"
-  fields:
-    field:
-      type: 'string'
-      label: 'Field'
-      description: 'Field description'
-      preview: 'Pattern field value'
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/custom-theme-hook.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/custom-theme-hook.html.twig
deleted file mode 100644
index 9b863bdf..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/custom-theme-hook.html.twig
+++ /dev/null
@@ -1 +0,0 @@
-With custom theme hook: {{ field }}
\ No newline at end of file
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-button--variant-danger.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-button--variant-danger.html.twig
deleted file mode 100644
index a35eda97..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-button--variant-danger.html.twig
+++ /dev/null
@@ -1 +0,0 @@
-<a href="{{ url }}" target="_blank" class="btn btn-{{ variant }} button">{{ 'Delete'|t }}</a>
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-simple.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-simple.html.twig
deleted file mode 100644
index 5a03b485..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-simple.html.twig
+++ /dev/null
@@ -1 +0,0 @@
-<div class="pattern-simple">{{ field }}</div>
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-with-variants--variant-one.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-with-variants--variant-one.html.twig
deleted file mode 100644
index 5ee19a3f..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-with-variants--variant-one.html.twig
+++ /dev/null
@@ -1 +0,0 @@
-<div class="pattern-with-variant-one">{{ field }}</div>
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-with-variants--variant-two.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-with-variants--variant-two.html.twig
deleted file mode 100644
index e1853a3a..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-with-variants--variant-two.html.twig
+++ /dev/null
@@ -1 +0,0 @@
-<div class="pattern-with-variant-two">{{ field }}</div>
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/raw/raw-template.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/raw/raw-template.twig
deleted file mode 100644
index 8fb4bf38..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/raw/raw-template.twig
+++ /dev/null
@@ -1 +0,0 @@
-With raw template: {{ field }}
\ No newline at end of file
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/ui_patterns_library_theme_test.info.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/ui_patterns_library_theme_test.info.yml
deleted file mode 100644
index d0069a21..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/ui_patterns_library_theme_test.info.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: 'UI Patterns library theme test'
-type: theme
-core: 8.x
-core_version_requirement: ^8 || ^9
-base theme: stark
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/ui_patterns_library_theme_test.ui_patterns.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/ui_patterns_library_theme_test.ui_patterns.yml
deleted file mode 100644
index 8d2b4ab8..00000000
--- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/ui_patterns_library_theme_test.ui_patterns.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-simple:
-  label: 'Simple'
-  description: 'A simple pattern'
-  fields:
-    field:
-      type: 'string'
-      label: 'Field'
-      description: 'Field description'
-      preview: 'Simple pattern field'
-
-with_variants:
-  label: 'With variants'
-  description: 'Pattern with variants'
-  variants:
-    one:
-      label: 'One'
-      description: 'First variant'
-    two:
-      label: 'Two'
-      description: 'Second variant'
-  fields:
-    field:
-      type: 'string'
-      label: 'Field'
-      description: 'Field description'
-      preview: 'With variants pattern field'
-
-with_custom_theme_hook:
-  theme hook: 'custom_theme_hook'
-  label: 'With custom theme hook'
-  description: 'Pattern with custom theme hook.'
-  fields:
-    field:
-      type: 'string'
-      label: 'Field'
-      description: 'Field description'
-      preview: 'Pattern field value'
diff --git a/modules/ui_patterns_library/tests/src/FunctionalJavascript/UiPatternsLibraryBadDefinitionTest.php b/modules/ui_patterns_library/tests/src/FunctionalJavascript/UiPatternsLibraryBadDefinitionTest.php
deleted file mode 100644
index 3f41c4fa..00000000
--- a/modules/ui_patterns_library/tests/src/FunctionalJavascript/UiPatternsLibraryBadDefinitionTest.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns_library\FunctionalJavascript;
-
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-
-/**
- * Test invalid definition error messages.
- *
- * @group ui_patterns_library
- */
-class UiPatternsLibraryBadDefinitionTest extends WebDriverTestBase {
-
-  /**
-   * Default theme.
-   *
-   * @var string
-   */
-  protected $defaultTheme = 'stark';
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = [
-    'ui_patterns',
-    'ui_patterns_library',
-    'ui_patterns_library_bad_definition_test',
-  ];
-
-  /**
-   * Test error messages for invalid pattern definitions.
-   */
-  public function testErrorMessages() {
-    $session = $this->assertSession();
-
-    $user = $this->drupalCreateUser(['access patterns page']);
-    $this->drupalLogin($user);
-
-    drupal_flush_all_caches();
-    $this->drupalGet('/patterns');
-
-    $session->pageTextContains("Pattern 'bad_definition' is skipped because of the following validation error(s):");
-    $session->pageTextContains('Validation error on "bad_definition.label": This value should not be null.');
-  }
-
-}
diff --git a/modules/ui_patterns_library/tests/src/FunctionalJavascript/UiPatternsLibraryOverviewTest.php b/modules/ui_patterns_library/tests/src/FunctionalJavascript/UiPatternsLibraryOverviewTest.php
deleted file mode 100644
index 3188b00b..00000000
--- a/modules/ui_patterns_library/tests/src/FunctionalJavascript/UiPatternsLibraryOverviewTest.php
+++ /dev/null
@@ -1,214 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns_library\FunctionalJavascript;
-
-use Drupal\Core\Serialization\Yaml;
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-
-/**
- * Test patterns overview page.
- *
- * @group ui_patterns_library
- */
-class UiPatternsLibraryOverviewTest extends WebDriverTestBase {
-
-  /**
-   * Default theme.
-   *
-   * @var string
-   */
-  protected $defaultTheme = 'ui_patterns_library_theme_test';
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = [
-    'ui_patterns',
-    'ui_patterns_library',
-    'ui_patterns_library_module_test',
-  ];
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function setUp(): void {
-    parent::setUp();
-
-    $user = $this->drupalCreateUser(['access patterns page']);
-    $this->drupalLogin($user);
-  }
-
-  /**
-   * Tests overview page.
-   */
-  public function testOverviewPage() {
-    $session = $this->assertSession();
-
-    $this->drupalGet('/patterns');
-
-    $session->elementContains('css', 'h1', 'Pattern library');
-    $session->elementContains('css', 'h2', 'Available patterns');
-
-    foreach ($this->getExpectedPatterns() as $index => $pattern) {
-
-      // Assert pattern anchor link.
-      $this->assertListLink($index + 1, $pattern['label'], $pattern['name']);
-
-      // Assert pattern preview.
-      $this->assertPatternPreview($pattern);
-
-      // Test view single page link.
-      $session->linkExists("View {$pattern['label']} as stand-alone");
-      $link = $this->getSession()->getPage()->findLink("View {$pattern['label']} as stand-alone");
-      $this->assertStringContainsString('/patterns/' . $pattern['name'], $link->getAttribute('href'));
-    }
-  }
-
-  /**
-   * Tests overview page.
-   */
-  public function testSinglePages() {
-    $session = $this->assertSession();
-
-    foreach ($this->getExpectedPatterns() as $pattern) {
-      $this->drupalGet('/patterns/' . $pattern['name']);
-      $session->elementContains('css', 'h1', $pattern['label']);
-
-      // Assert pattern preview.
-      $this->assertPatternPreview($pattern);
-    }
-  }
-
-  /**
-   * Test that libraries defined locally are loaded correctly.
-   */
-  public function testLocalLibraries() {
-    $session = $this->assertSession();
-
-    $this->drupalGet('/patterns/with_local_libraries');
-    $session->responseContains('href="/modules/custom/ui_patterns/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/css/library_one.css');
-    $session->responseContains('href="/modules/custom/ui_patterns/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/css/library_two.css');
-    $session->responseContains('src="/modules/custom/ui_patterns/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/js/library_two_1.js');
-    $session->responseContains('src="/modules/custom/ui_patterns/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/js/library_two_2.js');
-    $session->responseContains('src="/core/misc/tabledrag.js');
-  }
-
-  /**
-   * Assert pattern preview display.
-   *
-   * @param array $pattern
-   *   Expected pattern.
-   */
-  protected function assertPatternPreview(array $pattern) {
-    $session = $this->assertSession();
-
-    // Assert pattern title and description.
-    $root = '.pattern-preview__' . $pattern['name'];
-    $session->elementExists('css', $root);
-    $session->elementContains('css', "$root > h3.pattern-preview__label", $pattern['label']);
-    $session->elementContains('css', "$root > p.pattern-preview__description", $pattern['description']);
-
-    // Assert metadata block.
-    $this->assertPatternFields($root, $pattern);
-
-    if (!$pattern['has_variants']) {
-      // Make sure no variant markup exists.
-      $session->elementNotExists('css', "$root > fieldset.pattern-preview__preview > .pattern-preview__variants");
-
-      // Assert preview content when without variants.
-      $session->elementContains('css', "$root > fieldset.pattern-preview__preview > .pattern-preview__markup", $pattern['preview']);
-    }
-    else {
-      // Assert that variant markup exists.
-      $session->elementExists('css', "$root > fieldset.pattern-preview__preview > .pattern-preview__variants");
-
-      // Assert variant meta information and preview.
-      foreach ($pattern['variants'] as $variant) {
-        $this->assertPatternVariant($root, $variant);
-      }
-    }
-  }
-
-  /**
-   * Assert pattern table fields.
-   *
-   * @param string $root
-   *   CSS selector of element containing the table.
-   * @param array $pattern
-   *   Expected pattern.
-   *
-   * @throws \Behat\Mink\Exception\ElementHtmlException
-   */
-  protected function assertPatternFields($root, array $pattern) {
-    $session = $this->assertSession();
-
-    // Assert table header.
-    foreach (['Field', 'Label', 'Type', 'Description'] as $index => $item) {
-      $child = $index + 1;
-      $session->elementContains('css', "$root > table.pattern-preview__fields > thead > tr > th:nth-child($child)", $item);
-    }
-
-    // Assert field table rows.
-    foreach ($pattern['fields'] as $index => $field) {
-      $child = $index + 1;
-      $row_root = "$root > table.pattern-preview__fields > tbody > tr:nth-child($child)";
-      $session->elementContains('css', "$row_root > td:nth-child(1)", $field['name']);
-      $session->elementContains('css', "$row_root > td:nth-child(2)", $field['label']);
-      $session->elementContains('css', "$row_root > td:nth-child(3)", $field['type']);
-      $session->elementContains('css', "$row_root > td:nth-child(4)", $field['description']);
-    }
-  }
-
-  /**
-   * Assert pattern variant metadata and preview.
-   *
-   * @param string $root
-   *   CSS selector of element containing the table.
-   * @param array $variant
-   *   Variant expected values.
-   */
-  protected function assertPatternVariant($root, array $variant) {
-    $session = $this->assertSession();
-    $name = $variant['meta']['name'];
-
-    // Assert table header.
-    foreach (['Variant', 'Name', 'Description'] as $index => $item) {
-      $child = $index + 1;
-      $session->elementContains('css', "$root table.pattern-preview__variants--$name > thead > tr > th:nth-child($child)", $item);
-    }
-
-    // Assert variant meta table rows.
-    $row_root = "$root table.pattern-preview__variants--$name > tbody > tr";
-    $session->elementContains('css', "$row_root > td:nth-child(1)", $variant['meta']['name']);
-    $session->elementContains('css', "$row_root > td:nth-child(2)", $variant['meta']['label']);
-    $session->elementContains('css', "$row_root > td:nth-child(3)", $variant['meta']['description']);
-
-    // Assert variant preview.
-    $session->elementContains('css', "$root .pattern-preview__markup--variant_$name", $variant['preview']);
-  }
-
-  /**
-   * Assert pattern overview list link.
-   *
-   * @param int $index
-   *   Position on list.
-   * @param string $label
-   *   Pattern label.
-   * @param string $name
-   *   Pattern machine name.
-   *
-   * @throws \Behat\Mink\Exception\ElementHtmlException
-   */
-  protected function assertListLink($index, $label, $name) {
-    $this->assertSession()->elementContains('css', "ul > li:nth-child($index) > a", $label);
-    $this->assertSession()->elementAttributeContains('css', "ul > li:nth-child($index) > a", 'href', '#' . $name);
-  }
-
-  /**
-   * Get expected patterns.
-   */
-  protected function getExpectedPatterns() {
-    return Yaml::decode(file_get_contents(__DIR__ . '/../../fixtures/overview-page-patterns.yml'));
-  }
-
-}
diff --git a/modules/ui_patterns_library/ui_patterns_library.info.yml b/modules/ui_patterns_library/ui_patterns_library.info.yml
index 23ad2ff1..151e5c28 100644
--- a/modules/ui_patterns_library/ui_patterns_library.info.yml
+++ b/modules/ui_patterns_library/ui_patterns_library.info.yml
@@ -1,8 +1,7 @@
-name: UI Patterns Library
+name: "UI Patterns Library"
 type: module
-description: Exposed patterns in you modules and themes and display them in a pattern library page.
-core: 8.x
-core_version_requirement: ^8 || ^9
-package: User interface
+description: "Browse UI components in library pages."
+core_version_requirement: ^10
+package: "User interface"
 dependencies:
   - ui_patterns:ui_patterns
diff --git a/modules/ui_patterns_library/ui_patterns_library.links.menu.yml b/modules/ui_patterns_library/ui_patterns_library.links.menu.yml
new file mode 100644
index 00000000..49b255f8
--- /dev/null
+++ b/modules/ui_patterns_library/ui_patterns_library.links.menu.yml
@@ -0,0 +1,5 @@
+ui_patterns_library.overview:
+  title: 'UI Patterns library'
+  description: 'Browse components in library pages.'
+  parent: system.admin_reports
+  route_name: ui_patterns_library.overview
diff --git a/modules/ui_patterns_library/ui_patterns_library.module b/modules/ui_patterns_library/ui_patterns_library.module
index 79f767e9..12c5874e 100644
--- a/modules/ui_patterns_library/ui_patterns_library.module
+++ b/modules/ui_patterns_library/ui_patterns_library.module
@@ -10,17 +10,14 @@
  */
 function ui_patterns_library_theme() {
   return [
-    'patterns_overview_page' => [
-      'variables' => ['patterns' => NULL],
+    'ui_patterns_overview_page' => [
+      'variables' => ['groups' => NULL],
     ],
-    'patterns_single_page' => [
-      'variables' => ['pattern' => NULL],
-    ],
-    'patterns_meta_information' => [
-      'variables' => ['pattern' => NULL],
-    ],
-    'patterns_variant_meta_information' => [
-      'variables' => ['variant' => NULL],
+    'ui_patterns_single_page' => [
+      'variables' => ['component' => NULL],
     ],
+    'ui_patterns_component_metadata' => [
+      'variables' => ['component' => NULL],
+    ]
   ];
 }
diff --git a/modules/ui_patterns_library/ui_patterns_library.routing.yml b/modules/ui_patterns_library/ui_patterns_library.routing.yml
index 4960f572..b233b9e9 100644
--- a/modules/ui_patterns_library/ui_patterns_library.routing.yml
+++ b/modules/ui_patterns_library/ui_patterns_library.routing.yml
@@ -1,14 +1,14 @@
-ui_patterns.patterns.overview:
+ui_patterns_library.overview:
   path: '/patterns'
   defaults:
-    _controller: '\Drupal\ui_patterns_library\Controller\PatternsLibraryController::overview'
-    _title: 'Pattern library'
+    _controller: '\Drupal\ui_patterns_library\Controller\LibraryController::overview'
+    _title: 'Components library'
   requirements:
     _permission: 'access patterns page'
-ui_patterns.patterns.single:
+ui_patterns_library.single:
   path: '/patterns/{name}'
   defaults:
-    _controller: '\Drupal\ui_patterns_library\Controller\PatternsLibraryController::single'
-    _title_callback: '\Drupal\ui_patterns_library\Controller\PatternsLibraryController::title'
+    _controller: '\Drupal\ui_patterns_library\Controller\LibraryController::single'
+    _title_callback: '\Drupal\ui_patterns_library\Controller\LibraryController::title'
   requirements:
     _permission: 'access patterns page'
diff --git a/modules/ui_patterns_library/ui_patterns_library.services.yml b/modules/ui_patterns_library/ui_patterns_library.services.yml
index 2a8c750f..6c9176d3 100644
--- a/modules/ui_patterns_library/ui_patterns_library.services.yml
+++ b/modules/ui_patterns_library/ui_patterns_library.services.yml
@@ -1,5 +1,5 @@
-parameters:
-  ui_patterns_library.file_extensions:
-    - ".ui_patterns.yml"
-    - ".patterns.yml"
-    - ".pattern.yml"
+services:
+  ui_patterns_library.twig.extension:
+    class: Drupal\ui_patterns_library\Template\TwigExtension
+    tags:
+      - { name: twig.extension }
diff --git a/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewRowSource.php b/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewRowSource.php
new file mode 100644
index 00000000..4892d5bb
--- /dev/null
+++ b/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewRowSource.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns_views\Plugin\UiPatterns\Source;
+
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Plugin implementation of the source.
+ *
+ * @Source(
+ *   id = "view_row",
+ *   label = @Translation("View row"),
+ *   description = @Translation("TBD."),
+ *   prop_types = {
+ *     "slot"
+ *   }
+ * )
+ */
+final class ViewRowSource extends SourcePluginBase {
+
+  /**
+   *
+   */
+  public function getData(): mixed {
+    // $view = $this->getContextProperty('view');
+    // foreach ($view->display_handler->getFieldLabels() as $name => $label) {
+    //  $sources[] = $this->getSourceField($name, $label);
+    // }
+    return [];
+  }
+
+  /**
+   *
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewRowsSource.php b/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewRowsSource.php
new file mode 100644
index 00000000..aa535466
--- /dev/null
+++ b/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewRowsSource.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns_views\Plugin\UiPatterns\Source;
+
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Plugin implementation of the source_provider.
+ *
+ * @Source(
+ *   id = "view_rows",
+ *   label = @Translation("View rows"),
+ *   description = @Translation("TBD."),
+ *   prop_types = {
+ *     "slot"
+ *   }
+ * )
+ */
+final class ViewRowsSource extends SourcePluginBase {
+
+  /**
+   *
+   */
+  public function getData(): mixed {
+    return [];
+  }
+
+  /**
+   *
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewTitleSource.php b/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewTitleSource.php
new file mode 100644
index 00000000..7d7decbe
--- /dev/null
+++ b/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewTitleSource.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns_views\Plugin\UiPatterns\Source;
+
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Plugin implementation of the source.
+ *
+ * @Source(
+ *   id = "view_title",
+ *   label = @Translation("View title"),
+ *   description = @Translation("TBD."),
+ *   prop_types = {
+ *     "slot"
+ *   }
+ * )
+ */
+final class ViewTitleSource extends SourcePluginBase {
+
+  /**
+   *
+   */
+  public function getData(): mixed {
+    return 'Nice Site name';
+  }
+
+  /**
+   *
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewsRowSource.php b/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewsRowSource.php
deleted file mode 100644
index 0ee4011c..00000000
--- a/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewsRowSource.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_views\Plugin\UiPatterns\Source;
-
-use Drupal\ui_patterns\Plugin\PatternSourceBase;
-
-/**
- * Defines Views row pattern source plugin.
- *
- * @UiPatternsSource(
- *   id = "views_row",
- *   label = @Translation("Views row"),
- *   provider = "views",
- *   tags = {
- *     "views_row"
- *   }
- * )
- */
-class ViewsRowSource extends PatternSourceBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getSourceFields() {
-    $sources = [];
-    /** @var \Drupal\views\ViewExecutable $view */
-    $view = $this->getContextProperty('view');
-    foreach ($view->display_handler->getFieldLabels() as $name => $label) {
-      $sources[] = $this->getSourceField($name, $label);
-    }
-    return $sources;
-  }
-
-}
diff --git a/modules/ui_patterns_views/src/Plugin/views/row/Component.php b/modules/ui_patterns_views/src/Plugin/views/row/Component.php
new file mode 100644
index 00000000..003a6937
--- /dev/null
+++ b/modules/ui_patterns_views/src/Plugin/views/row/Component.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\ui_patterns_views\Plugin\views\row;
+
+use Drupal\ui_patterns\Form\PatternDisplayFormTrait;
+use Drupal\views\Plugin\views\row\Fields;
+
+/**
+ * Pattern Views row plugin.
+ *
+ * @ingroup views_row_plugins
+ *
+ * @ViewsRow(
+ *   id = "component",
+ *   title = @Translation("Component"),
+ *   help = @Translation("Displays fields using an UI component."),
+ *   theme = "component_views_row",
+ *   display_types = {"normal"}
+ * )
+ */
+class Component extends Fields {
+
+  use PatternDisplayFormTrait;
+
+}
diff --git a/modules/ui_patterns_views/src/Plugin/views/row/Pattern.php b/modules/ui_patterns_views/src/Plugin/views/row/Pattern.php
deleted file mode 100644
index 2a1a2993..00000000
--- a/modules/ui_patterns_views/src/Plugin/views/row/Pattern.php
+++ /dev/null
@@ -1,152 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_views\Plugin\views\row;
-
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\ui_patterns\Form\PatternDisplayFormTrait;
-use Drupal\ui_patterns\UiPatternsSourceManager;
-use Drupal\ui_patterns\UiPatternsManager;
-use Drupal\views\Plugin\views\field\FieldPluginBase;
-use Drupal\views\Plugin\views\row\Fields;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Pattern Views row plugin.
- *
- * @ingroup views_row_plugins
- *
- * @ViewsRow(
- *   id = "ui_patterns",
- *   title = @Translation("Pattern"),
- *   help = @Translation("Displays fields using a pattern."),
- *   theme = "pattern_views_row",
- *   display_types = {"normal"}
- * )
- */
-class Pattern extends Fields {
-
-  use PatternDisplayFormTrait;
-
-  /**
-   * Module Handler.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler = NULL;
-
-  /**
-   * UI Patterns manager.
-   *
-   * @var \Drupal\ui_patterns\UiPatternsManager
-   */
-  protected $patternsManager;
-
-  /**
-   * UI Patterns manager.
-   *
-   * @var \Drupal\ui_patterns\UiPatternsSourceManager
-   */
-  protected $sourceManager;
-
-  /**
-   * Pattern constructor.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin_id for the plugin instance.
-   * @param mixed $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\ui_patterns\UiPatternsManager $patterns_manager
-   *   UI Patterns manager.
-   * @param \Drupal\ui_patterns\UiPatternsSourceManager $source_manager
-   *   UI Patterns source manager.
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   Module handler.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, UiPatternsManager $patterns_manager, UiPatternsSourceManager $source_manager, ModuleHandlerInterface $module_handler) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->patternsManager = $patterns_manager;
-    $this->sourceManager = $source_manager;
-    $this->moduleHandler = $module_handler;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('plugin.manager.ui_patterns'),
-      $container->get('plugin.manager.ui_patterns_source'),
-      $container->get('module_handler')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function defineOptions() {
-    $options = parent::defineOptions();
-    $options['hide_empty'] = ['default' => FALSE];
-    $options['default_field_elements'] = ['default' => FALSE];
-    return $options;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
-    parent::buildOptionsForm($form, $form_state);
-
-    $form['default_field_elements'] = [
-      '#type' => 'checkbox',
-      '#title' => $this->t('Provide default field wrapper elements'),
-      '#default_value' => $this->options['default_field_elements'],
-      '#description' => $this->t('If not checked, fields that are not configured to customize their HTML elements will get no wrappers at all for their field, label and field + label wrappers. You can use this to quickly reduce the amount of markup the view provides by default, at the cost of making it more difficult to apply CSS.'),
-    ];
-
-    $form['hide_empty'] = [
-      '#type' => 'checkbox',
-      '#title' => $this->t('Hide empty fields'),
-      '#default_value' => $this->options['hide_empty'],
-      '#description' => $this->t('Do not display fields, labels or markup for fields that are empty.'),
-    ];
-
-    $context = ['view' => $this->view];
-    $this->buildPatternDisplayForm($form, 'views_row', $context, $this->options);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitOptionsForm(&$form, FormStateInterface $form_state) {
-    $settings = $form_state->getValue('row_options');
-    self::processFormStateValues($settings);
-    $form_state->setValue('row_options', $settings);
-  }
-
-  /**
-   * Helper function: check for all conditions that make a field visible.
-   *
-   * @param \Drupal\views\Plugin\views\field\FieldPluginBase $field
-   *   Field object.
-   * @param \Drupal\Component\Render\MarkupInterface|null $field_output
-   *   Field output.
-   *
-   * @return bool
-   *   TRUE if a field should be visible, FALSE otherwise.
-   *
-   * @see template_preprocess_pattern_views_row()
-   */
-  public function isFieldVisible(FieldPluginBase $field, $field_output) {
-    $empty_value = $field->isValueEmpty($field_output, $field->options['empty_zero']);
-    $hide_field = !$empty_value || (empty($field->options['hide_empty']) && empty($this->options['hide_empty']));
-    $empty = empty($field->options['exclude']) && $hide_field;
-    return $empty && $this->hasMappingDestination('views_row', $field->field, $this->options);
-  }
-
-}
diff --git a/modules/ui_patterns_views/src/Plugin/views/style/Component.php b/modules/ui_patterns_views/src/Plugin/views/style/Component.php
new file mode 100755
index 00000000..64e24058
--- /dev/null
+++ b/modules/ui_patterns_views/src/Plugin/views/style/Component.php
@@ -0,0 +1,26 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns_views\Plugin\views\style;
+
+use Drupal\ui_patterns\Form\UiPatternsFormBuilderTrait;
+use Drupal\views\Plugin\views\style\StylePluginBase;
+
+/**
+ * Style plugin to render items in a pattern field.
+ *
+ * @ingroup views_style_plugins
+ *
+ * @ViewsStyle(
+ *     id = "component",
+ *     title = @Translation("Component"),
+ *     help = @Translation("Displays views with UI components."),
+ *     theme = "view--pattern",
+ *     display_types = {"normal"}
+ * )
+ */
+class Component extends StylePluginBase {
+  use UiPatternsFormBuilderTrait;
+
+}
diff --git a/modules/ui_patterns_views/templates/pattern-views-row.html.twig b/modules/ui_patterns_views/templates/pattern-views-row.html.twig
deleted file mode 100644
index 0fbe3c40..00000000
--- a/modules/ui_patterns_views/templates/pattern-views-row.html.twig
+++ /dev/null
@@ -1,16 +0,0 @@
-{#
-/**
- * @file
- * Default UI Patterns Views row template.
- *
- * Available variables:
- * - view: The view in use.
- * - pattern: pattern element to be rendered.
- * - row: The raw result from the query, with all data it fetched.
- *
- * @see template_preprocess_pattern_views_row()
- *
- * @ingroup themeable
- */
-#}
-{{ pattern }}
diff --git a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/config/install/field.field.node.article.body.yml b/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/config/install/field.field.node.article.body.yml
deleted file mode 100644
index 8f3681d9..00000000
--- a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/config/install/field.field.node.article.body.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - field.storage.node.body
-    - node.type.article
-  module:
-    - text
-id: node.article.body
-field_name: body
-entity_type: node
-bundle: article
-label: Body
-description: ''
-required: false
-translatable: true
-default_value: {  }
-default_value_callback: ''
-settings:
-  display_summary: true
-field_type: text_with_summary
diff --git a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/config/install/node.type.article.yml b/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/config/install/node.type.article.yml
deleted file mode 100644
index f47f2add..00000000
--- a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/config/install/node.type.article.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-langcode: en
-status: true
-dependencies: {  }
-name: Article
-type: article
-description: ''
-help: ''
-new_revision: true
-preview_mode: 1
-display_submitted: true
diff --git a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/config/install/views.view.articles.yml b/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/config/install/views.view.articles.yml
deleted file mode 100644
index 59f8ca52..00000000
--- a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/config/install/views.view.articles.yml
+++ /dev/null
@@ -1,189 +0,0 @@
-langcode: en
-status: true
-dependencies:
-  config:
-    - node.type.article
-  module:
-    - node
-    - ui_patterns_views
-    - user
-id: articles
-label: Articles
-module: views
-description: ''
-tag: ''
-base_table: node_field_data
-base_field: nid
-core: 8.x
-core_version_requirement: ^8 || ^9
-display:
-  default:
-    display_plugin: default
-    id: default
-    display_title: Master
-    position: 0
-    display_options:
-      access:
-        type: perm
-        options:
-          perm: 'access content'
-      cache:
-        type: tag
-        options: {  }
-      query:
-        type: views_query
-        options:
-          disable_sql_rewrite: false
-          distinct: false
-          replica: false
-          query_comment: ''
-          query_tags: {  }
-      exposed_form:
-        type: basic
-        options:
-          submit_button: Apply
-          reset_button: false
-          reset_button_label: Reset
-          exposed_sorts_label: 'Sort by'
-          expose_sort_order: true
-          sort_asc_label: Asc
-          sort_desc_label: Desc
-      pager:
-        type: some
-        options:
-          items_per_page: 10
-          offset: 0
-      style:
-        type: default
-      row:
-        type: ui_patterns
-        options:
-          default_field_elements: 0
-          inline:
-            title: 0
-          separator: ''
-          hide_empty: 0
-          pattern: teaser
-          pattern_variant: default
-          pattern_mapping:
-            'views_row:title':
-              destination: title
-              weight: 0
-              plugin: views_row
-              source: title
-      fields:
-        title:
-          id: title
-          table: node_field_data
-          field: title
-          entity_type: node
-          entity_field: title
-          label: ''
-          alter:
-            alter_text: false
-            make_link: false
-            absolute: false
-            trim: false
-            word_boundary: false
-            ellipsis: false
-            strip_tags: false
-            html: false
-          hide_empty: false
-          empty_zero: false
-          settings:
-            link_to_entity: true
-          plugin_id: field
-          relationship: none
-          group_type: group
-          admin_label: ''
-          exclude: false
-          element_type: ''
-          element_class: ''
-          element_label_type: ''
-          element_label_class: ''
-          element_label_colon: true
-          element_wrapper_type: ''
-          element_wrapper_class: ''
-          element_default_classes: true
-          empty: ''
-          hide_alter_empty: true
-          click_sort_column: value
-          type: string
-          group_column: value
-          group_columns: {  }
-          group_rows: true
-          delta_limit: 0
-          delta_offset: 0
-          delta_reversed: false
-          delta_first_last: false
-          multi_type: separator
-          separator: ', '
-          field_api_classes: false
-      filters:
-        status:
-          value: '1'
-          table: node_field_data
-          field: status
-          plugin_id: boolean
-          entity_type: node
-          entity_field: status
-          id: status
-          expose:
-            operator: ''
-          group: 1
-        type:
-          id: type
-          table: node_field_data
-          field: type
-          value:
-            article: article
-          entity_type: node
-          entity_field: type
-          plugin_id: bundle
-      sorts:
-        created:
-          id: created
-          table: node_field_data
-          field: created
-          order: DESC
-          entity_type: node
-          entity_field: created
-          plugin_id: date
-          relationship: none
-          group_type: group
-          admin_label: ''
-          exposed: false
-          expose:
-            label: ''
-          granularity: second
-      title: Articles
-      header: {  }
-      footer: {  }
-      empty: {  }
-      relationships: {  }
-      arguments: {  }
-      display_extenders: {  }
-    cache_metadata:
-      max-age: -1
-      contexts:
-        - 'languages:language_content'
-        - 'languages:language_interface'
-        - 'user.node_grants:view'
-        - user.permissions
-      tags: {  }
-  page_1:
-    display_plugin: page
-    id: page_1
-    display_title: Page
-    position: 1
-    display_options:
-      display_extenders: {  }
-      path: articles
-    cache_metadata:
-      max-age: -1
-      contexts:
-        - 'languages:language_content'
-        - 'languages:language_interface'
-        - 'user.node_grants:view'
-        - user.permissions
-      tags: {  }
diff --git a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/templates/pattern-teaser.html.twig b/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/templates/pattern-teaser.html.twig
deleted file mode 100644
index 519b873c..00000000
--- a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/templates/pattern-teaser.html.twig
+++ /dev/null
@@ -1,8 +0,0 @@
-{#
-/**
- * @file
- * Test teaser pattern.
- */
-#}
-<h3>{{ title }}</h3>
-<p>{{ description }}</p>
\ No newline at end of file
diff --git a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/templates/teaser.ui_patterns.yml b/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/templates/teaser.ui_patterns.yml
deleted file mode 100644
index 86f7e8a5..00000000
--- a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/templates/teaser.ui_patterns.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-teaser:
-  label: "Teaser"
-  variants:
-    default:
-      label: "Default"
-    highlighted:
-      label: "Highlighted"
-  fields:
-    title:
-      type: "text"
-      label: "Title"
-      preview: "Title"
-    description:
-      type: "text"
-      label: "Description"
-      preview: "Description"
diff --git a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/ui_patterns_views_test.info.yml b/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/ui_patterns_views_test.info.yml
deleted file mode 100644
index e6954e14..00000000
--- a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/ui_patterns_views_test.info.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: 'UI Patterns Views Test'
-type: module
-description: 'Test module for UI Patterns.'
-core: 8.x
-core_version_requirement: ^8 || ^9
-hidden: true
-package: 'User interface'
-dependencies:
-- ui_patterns
-- ui_patterns_views
-- ui_patterns_library
-- views_ui
-- node
-- text
-
-config_devel:
-  install:
-  - field.field.node.article.body
-  - node.type.article
-  - views.view.articles
diff --git a/modules/ui_patterns_views/tests/src/FunctionalJavascript/UiPatternsViewsRenderTest.php b/modules/ui_patterns_views/tests/src/FunctionalJavascript/UiPatternsViewsRenderTest.php
deleted file mode 100644
index cea28e8f..00000000
--- a/modules/ui_patterns_views/tests/src/FunctionalJavascript/UiPatternsViewsRenderTest.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns_views\FunctionalJavascript;
-
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-use Drupal\Tests\ui_patterns\Traits\TwigDebugTrait;
-
-/**
- * Test Views pattern rendering.
- *
- * @group ui_patterns_views
- */
-class UiPatternsViewsRenderTest extends WebDriverTestBase {
-
-  use TwigDebugTrait;
-
-  /**
-   * Default theme.
-   *
-   * @var string
-   */
-  protected $defaultTheme = 'stark';
-
-  /**
-   * Disable schema validation when running tests.
-   *
-   * @var bool
-   *
-   * @todo Fix this by providing actual schema validation.
-   */
-  protected $strictConfigSchema = FALSE;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = [
-    'field',
-    'ui_patterns_views_test',
-  ];
-
-  /**
-   * Test that pattern field group settings are correctly saved.
-   */
-  public function testUiPatternsViewsRendering() {
-    $assert_session = $this->assertSession();
-
-    $this->enableTwigDebugMode();
-
-    $user = $this->drupalCreateUser([], NULL, TRUE);
-    $this->drupalLogin($user);
-
-    $this->drupalCreateNode([
-      'title' => 'Test article',
-      'type' => 'article',
-    ]);
-
-    $this->drupalGet('/articles');
-
-    // Assert correct variant suggestions.
-    $suggestions = [
-      'pattern-teaser--variant-default--views-row--articles--page-1.html.twig',
-      'pattern-teaser--variant-default--views-row--articles.html.twig',
-      'pattern-teaser--variant-default--views-row.html.twig',
-
-      'pattern-teaser--views-row--articles--page-1.html.twig',
-      'pattern-teaser--views-row--articles.html.twig',
-      'pattern-teaser--views-row.html.twig',
-
-      'pattern-teaser--variant-default.html.twig',
-      'pattern-teaser.html.twig',
-    ];
-    foreach ($suggestions as $suggestion) {
-      $assert_session->responseContains($suggestion);
-    }
-
-    // Test field content is rendered in field group pattern.
-    $assert_session->elementContains('css', 'h3', 'Test article');
-  }
-
-}
diff --git a/modules/ui_patterns_views/tests/src/FunctionalJavascript/UiPatternsViewsSettingsTest.php b/modules/ui_patterns_views/tests/src/FunctionalJavascript/UiPatternsViewsSettingsTest.php
deleted file mode 100644
index c0162029..00000000
--- a/modules/ui_patterns_views/tests/src/FunctionalJavascript/UiPatternsViewsSettingsTest.php
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns_views\FunctionalJavascript;
-
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-use Drupal\views\Entity\View;
-
-/**
- * Test that UI Patterns Views display formatter can be properly configured.
- *
- * @group ui_patterns_field_group
- */
-class UiPatternsViewsSettingsTest extends WebDriverTestBase {
-
-  /**
-   * Default theme.
-   *
-   * @var string
-   */
-  protected $defaultTheme = 'stark';
-
-  /**
-   * Disable schema validation when running tests.
-   *
-   * @var bool
-   *
-   * @todo Fix this by providing actual schema validation.
-   */
-  protected $strictConfigSchema = FALSE;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = [
-    'field',
-    'ui_patterns_views_test',
-  ];
-
-  /**
-   * Test that pattern field group settings are correctly saved.
-   */
-  public function testUiPatternsViewsSettings() {
-    $page = $this->getSession()->getPage();
-    $assert_session = $this->assertSession();
-
-    $user = $this->drupalCreateUser([], NULL, TRUE);
-    $this->drupalLogin($user);
-
-    // Visit Articles views setting page.
-    $this->drupalGet('/admin/structure/views/view/articles');
-
-    // Access row style settings.
-    $page->clickLink('Change settings for this style');
-    $assert_session->assertWaitOnAjaxRequest();
-
-    // Configure row style.
-    $page->selectFieldOption('Variant', 'Highlighted');
-    $page->selectFieldOption('Destination for Content: Title', 'Description');
-
-    // Submit row style settings.
-    $page->find('css', '.ui-dialog-buttonpane .form-actions')->pressButton('Apply');
-    $assert_session->assertWaitOnAjaxRequest();
-
-    // Save view.
-    $page->find('css', '#edit-actions')->pressButton('Save');
-
-    $view = View::load('articles');
-    $settings = $view->getDisplay('default')['display_options']['row']['options'];
-
-    // Assert settings values.
-    $this->assertEquals($settings['pattern'], 'teaser');
-    $this->assertEquals($settings['pattern_variant'], 'highlighted');
-
-    // Assert mappings.
-    $this->assertNotEmpty($settings['pattern_mapping'], "Pattern mapping is empty.");
-
-    $mapping = $settings['pattern_mapping'];
-    $this->assertArrayHasKey('views_row:title', $mapping, 'Mapping not found.');
-    $this->assertEquals($mapping['views_row:title']['destination'], 'description', "Mapping not valid.");
-  }
-
-}
diff --git a/modules/ui_patterns_views/ui_patterns_views.info.yml b/modules/ui_patterns_views/ui_patterns_views.info.yml
index f1297fdd..be4033ab 100644
--- a/modules/ui_patterns_views/ui_patterns_views.info.yml
+++ b/modules/ui_patterns_views/ui_patterns_views.info.yml
@@ -1,9 +1,8 @@
-name: UI Patterns Views
+name: "UI Patterns Views"
 type: module
-description: Use patterns as Views templates.
-core: 8.x
-core_version_requirement: ^8 || ^9
-package: User interface
+description: "Use UI components with Views rows and styles plugins."
+core_version_requirement: ^10
+package: "User interface"
 dependencies:
   - drupal:views
   - ui_patterns:ui_patterns
diff --git a/modules/ui_patterns_views/ui_patterns_views.module b/modules/ui_patterns_views/ui_patterns_views.module
deleted file mode 100644
index 58f0b042..00000000
--- a/modules/ui_patterns_views/ui_patterns_views.module
+++ /dev/null
@@ -1,113 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains module file.
- */
-
-use Drupal\ui_patterns\Element\PatternContext;
-
-/**
- * Implements hook_theme().
- */
-function ui_patterns_views_theme() {
-  return [
-    'pattern_views_row' => [
-      'variables' => ['view' => NULL, 'options' => [], 'row' => NULL],
-    ],
-  ];
-}
-
-/**
- * Preprocess hook.
- *
- * @param array $variables
- *   Theme variables.
- */
-function template_preprocess_pattern_views_row(array &$variables) {
-  /** @var \Drupal\views\ResultRow $row */
-  /** @var \Drupal\views\ViewExecutable $view */
-  /** @var \Drupal\ui_patterns_views\Plugin\views\row\Pattern $row_plugin */
-
-  $fields = [];
-  $view = $variables['view'];
-  $row_plugin = $view->rowPlugin;
-  $options = $variables['options'];
-  $row = $variables['row'];
-
-  foreach ($options['pattern_mapping'] as $mapping) {
-    $field_name = $mapping['source'];
-    $field = $view->field[$field_name];
-    $field_output = $view->style_plugin->getField($row->index, $field_name);
-    if ($row_plugin->isFieldVisible($field, $field_output)) {
-      $destination = $row_plugin->getMappingDestination('views_row', $field_name, $options);
-      $fields[$destination][] = $field_output;
-    }
-  }
-
-  $variables['pattern'] = [];
-  if ($view->preview && !isset($view->element['#embed'])) {
-    \Drupal::messenger()->addWarning(t("Pattern Views row plugin does not support preview."));
-    $variables['pattern'] = ['#type' => 'status_messages'];
-  }
-  elseif (!empty($fields)) {
-    $variables['pattern'] = [
-      '#type' => 'pattern',
-      '#id' => $options['pattern'],
-      '#fields' => $fields,
-      '#multiple_sources' => TRUE,
-      '#variant' => $options['pattern_variant'] ?? '',
-    ];
-
-    // Allow default context values to not override those exposed elsewhere.
-    $variables['pattern']['#context']['type'] = 'views_row';
-    $variables['pattern']['#context']['view_name'] = $view->storage->id();
-    $variables['pattern']['#context']['display'] = $view->current_display;
-    $variables['pattern']['#context']['view'] = $view;
-    $variables['pattern']['#context']['row'] = $row;
-  }
-}
-
-/**
- * Implements hook_ui_patterns_suggestions_alter().
- */
-function ui_patterns_views_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) {
-  if ($context->isOfType('views_row')) {
-    $hook = $variables['theme_hook_original'];
-    $variant = $variables["variant"] ?? '';
-    $view_name = $context->getProperty('view_name');
-    $display = $context->getProperty('display');
-
-    $suggestions[] = $hook . '__views_row';
-    $suggestions[] = $hook . '__views_row__' . $view_name;
-    $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $display;
-
-    if (!empty($variant)) {
-      $suggestions[] = $hook . '__variant_' . $variant . '__views_row';
-      $suggestions[] = $hook . '__variant_' . $variant . '__views_row__' . $view_name;
-      $suggestions[] = $hook . '__variant_' . $variant . '__views_row__' . $view_name . '__' . $display;
-    }
-  }
-}
-
-/**
- * Implements hook_ui_patterns_destination_suggestions_alter().
- */
-function ui_patterns_views_ui_patterns_destination_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) {
-  if ($context->isOfType('views_row')) {
-    $hook = $variables['theme_hook_original'];
-    $variant = $variables["variant"] ?? '';
-    $view_name = $context->getProperty('view_name');
-    $display = $context->getProperty('display');
-    $pattern = $context->getProperty('pattern');
-    $field = $context->getProperty('field');
-
-    $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $pattern . '__' . $field;
-    $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $display . '__' . $pattern . '__' . $field;
-
-    if (!empty($variant)) {
-      $suggestions[] = $hook . '__variant_' . $variant . '__views_row__' . $view_name . '__' . $pattern . '__' . $field;
-      $suggestions[] = $hook . '__variant_' . $variant . '__views_row__' . $view_name . '__' . $display . '__' . $pattern . '__' . $field;
-    }
-  }
-}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
deleted file mode 100644
index 5322204e..00000000
--- a/phpunit.xml.dist
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<phpunit bootstrap="${drupal.root}/core/tests/bootstrap.php" backupGlobals="true" colors="true" cacheResult="false">
-  <php>
-    <ini name="error_reporting" value="32767"/>
-    <ini name="memory_limit" value="-1"/>
-    <env name="SIMPLETEST_BASE_URL" value="${drupal.base_url}"/>
-    <env name="SIMPLETEST_IGNORE_DIRECTORIES" value="${drupal.root}"/>
-    <env name="SIMPLETEST_DB" value="mysql://${drupal.database.user}:${drupal.database.password}@${drupal.database.host}:${drupal.database.port}/${drupal.database.name}"/>
-    <env name="MINK_DRIVER_ARGS_WEBDRIVER" value='["${selenium.browser}", null, "${selenium.host}/wd/hub"]'/>
-  </php>
-  <testsuites>
-    <testsuite name="ui-patterns">
-      <directory>./tests/</directory>
-      <directory>./modules/*/tests/</directory>
-    </testsuite>
-  </testsuites>
-</phpunit>
diff --git a/runner.yml.dist b/runner.yml.dist
deleted file mode 100644
index 465d1e8c..00000000
--- a/runner.yml.dist
+++ /dev/null
@@ -1,34 +0,0 @@
-drupal:
-  root: "build"
-  base_url: "http://nginx"
-  database:
-    host: "mariadb"
-    port: "3306"
-    name: "drupal"
-    user: "drupal"
-    password: "drupal"
-  post_install:
-    - "./vendor/bin/drush en -y ui_patterns ui_patterns_library ui_patterns_ds ui_patterns_field_group ui_patterns_layouts ui_patterns_views"
-    - "./vendor/bin/drush en -y coffee config_devel"
-    - "./vendor/bin/drush en -y page_manager paragraphs panels"
-    - "./vendor/bin/drush cr"
-  settings:
-    settings:
-      file_scan_ignore_directories:
-        - "vendor"
-        - "${drupal.root}"
-
-selenium:
-  host: "http://selenium:4444"
-  browser: "chrome"
-
-commands:
-  drupal:site-setup:
-    - { task: "run", command: "drupal:symlink-project" }
-    # Generate settings.testing.php, it will be used when running functional tests.
-    - { task: "process-php", type: "write", config: "drupal.settings", source: "${drupal.root}/sites/default/default.settings.php", destination: "${drupal.root}/sites/default/settings.testing.php", override: true }
-    - { task: "run", command: "drupal:drush-setup" }
-    - { task: "run", command: "drupal:settings-setup" }
-    - { task: "run", command: "setup:phpunit" }
-  setup:phpunit:
-    - { task: "process", source: "phpunit.xml.dist", destination: "phpunit.xml" }
diff --git a/src/Annotation/PropType.php b/src/Annotation/PropType.php
new file mode 100644
index 00000000..14146257
--- /dev/null
+++ b/src/Annotation/PropType.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\ui_patterns\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines prop_type annotation object.
+ *
+ * @Annotation
+ */
+class PropType extends Plugin {
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The human-readable name of the plugin.
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   *
+   * @ingroup plugin_translatable
+   */
+  public $label;
+
+  /**
+   * The description of the plugin.
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   *
+   * @ingroup plugin_translatable
+   */
+  public $description;
+
+  /**
+   * The json schema of the plugin matches.
+   *
+   * @var array
+   */
+  public $schema;
+
+  /**
+   * The priority of the PropType.
+   *
+   * @var int
+   */
+  public int $priority;
+
+}
diff --git a/src/Annotation/Source.php b/src/Annotation/Source.php
new file mode 100644
index 00000000..e8a319f2
--- /dev/null
+++ b/src/Annotation/Source.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines source_provider annotation object.
+ *
+ * @Annotation
+ */
+final class Source extends Plugin {
+
+  /**
+   * The plugin ID.
+   */
+  public readonly string $id;
+
+  /**
+   * The human-readable name of the plugin.
+   *
+   * @ingroup plugin_translatable
+   */
+  public readonly string $title;
+
+  /**
+   * The description of the plugin.
+   *
+   * @ingroup plugin_translatable
+   */
+  public readonly string $description;
+
+  /**
+   * An array of prop types the source provider supports.
+   *
+   * @var array
+   */
+  public $prop_types = [];
+
+}
diff --git a/src/Annotation/UiPattern.php b/src/Annotation/UiPattern.php
deleted file mode 100644
index 203cce7a..00000000
--- a/src/Annotation/UiPattern.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Annotation;
-
-use Drupal\Component\Annotation\Plugin;
-
-/**
- * Defines a UI Patterns annotation object.
- *
- * @see plugin_api
- *
- * @Annotation
- */
-class UiPattern extends Plugin {
-
-  /**
-   * The plugin ID.
-   *
-   * @var string
-   */
-  public $id;
-
-  /**
-   * The label of the plugin.
-   *
-   * @var \Drupal\Core\Annotation\Translation
-   *
-   * @ingroup plugin_translatable
-   */
-  public $label;
-
-}
diff --git a/src/Annotation/UiPatternsSource.php b/src/Annotation/UiPatternsSource.php
deleted file mode 100644
index 1f02b88b..00000000
--- a/src/Annotation/UiPatternsSource.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Annotation;
-
-use Drupal\Component\Annotation\Plugin;
-
-/**
- * Defines a UI Patterns Source item annotation object.
- *
- * @see \Drupal\ui_patterns\UiPatternsSourceManager
- * @see plugin_api
- *
- * @Annotation
- */
-class UiPatternsSource extends Plugin {
-
-  /**
-   * The plugin ID.
-   *
-   * @var string
-   */
-  public $id;
-
-  /**
-   * The label of the plugin.
-   *
-   * @var \Drupal\Core\Annotation\Translation
-   *
-   * @ingroup plugin_translatable
-   */
-  public $label;
-
-  /**
-   * Module that must be enabled in order for the plugin to be discoverable.
-   *
-   * @var string
-   */
-  public $provider;
-
-  /**
-   * An array of tags specifying in which context the source plugin can be used.
-   *
-   * We should tackle this by using contexts but, until configuration entities
-   * will not be exposed as typed data, we will use tags instead.
-   *
-   * @var array
-   *
-   * @link https://www.drupal.org/node/1818574
-   */
-  public $tags = [];
-
-}
diff --git a/src/Definition/ArrayAccessDefinitionTrait.php b/src/Definition/ArrayAccessDefinitionTrait.php
deleted file mode 100644
index db733336..00000000
--- a/src/Definition/ArrayAccessDefinitionTrait.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Definition;
-
-/**
- * Helper trait implementing PHP array access.
- *
- * @property $definition
- *
- * @package Drupal\ui_patterns\Definition
- */
-trait ArrayAccessDefinitionTrait {
-
-  /**
-   * {@inheritdoc}
-   */
-  #[\ReturnTypeWillChange]
-  public function offsetExists($offset) {
-    return array_key_exists($offset, $this->definition);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  #[\ReturnTypeWillChange]
-  public function offsetGet($offset) {
-    return $this->definition[$offset] ?? NULL;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  #[\ReturnTypeWillChange]
-  public function offsetSet($offset, $value) {
-    $this->definition[$offset] = $value;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  #[\ReturnTypeWillChange]
-  public function offsetUnset($offset) {
-    unset($this->definition[$offset]);
-  }
-
-}
diff --git a/src/Definition/PatternDefinition.php b/src/Definition/PatternDefinition.php
deleted file mode 100644
index a9e279fb..00000000
--- a/src/Definition/PatternDefinition.php
+++ /dev/null
@@ -1,649 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Definition;
-
-use Drupal\Component\Plugin\Definition\DerivablePluginDefinitionInterface;
-use Drupal\Component\Plugin\Definition\PluginDefinition;
-
-/**
- * Pattern definition class.
- *
- * @package Drupal\ui_patterns\Definition
- */
-class PatternDefinition extends PluginDefinition implements DerivablePluginDefinitionInterface, \ArrayAccess {
-
-  use ArrayAccessDefinitionTrait;
-
-  /**
-   * Pattern prefix.
-   */
-  const PATTERN_PREFIX = 'pattern_';
-
-  /**
-   * Prefix for locally defined libraries.
-   */
-  const LIBRARY_PREFIX = 'ui_patterns';
-
-  /**
-   * Pattern definition.
-   *
-   * @var array
-   */
-  protected $definition = [
-    'id' => NULL,
-    'label' => NULL,
-    'description' => NULL,
-    'base path' => NULL,
-    'file name' => NULL,
-    'use' => NULL,
-    'theme hook' => NULL,
-    'custom theme hook' => FALSE,
-    'template' => NULL,
-    'libraries' => [],
-    'fields' => [],
-    'variants' => [],
-    'tags' => [],
-    'additional' => [],
-    'deriver' => NULL,
-    'provider' => NULL,
-    'class' => NULL,
-  ];
-
-  /**
-   * PatternDefinition constructor.
-   */
-  public function __construct(array $definition = []) {
-    foreach ($definition as $name => $value) {
-      if (array_key_exists($name, $this->definition)) {
-        $this->definition[$name] = $value;
-      }
-      else {
-        $this->definition['additional'][$name] = $value;
-      }
-    }
-
-    $this->id = $this->definition['id'];
-    $this->setFields($this->definition['fields']);
-    $this->setVariants($this->definition['variants']);
-    $this->setThemeHook(self::PATTERN_PREFIX . $this->id());
-
-    if (!empty($definition['theme hook'])) {
-      $this->setThemeHook($definition['theme hook']);
-      $this->definition['custom theme hook'] = TRUE;
-    }
-
-    if (!$this->hasTemplate()) {
-      $this->setTemplate(str_replace('_', '-', $this->getThemeHook()));
-    }
-  }
-
-  /**
-   * Return array definition.
-   *
-   * @return array
-   *   Array definition.
-   */
-  public function toArray() {
-    $definition = $this->definition;
-    foreach ($this->getFields() as $field) {
-      $definition['fields'][$field->getName()] = $field->toArray();
-    }
-    foreach ($this->getVariants() as $variant) {
-      $definition['variants'][$variant->getName()] = $variant->toArray();
-    }
-
-    return $definition;
-  }
-
-  /**
-   * Getter.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getLabel() {
-    return $this->definition['label'];
-  }
-
-  /**
-   * Setter.
-   *
-   * @param mixed $label
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setLabel($label) {
-    $this->definition['label'] = $label;
-    return $this;
-  }
-
-  /**
-   * Getter.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getBasePath() {
-    return $this->definition['base path'];
-  }
-
-  /**
-   * Setter.
-   *
-   * @param mixed $basePath
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setBasePath($basePath) {
-    $this->definition['base path'] = $basePath;
-    return $this;
-  }
-
-  /**
-   * Getter.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getFileName() {
-    return $this->definition['file name'];
-  }
-
-  /**
-   * Setter.
-   *
-   * @param mixed $fileName
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setFileName($fileName) {
-    $this->definition['file name'] = $fileName;
-    return $this;
-  }
-
-  /**
-   * Get Provider property.
-   *
-   * @return string
-   *   Property value.
-   */
-  public function getProvider() {
-    return $this->definition['provider'];
-  }
-
-  /**
-   * Setter.
-   *
-   * @param mixed $provider
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setProvider($provider) {
-    $this->definition['provider'] = $provider;
-    return $this;
-  }
-
-  /**
-   * Getter.
-   *
-   * @return PatternDefinitionField[]
-   *   Property value.
-   */
-  public function getFields() {
-    return $this->definition['fields'];
-  }
-
-  /**
-   * Get field as options.
-   *
-   * @return array
-   *   Fields as select options.
-   */
-  public function getFieldsAsOptions() {
-    $options = [];
-    foreach ($this->getFields() as $field) {
-      $options[$field->getName()] = $field->getLabel();
-    }
-    return $options;
-  }
-
-  /**
-   * Setter.
-   *
-   * @param array $fields
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setFields(array $fields) {
-    foreach ($fields as $name => $value) {
-      $field = $this->getFieldDefinition($name, $value);
-      $this->definition['fields'][$field->getName()] = $field;
-    }
-    return $this;
-  }
-
-  /**
-   * Check whereas pattern has variants.
-   *
-   * @return bool
-   *   Whereas pattern has variants.
-   */
-  public function hasVariants() {
-    return !empty($this->definition['variants']);
-  }
-
-  /**
-   * Getter.
-   *
-   * @return \Drupal\ui_patterns\Definition\PatternDefinitionVariant[]
-   *   Property value.
-   */
-  public function getVariants() {
-    return $this->definition['variants'];
-  }
-
-  /**
-   * Get field as options.
-   *
-   * @return array
-   *   Variants as select options.
-   */
-  public function getVariantsAsOptions() {
-    $options = [];
-    foreach ($this->getVariants() as $field) {
-      $options[$field->getName()] = $field->getLabel();
-    }
-    return $options;
-  }
-
-  /**
-   * Setter.
-   *
-   * @param array $variants
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setVariants(array $variants) {
-    foreach ($variants as $name => $value) {
-      $variant = $this->getVariantDefinition($name, $value);
-      $this->definition['variants'][$variant->getName()] = $variant;
-    }
-    return $this;
-  }
-
-  /**
-   * Get field.
-   *
-   * @param string $name
-   *   Field name.
-   *
-   * @return PatternDefinitionField|null
-   *   Definition field.
-   */
-  public function getField($name) {
-    return $this->hasField($name) ? $this->definition['fields'][$name] : NULL;
-  }
-
-  /**
-   * Check whereas field exists.
-   *
-   * @param string $name
-   *   Field name.
-   *
-   * @return bool
-   *   Whereas field exists
-   */
-  public function hasField($name) {
-    return isset($this->definition['fields'][$name]);
-  }
-
-  /**
-   * Set field.
-   *
-   * @param string $name
-   *   Field name.
-   * @param string $label
-   *   Field label.
-   *
-   * @return $this
-   */
-  public function setField($name, $label) {
-    $this->definition['fields'][$name] = $this->getFieldDefinition($name, $label);
-    return $this;
-  }
-
-  /**
-   * Get variant.
-   *
-   * @param string $name
-   *   Field name.
-   *
-   * @return PatternDefinitionField|null
-   *   Definition field.
-   */
-  public function getVariant($name) {
-    return $this->hasVariant($name) ? $this->definition['variants'][$name] : NULL;
-  }
-
-  /**
-   * Check whereas variant exists.
-   *
-   * @param string $name
-   *   Variant name.
-   *
-   * @return bool
-   *   Whereas variant exists
-   */
-  public function hasVariant($name) {
-    return isset($this->definition['variants'][$name]);
-  }
-
-  /**
-   * Set variant.
-   *
-   * @param string $name
-   *   Variant name.
-   * @param string $label
-   *   Variant label.
-   *
-   * @return $this
-   */
-  public function setVariant($name, $label) {
-    $this->definition['variants'][$name] = $this->getVariantDefinition($name, $label);
-    return $this;
-  }
-
-  /**
-   * Getter.
-   *
-   * @return string
-   *   Property value.
-   */
-  public function getThemeHook() {
-    return $this->definition['theme hook'];
-  }
-
-  /**
-   * Setter.
-   *
-   * @param string $theme_hook
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setThemeHook($theme_hook) {
-    $this->definition['theme hook'] = $theme_hook;
-    return $this;
-  }
-
-  /**
-   * Getter.
-   *
-   * @return string
-   *   Property value.
-   */
-  public function getDescription() {
-    return $this->definition['description'];
-  }
-
-  /**
-   * Setter.
-   *
-   * @param string $description
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setDescription($description) {
-    $this->definition['description'] = $description;
-    return $this;
-  }
-
-  /**
-   * Getter.
-   *
-   * @return bool
-   *   Whereas definition uses the "use:" property.
-   */
-  public function hasUse() {
-    return !empty($this->definition['use']);
-  }
-
-  /**
-   * Getter.
-   *
-   * @return string
-   *   Property value.
-   */
-  public function getUse() {
-    return $this->definition['use'];
-  }
-
-  /**
-   * Setter.
-   *
-   * @param string $use
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setUse($use) {
-    $this->definition['use'] = $use;
-    return $this;
-  }
-
-  /**
-   * Getter.
-   *
-   * @return array
-   *   Property value.
-   */
-  public function getTags() {
-    return $this->definition['tags'];
-  }
-
-  /**
-   * Setter.
-   *
-   * @param array $tags
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setTags(array $tags) {
-    $this->definition['tags'] = $tags;
-    return $this;
-  }
-
-  /**
-   * Getter.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function hasCustomThemeHook() {
-    return $this->definition['custom theme hook'];
-  }
-
-  /**
-   * Getter.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getTemplate() {
-    return $this->definition['template'];
-  }
-
-  /**
-   * Setter.
-   *
-   * @param mixed $template
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setTemplate($template) {
-    $this->definition['template'] = $template;
-    return $this;
-  }
-
-  /**
-   * Getter.
-   *
-   * @return bool
-   *   Whereas has template.
-   */
-  public function hasTemplate() {
-    return !empty($this->definition['template']);
-  }
-
-  /**
-   * Getter.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getLibraries() {
-    return $this->definition['libraries'];
-  }
-
-  /**
-   * Getter.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getLibrariesNames() {
-    $libraries = [];
-    foreach ($this->getLibraries() as $library) {
-      if (is_array($library)) {
-        $libraries[] = self::LIBRARY_PREFIX . '/' . $this->id() . '.' . key($library);
-      }
-      else {
-        $libraries[] = $library;
-      }
-    }
-    return $libraries;
-  }
-
-  /**
-   * Setter.
-   *
-   * @param mixed $libraries
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setLibraries($libraries) {
-    $this->definition['libraries'] = $libraries;
-    return $this;
-  }
-
-  /**
-   * Get Deriver property.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getDeriver() {
-    return $this->definition['deriver'];
-  }
-
-  /**
-   * Get Additional property.
-   *
-   * @return array
-   *   Property value.
-   */
-  public function getAdditional() {
-    return $this->definition['additional'];
-  }
-
-  /**
-   * Get Class property.
-   *
-   * @return string
-   *   Property value.
-   */
-  public function getClass() {
-    return $this->definition['class'];
-  }
-
-  /**
-   * Set Class property.
-   *
-   * @param string $class
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setClass($class) {
-    parent::setClass($class);
-    $this->definition['class'] = $class;
-    return $this;
-  }
-
-  /**
-   * Set Additional property.
-   *
-   * @param array $additional
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setAdditional(array $additional) {
-    $this->definition['additional'] = $additional;
-    return $this;
-  }
-
-  /**
-   * Set Deriver property.
-   *
-   * @param mixed $deriver
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setDeriver($deriver) {
-    $this->definition['deriver'] = $deriver;
-    return $this;
-  }
-
-  /**
-   * Factory method: create a new field definition.
-   *
-   * @param string $name
-   *   Field name.
-   * @param string $value
-   *   Field value.
-   *
-   * @return \Drupal\ui_patterns\Definition\PatternDefinitionField
-   *   Definition instance.
-   */
-  public function getFieldDefinition($name, $value) {
-    return new PatternDefinitionField($name, $value);
-  }
-
-  /**
-   * Factory method: create a new variant definition.
-   *
-   * @param string $name
-   *   Variant name.
-   * @param string $value
-   *   Variant value.
-   *
-   * @return \Drupal\ui_patterns\Definition\PatternDefinitionVariant
-   *   Definition instance.
-   */
-  public function getVariantDefinition($name, $value) {
-    return new PatternDefinitionVariant($name, $value);
-  }
-
-}
diff --git a/src/Definition/PatternDefinitionField.php b/src/Definition/PatternDefinitionField.php
deleted file mode 100644
index 75183b4a..00000000
--- a/src/Definition/PatternDefinitionField.php
+++ /dev/null
@@ -1,165 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Definition;
-
-/**
- * Definition class for a pattern field.
- *
- * @package Drupal\ui_patterns\Definition
- */
-class PatternDefinitionField implements \ArrayAccess {
-
-  use ArrayAccessDefinitionTrait;
-
-  /**
-   * Default field values.
-   *
-   * @var array
-   */
-  protected $definition = [
-    'name' => NULL,
-    'label' => NULL,
-    'description' => NULL,
-    'type' => NULL,
-    'preview' => NULL,
-    'escape' => TRUE,
-  ];
-
-  /**
-   * PatternDefinitionField constructor.
-   */
-  public function __construct($name, $value) {
-    if (is_scalar($value)) {
-      $this->definition['name'] = is_numeric($name) ? $value : $name;
-      $this->definition['label'] = $value;
-    }
-    else {
-      $this->definition['name'] = !isset($value['name']) ? $name : $value['name'];
-      $this->definition['label'] = $value['label'];
-      $this->definition = $value + $this->definition;
-    }
-  }
-
-  /**
-   * Return array definition.
-   *
-   * @return array
-   *   Array definition.
-   */
-  public function toArray() {
-    return $this->definition;
-  }
-
-  /**
-   * Get Name property.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getName() {
-    return $this->definition['name'];
-  }
-
-  /**
-   * Get Label property.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getLabel() {
-    return $this->definition['label'];
-  }
-
-  /**
-   * Get Description property.
-   *
-   * @return string
-   *   Property value.
-   */
-  public function getDescription() {
-    return $this->definition['description'];
-  }
-
-  /**
-   * Set Description property.
-   *
-   * @param string $description
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setDescription($description) {
-    $this->definition['description'] = $description;
-    return $this;
-  }
-
-  /**
-   * Get Type property.
-   *
-   * @return string
-   *   Property value.
-   */
-  public function getType() {
-    return $this->definition['type'];
-  }
-
-  /**
-   * Set Type property.
-   *
-   * @param string $type
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setType($type) {
-    $this->definition['type'] = $type;
-    return $this;
-  }
-
-  /**
-   * Get Preview property.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getPreview() {
-    return $this->definition['preview'];
-  }
-
-  /**
-   * Set Preview property.
-   *
-   * @param mixed $preview
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setPreview($preview) {
-    $this->definition['preview'] = $preview;
-    return $this;
-  }
-
-  /**
-   * Get Escape property.
-   *
-   * @return bool
-   *   Property value.
-   */
-  public function getEscape() {
-    return $this->definition['escape'];
-  }
-
-  /**
-   * Set Escape property.
-   *
-   * @param bool $escape
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setEscape($escape) {
-    $this->definition['escape'] = $escape;
-    return $this;
-  }
-
-}
diff --git a/src/Definition/PatternDefinitionVariant.php b/src/Definition/PatternDefinitionVariant.php
deleted file mode 100644
index b17cabda..00000000
--- a/src/Definition/PatternDefinitionVariant.php
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Definition;
-
-/**
- * Definition class for a pattern variant.
- *
- * @package Drupal\ui_patterns\Definition
- */
-class PatternDefinitionVariant implements \ArrayAccess {
-
-  use ArrayAccessDefinitionTrait;
-
-  /**
-   * Default field values.
-   *
-   * @var array
-   */
-  protected $definition = [
-    'name' => NULL,
-    'label' => NULL,
-    'description' => NULL,
-  ];
-
-  /**
-   * PatternDefinitionVariant constructor.
-   */
-  public function __construct($name, $value) {
-    if (is_scalar($value)) {
-      $this->definition['name'] = is_numeric($name) ? $value : $name;
-      $this->definition['label'] = $value;
-    }
-    else {
-      $this->definition['name'] = !isset($value['name']) ? $name : $value['name'];
-      $this->definition['label'] = $value['label'];
-      $this->definition = $value + $this->definition;
-    }
-  }
-
-  /**
-   * Return array definition.
-   *
-   * @return array
-   *   Array definition.
-   */
-  public function toArray() {
-    return $this->definition;
-  }
-
-  /**
-   * Get Name property.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getName() {
-    return $this->definition['name'];
-  }
-
-  /**
-   * Get Label property.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getLabel() {
-    return $this->definition['label'];
-  }
-
-  /**
-   * Get Description property.
-   *
-   * @return string
-   *   Property value.
-   */
-  public function getDescription() {
-    return $this->definition['description'];
-  }
-
-  /**
-   * Set Description property.
-   *
-   * @param string $description
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setDescription($description) {
-    $this->definition['description'] = $description;
-    return $this;
-  }
-
-}
diff --git a/src/Definition/PatternSourceField.php b/src/Definition/PatternSourceField.php
deleted file mode 100644
index 16cfd825..00000000
--- a/src/Definition/PatternSourceField.php
+++ /dev/null
@@ -1,154 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Definition;
-
-/**
- * Pattern source field class.
- *
- * @package Drupal\ui_patterns\Definition
- */
-class PatternSourceField {
-
-  const FIELD_KEY_SEPARATOR = ':';
-
-  /**
-   * Field name.
-   *
-   * @var string
-   */
-  private $fieldName;
-
-  /**
-   * Field label.
-   *
-   * @var string
-   */
-  private $fieldLabel;
-
-  /**
-   * Plugin ID.
-   *
-   * @var string
-   */
-  private $pluginId;
-
-  /**
-   * Plugin label.
-   *
-   * @var string
-   */
-  private $pluginLabel;
-
-  /**
-   * SourceField constructor.
-   */
-  public function __construct($field_name, $field_label, $plugin_id, $plugin_label) {
-    $this->fieldName = $field_name;
-    $this->fieldLabel = $field_label;
-    $this->pluginId = $plugin_id;
-    $this->pluginLabel = $plugin_label;
-  }
-
-  /**
-   * Get FieldName property.
-   *
-   * @return string
-   *   Property value.
-   */
-  public function getFieldName() {
-    return $this->fieldName;
-  }
-
-  /**
-   * Set FieldName property.
-   *
-   * @param string $fieldName
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setFieldName($fieldName) {
-    $this->fieldName = $fieldName;
-    return $this;
-  }
-
-  /**
-   * Get FieldLabel property.
-   *
-   * @return string
-   *   Property value.
-   */
-  public function getFieldLabel() {
-    return $this->fieldLabel;
-  }
-
-  /**
-   * Set FieldLabel property.
-   *
-   * @param string $fieldLabel
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setFieldLabel($fieldLabel) {
-    $this->fieldLabel = $fieldLabel;
-    return $this;
-  }
-
-  /**
-   * Get Plugin property.
-   *
-   * @return string
-   *   Property value.
-   */
-  public function getPluginId() {
-    return $this->pluginId;
-  }
-
-  /**
-   * Set Plugin property.
-   *
-   * @param string $pluginId
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setPluginId($pluginId) {
-    $this->pluginId = $pluginId;
-    return $this;
-  }
-
-  /**
-   * Get PluginLabel property.
-   *
-   * @return string
-   *   Property value.
-   */
-  public function getPluginLabel() {
-    return $this->pluginLabel;
-  }
-
-  /**
-   * Set PluginLabel property.
-   *
-   * @param string $pluginLabel
-   *   Property value.
-   *
-   * @return $this
-   */
-  public function setPluginLabel($pluginLabel) {
-    $this->pluginLabel = $pluginLabel;
-    return $this;
-  }
-
-  /**
-   * Get unique field key.
-   *
-   * @return string
-   *   Field key.
-   */
-  public function getFieldKey() {
-    return $this->getPluginId() . self::FIELD_KEY_SEPARATOR . $this->getFieldName();
-  }
-
-}
diff --git a/src/Element/ComponentElement.php b/src/Element/ComponentElement.php
new file mode 100644
index 00000000..11971dc4
--- /dev/null
+++ b/src/Element/ComponentElement.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Drupal\ui_patterns\Element;
+
+use Drupal\sdc\Element\ComponentElement as SdcComponentElement;
+use Drupal\sdc\Utilities;
+
+/**
+ * Override Drupal\sdc\Element\ComponentElement.
+ *
+ * @RenderElement("component")
+ */
+class ComponentElement extends SdcComponentElement {
+
+  /**
+   * {@inheritdoc}
+   *
+   * Duplicated because generateComponentTemplate() is private.
+   */
+  public function preRenderComponent(array $element): array {
+    $props = $element['#props'];
+    $props_alter_callbacks = $element['#propsAlter'];
+    // This callback can be used to prepare the context. For instance to replace
+    // tokens in the props.
+    $props = array_reduce(
+      $props_alter_callbacks,
+      fn(array $carry, callable $callback) => $this->doTrustedCallback(
+        $callback,
+        [$carry],
+        '%s is not trusted',
+      ),
+      $props
+    );
+    $inline_template = $this->generateComponentTemplate(
+      $element['#component'],
+      $element['#slots'],
+      $element['#slotsAlter'],
+      $props,
+    );
+    $element['inline-template'] = [
+      '#type' => 'inline_template',
+      '#template' => $inline_template,
+      '#context' => $props,
+    ];
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * Related SDC issue: https://www.drupal.org/project/drupal/issues/3391702
+   */
+  private function generateComponentTemplate(
+    string $id,
+    array $slots,
+    array $slots_alter_callbacks,
+    array &$context,
+  ): string {
+    $template = '{# This template was dynamically generated by sdc #}' . PHP_EOL;
+    $template .= sprintf('{%% embed \'%s\' %%}', $id);
+    $template .= PHP_EOL;
+    foreach ($slots as $slot_name => $slot_value) {
+      if (!Utilities::isRenderArray($slot_value) && \is_scalar($slot_value)) {
+        $slot_value = [
+          "#plain_text" => (string) $slot_value,
+        ];
+      }
+      $context[$slot_name] = array_reduce(
+        $slots_alter_callbacks,
+        fn(array $carry, callable $callback) => $this->doTrustedCallback(
+          $callback,
+          [$carry, $context],
+          '%s is not trusted',
+        ),
+        $slot_value
+      );
+      $template .= "  {% block $slot_name %}" . PHP_EOL
+        . "    {{ $slot_name }}" . PHP_EOL
+        . "  {% endblock %}" . PHP_EOL;
+    }
+    $template .= '{% endembed %}' . PHP_EOL;
+    return $template;
+  }
+
+}
diff --git a/src/Element/Pattern.php b/src/Element/Pattern.php
deleted file mode 100644
index e04ea99a..00000000
--- a/src/Element/Pattern.php
+++ /dev/null
@@ -1,238 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Element;
-
-use Drupal\Core\Render\Element\RenderElement;
-use Drupal\Core\Template\Attribute;
-use Drupal\ui_patterns\UiPatterns;
-
-/**
- * Renders a pattern element.
- *
- * @RenderElement("pattern")
- */
-class Pattern extends RenderElement {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getInfo() {
-    $class = get_class($this);
-    return [
-      '#input' => FALSE,
-      '#multiple_sources' => FALSE,
-      '#pre_render' => [
-        [$class, 'processContext'],
-        [$class, 'processRenderArray'],
-        [$class, 'processLibraries'],
-        [$class, 'processMultipleSources'],
-        [$class, 'processFields'],
-        [$class, 'ensureVariant'],
-        [$class, 'processUse'],
-      ],
-    ];
-  }
-
-  /**
-   * Process render array.
-   *
-   * @param array $element
-   *   Render array.
-   *
-   * @return array
-   *   Render array.
-   */
-  public static function processRenderArray(array $element) {
-    $element['#theme'] = UiPatterns::getPatternDefinition($element['#id'])->getThemeHook();
-
-    if (isset($element['#attributes']) && !empty($element['#attributes']) && is_array($element['#attributes'])) {
-      $element['#attributes'] = new Attribute($element['#attributes']);
-    }
-    else {
-      $element['#attributes'] = new Attribute();
-    }
-
-    unset($element['#type']);
-    return $element;
-  }
-
-  /**
-   * Process libraries.
-   *
-   * @param array $element
-   *   Render array.
-   *
-   * @return array
-   *   Render array.
-   */
-  public static function processLibraries(array $element) {
-    foreach (UiPatterns::getPatternDefinition($element['#id'])->getLibrariesNames() as $library) {
-      $element['#attached']['library'][] = $library;
-    }
-
-    return $element;
-  }
-
-  /**
-   * Process fields.
-   *
-   * @param array $element
-   *   Render array.
-   *
-   * @return array
-   *   Render array.
-   */
-  public static function processFields(array $element) {
-    // Make sure we don't render anything in case fields are empty.
-    if (self::hasFields($element)) {
-      $fields = $element['#fields'];
-      unset($element['#fields']);
-
-      foreach ($fields as $name => $field) {
-        $key = '#' . $name;
-        $element[$key] = $field;
-      }
-    }
-    else {
-      $element['#markup'] = '';
-    }
-
-    return $element;
-  }
-
-  /**
-   * Make sure that we never pass through a value that is not a string.
-   *
-   * This would prevent accidental assignments of a render array as variant
-   * which would break hook_ui_patterns_suggestions_alter().
-   *
-   * @param array $element
-   *   Render array.
-   *
-   * @return array
-   *   Render array.
-   */
-  public static function ensureVariant(array $element) {
-    if (!isset($element['#variant']) || !is_string($element['#variant'])) {
-      $element['#variant'] = '';
-    }
-
-    return $element;
-  }
-
-  /**
-   * Process use property.
-   *
-   * @param array $element
-   *   Render array.
-   *
-   * @return array
-   *   Render array.
-   */
-  public static function processUse(array $element) {
-    $definition = UiPatterns::getPatternDefinition($element['#id']);
-    if ($definition->hasUse()) {
-      $element['#use'] = $definition->getUse();
-    }
-
-    return $element;
-  }
-
-  /**
-   * Process fields.
-   *
-   * @param array $element
-   *   Render array.
-   *
-   * @return array
-   *   Render array.
-   */
-  public static function processMultipleSources(array $element) {
-    // Make sure we don't render anything in case fields are empty.
-    if (self::hasFields($element) && self::hasMultipleSources($element)) {
-      foreach ($element['#fields'] as $name => $field) {
-        // This guarantees backward compatibility: single sources be simple.
-        $element['#fields'][$name] = reset($field);
-        if (count($field) > 1) {
-          /** @var \Drupal\ui_patterns\Element\PatternContext $context */
-          $context = $element['#context'];
-          $context->setProperty('pattern', $element['#id']);
-          $context->setProperty('field', $name);
-
-          // Render multiple sources with "patterns_destination" template.
-          $element['#fields'][$name] = [
-            '#sources' => $field,
-            '#context' => $context,
-            '#theme' => 'patterns_destination',
-          ];
-        }
-      }
-    }
-    return $element;
-  }
-
-  /**
-   * Process context.
-   *
-   * @param array $element
-   *   Render array.
-   *
-   * @return array
-   *   Render array.
-   *
-   * @throws \Drupal\ui_patterns\Exception\PatternRenderException
-   *    Throws an exception if no context type is specified.
-   */
-  public static function processContext(array $element) {
-
-    if (self::hasValidContext($element)) {
-      $context = $element['#context'];
-      $element['#context'] = new PatternContext($context['type'], $element['#context']);
-    }
-    else {
-      $element['#context'] = new PatternContext('empty');
-    }
-
-    return $element;
-  }
-
-  /**
-   * Whereas pattern has field or not.
-   *
-   * @param array $element
-   *   Render array.
-   *
-   * @return bool
-   *   TRUE or FALSE.
-   */
-  public static function hasFields(array $element) {
-    return isset($element['#fields']) && !empty($element['#fields']) && is_array($element['#fields']);
-  }
-
-  /**
-   * Whereas pattern fields can accept multiple sources.
-   *
-   * @param array $element
-   *   Render array.
-   *
-   * @return bool
-   *   TRUE or FALSE.
-   */
-  public static function hasMultipleSources(array $element) {
-    return isset($element['#multiple_sources']) && $element['#multiple_sources'] === TRUE;
-  }
-
-  /**
-   * Whereas pattern has a valid context, i.e. context "type" is set.
-   *
-   * @param array $element
-   *   Render array.
-   *
-   * @return bool
-   *   TRUE or FALSE.
-   */
-  public static function hasValidContext(array $element) {
-    return isset($element['#context']) && is_array($element['#context']) && !empty($element['#context']['type']);
-  }
-
-}
diff --git a/src/Element/PatternContext.php b/src/Element/PatternContext.php
deleted file mode 100644
index aa987501..00000000
--- a/src/Element/PatternContext.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Element;
-
-/**
- * Represent the context in which the pattern is being rendered.
- *
- * @package Drupal\ui_patterns\Context
- */
-class PatternContext {
-
-  /**
-   * Pattern context type.
-   *
-   * @var string
-   */
-  protected $type = '';
-
-  /**
-   * Context properties.
-   *
-   * @var array
-   */
-  protected $properties = [];
-
-  /**
-   * PatternContext constructor.
-   *
-   * @param string $type
-   *   Pattern context type.
-   * @param array $values
-   *   Initial context values.
-   */
-  public function __construct($type, array $values = []) {
-    $this->type = $type;
-    unset($values['type']);
-    foreach ($values as $name => $value) {
-      $this->setProperty($name, $value);
-    }
-  }
-
-  /**
-   * Get pattern context property.
-   *
-   * @return mixed
-   *   Property value.
-   */
-  public function getProperty($name) {
-    return $this->properties[$name] ?? NULL;
-  }
-
-  /**
-   * Set pattern context property.
-   *
-   * @param string $name
-   *   Property name.
-   * @param mixed $value
-   *   Property value.
-   */
-  public function setProperty($name, $value) {
-    $this->properties[$name] = $value;
-  }
-
-  /**
-   * Check whereas the current context is of a given type.
-   *
-   * @param string $type
-   *   Type string.
-   *
-   * @return bool
-   *   Whereas the current context is of a given type.
-   */
-  public function isOfType($type) {
-    return $this->type == $type;
-  }
-
-  /**
-   * Get context type.
-   *
-   * @return string
-   *   Context type.
-   */
-  public function getType() {
-    return $this->type;
-  }
-
-}
diff --git a/src/Element/PatternPreview.php b/src/Element/PatternPreview.php
deleted file mode 100644
index cbd15d9e..00000000
--- a/src/Element/PatternPreview.php
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Element;
-
-use Drupal\Core\Render\Markup;
-use Drupal\ui_patterns\UiPatterns;
-
-/**
- * Renders a pattern preview element.
- *
- * @RenderElement("pattern_preview")
- */
-class PatternPreview extends Pattern {
-
-  /**
-   * Process fields.
-   *
-   * @param array $element
-   *   Render array.
-   *
-   * @return array
-   *   Render array.
-   */
-  public static function processFields(array $element) {
-    $definition = UiPatterns::getPatternDefinition($element['#id']);
-
-    $fields = [];
-    foreach ($definition->getFields() as $field) {
-      $preview = $field->getPreview();
-      // Some fields are used as Twig array keys and don't need escaping.
-      if ($field->getEscape()) {
-        // The examples are not user submitted and are safe markup.
-        $preview = self::getPreviewMarkup($preview);
-      }
-
-      $fields[$field->getName()] = $preview;
-    }
-
-    if (isset($definition['additional']['attributes'])) {
-      $fields['attributes'] = $definition['extra']['attributes'];
-    }
-    $element['#fields'] = $fields;
-
-    return parent::processFields($element);
-  }
-
-  /**
-   * Make previews markup safe.
-   *
-   * @param string|string[] $preview
-   *   The preview, may be a string or an array.
-   *
-   * @return array|\Drupal\Component\Render\MarkupInterface|string
-   *   Preview safe markup.
-   */
-  public static function getPreviewMarkup($preview) {
-    if (is_array($preview)) {
-      $rendered = [];
-      // If preview is a render array add hashes to keys.
-      $hash_keys = array_key_exists('theme', $preview) || array_key_exists('type', $preview);
-      foreach ($preview as $key => $value) {
-        $key = $hash_keys ? '#' . $key : $key;
-        if (is_array($value)) {
-          // Process array values recursively.
-          $value = self::getPreviewMarkup($value);
-        }
-        $rendered[$key] = $value;
-      }
-
-      return $rendered;
-    }
-
-    return Markup::create($preview);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function processContext(array $element) {
-    $element['#context'] = new PatternContext('preview');
-
-    return $element;
-  }
-
-}
diff --git a/src/Exception/PatternDefinitionException.php b/src/Exception/PatternDefinitionException.php
deleted file mode 100644
index 33f77978..00000000
--- a/src/Exception/PatternDefinitionException.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Exception;
-
-use Drupal\Component\Plugin\Exception\PluginException;
-
-/**
- * Exception thrown in case ofan invalid pattern definition.
- *
- * @package Drupal\ui_patterns\Exception
- */
-class PatternDefinitionException extends PluginException {
-
-}
diff --git a/src/Exception/PatternRenderException.php b/src/Exception/PatternRenderException.php
deleted file mode 100644
index 18e7cbba..00000000
--- a/src/Exception/PatternRenderException.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Exception;
-
-/**
- * Exception thrown in case of an invalid pattern rendering.
- *
- * @package Drupal\ui_patterns\Exception
- */
-class PatternRenderException extends \Exception {
-
-}
diff --git a/src/Form/PatternDisplayFormTrait.php b/src/Form/PatternDisplayFormTrait.php
deleted file mode 100644
index 91a53864..00000000
--- a/src/Form/PatternDisplayFormTrait.php
+++ /dev/null
@@ -1,248 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Form;
-
-use Drupal\Component\Utility\SortArray;
-use Drupal\ui_patterns\Plugin\PatternSourceBase;
-
-/**
- * Helper trait dealing with pattern display forms.
- *
- * @property \Drupal\ui_patterns\UiPatternsManager $patternsManager
- * @property \Drupal\ui_patterns\UiPatternsSourceManager $sourceManager
- * @property \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
- * @method \Drupal\Core\StringTranslation\TranslatableMarkup t($string, array $args = [], array $options = [])
- *
- * @package Drupal\ui_patterns\Form
- */
-trait PatternDisplayFormTrait {
-
-  /**
-   * Build pattern display form.
-   *
-   * @param array $form
-   *   Form array.
-   * @param string $tag
-   *   Source field tag.
-   * @param array $context
-   *   Plugin context.
-   * @param array $configuration
-   *   Default configuration coming form the host form.
-   */
-  public function buildPatternDisplayForm(array &$form, $tag, array $context, array $configuration) {
-
-    $form['pattern'] = [
-      '#type' => 'select',
-      '#empty_value' => '_none',
-      '#title' => $this->t('Pattern'),
-      '#options' => $this->patternsManager->getPatternsOptions(),
-      '#default_value' => $configuration['pattern'] ?? NULL,
-      '#required' => TRUE,
-      '#attributes' => ['id' => 'patterns-select'],
-    ];
-    $form['variants'] = ['#type' => 'container'];
-
-    /** @var \Drupal\ui_patterns\Definition\PatternDefinition $definition */
-    foreach ($this->patternsManager->getDefinitions() as $pattern_id => $definition) {
-      if ($definition->hasVariants()) {
-        $form['variants'][$pattern_id] = [
-          '#type' => 'select',
-          '#title' => $this->t('Variant'),
-          '#options' => $definition->getVariantsAsOptions(),
-          '#default_value' => $configuration['pattern_variant'] ?? NULL,
-          '#weight' => 0,
-          '#states' => [
-            'visible' => [
-              'select[id="patterns-select"]' => ['value' => $pattern_id],
-            ],
-          ],
-        ];
-      }
-      $form['pattern_mapping'][$pattern_id] = [
-        '#type' => 'container',
-        '#weight' => 1,
-        '#states' => [
-          'visible' => [
-            'select[id="patterns-select"]' => ['value' => $pattern_id],
-          ],
-        ],
-        'settings' => $this->getMappingForm($pattern_id, $tag, $context, $configuration),
-      ];
-    }
-
-    $this->moduleHandler->alter('ui_patterns_display_settings_form', $form, $configuration);
-  }
-
-  /**
-   * Get mapping form.
-   *
-   * @param string $pattern_id
-   *   Pattern ID for which to print the mapping form for.
-   * @param string $tag
-   *   Source field plugin tag.
-   * @param array $context
-   *   Plugin context.
-   * @param array $configuration
-   *   Default configuration coming form the host form.
-   *
-   * @return array
-   *   Mapping form.
-   */
-  public function getMappingForm($pattern_id, $tag, array $context, array $configuration) {
-    /** @var \Drupal\ui_patterns\Definition\PatternDefinition $pattern */
-    $pattern = $this->patternsManager->getDefinition($pattern_id);
-
-    $elements = [
-      '#type' => 'table',
-      '#header' => [
-        $this->t('Source'),
-        $this->t('Plugin'),
-        $this->t('Destination'),
-        $this->t('Weight'),
-      ],
-    ];
-    $elements['#tabledrag'][] = [
-      'action' => 'order',
-      'relationship' => 'sibling',
-      'group' => 'field-weight',
-    ];
-
-    $destinations = ['_hidden' => $this->t('- Hidden -')] + $pattern->getFieldsAsOptions();
-
-    $fields = [];
-    foreach ($this->sourceManager->getFieldsByTag($tag, $context) as $field_name => $field) {
-      $weight = (int) $this->getDefaultValue($configuration, $field_name, 'weight');
-      $fields[$field_name] = [
-        'info' => [
-          '#plain_text' => $field->getFieldLabel(),
-        ],
-        'plugin' => [
-          '#plain_text' => $field->getPluginLabel(),
-        ],
-        'destination' => [
-          '#type' => 'select',
-          '#title' => $this->t('Destination for @field', ['@field' => $field->getFieldLabel()]),
-          '#title_display' => 'invisible',
-          '#default_value' => $this->getDefaultValue($configuration, $field_name, 'destination'),
-          '#options' => $destinations,
-        ],
-        'weight' => [
-          '#type' => 'weight',
-          '#default_value' => $weight,
-          '#delta' => 20,
-          '#title' => $this->t('Weight for @field field', ['@field' => $field->getFieldLabel()]),
-          '#title_display' => 'invisible',
-          '#attributes' => [
-            'class' => ['field-weight'],
-          ],
-        ],
-        '#attributes' => [
-          'class' => ['draggable'],
-        ],
-        '#weight' => $weight,
-      ];
-    }
-
-    uasort($fields, [SortArray::class, 'sortByWeightProperty']);
-    return array_merge($elements, $fields);
-  }
-
-  /**
-   * Normalize settings coming from a form submission.
-   *
-   * @param array $settings
-   *   Pattern display form values array.
-   */
-  public static function processFormStateValues(array &$settings) {
-    if (isset($settings['variants']) && isset($settings['variants'][$settings['pattern']])) {
-      $settings['pattern_variant'] = $settings['variants'][$settings['pattern']];
-      unset($settings['variants']);
-    }
-
-    // Normalize only when necessary.
-    if (isset($settings['pattern_mapping'][$settings['pattern']]['settings'])) {
-      $settings['pattern_mapping'] = $settings['pattern_mapping'][$settings['pattern']]['settings'];
-
-      // Process fields and filter out the hidden ones.
-      foreach ($settings['pattern_mapping'] as $key => $setting) {
-        if ($setting['destination'] == '_hidden') {
-          unset($settings['pattern_mapping'][$key]);
-        }
-        else {
-          [$plugin, $source] = explode(PatternSourceBase::DERIVATIVE_SEPARATOR, $key, 2);
-          $settings['pattern_mapping'][$key]['plugin'] = $plugin;
-          $settings['pattern_mapping'][$key]['source'] = $source;
-        }
-      }
-
-      // Normalize weights.
-      $weight = 0;
-      uasort($settings['pattern_mapping'], [
-        SortArray::class,
-        'sortByWeightElement',
-      ]);
-      foreach ($settings['pattern_mapping'] as $key => $setting) {
-        $settings['pattern_mapping'][$key]['weight'] = $weight++;
-      }
-    }
-  }
-
-  /**
-   * Helper function: return mapping destination given plugin id and field name.
-   *
-   * @param string $plugin
-   *   Current plugin ID.
-   * @param string $source
-   *   Source field name.
-   * @param array $settings
-   *   Setting array.
-   *
-   * @return string|null
-   *   Destination field or NULL if none found.
-   */
-  public function getMappingDestination($plugin, $source, array $settings) {
-    $mapping_id = $plugin . PatternSourceBase::DERIVATIVE_SEPARATOR . $source;
-    if (isset($settings['pattern_mapping'][$mapping_id])) {
-      return $settings['pattern_mapping'][$mapping_id]['destination'];
-    }
-    return NULL;
-  }
-
-  /**
-   * Helper function: check if given source field has mapping destination.
-   *
-   * @param string $plugin
-   *   Current plugin ID.
-   * @param string $source
-   *   Source field name.
-   * @param array $settings
-   *   Setting array.
-   *
-   * @return bool
-   *   TRUE if source has destination field, FALSE otherwise.
-   */
-  public function hasMappingDestination($plugin, $source, array $settings) {
-    return $this->getMappingDestination($plugin, $source, $settings) !== NULL;
-  }
-
-  /**
-   * Helper function: get default value.
-   *
-   * @param array $configuration
-   *   Configuration.
-   * @param string $field_name
-   *   Field name.
-   * @param string $value
-   *   Value name.
-   *
-   * @return string
-   *   Field property value.
-   */
-  protected function getDefaultValue(array $configuration, $field_name, $value) {
-    if (isset($configuration['pattern_mapping'][$field_name][$value])) {
-      return $configuration['pattern_mapping'][$field_name][$value];
-    }
-    return NULL;
-  }
-
-}
diff --git a/src/Form/UiPatternsFormBuilderTrait.php b/src/Form/UiPatternsFormBuilderTrait.php
new file mode 100644
index 00000000..9551a0d5
--- /dev/null
+++ b/src/Form/UiPatternsFormBuilderTrait.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Drupal\ui_patterns\Form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\sdc\Plugin\Component;
+
+/**
+ *
+ */
+trait UiPatternsFormBuilderTrait {
+
+  /**
+   * ...
+   *
+   * To use with:
+   * - PluginSettingsInterface::defaultSettings
+   * - ConfigurableInterface::defaultConfiguration
+   * - views/PluginBase::setOptionDefaults
+   * - ...
+   */
+  public static function getComponentFormDefault() {
+    return [
+      "component_id" => "",
+      "variant_id" => "",
+      "slots" => [],
+      "props" => [],
+    ];
+  }
+
+  /**
+   * Build the complete form.
+   *
+   * @param Drupal\sdc\Plugin\Component $component
+   *   The component plugin.
+   */
+  protected function buildComponentForm(FormStateInterface $form_state, Component $component, array $context): array {
+    return [
+      "component_id" => $this->buildComponentSelectorForm(),
+      "variant_id" => $this->buildVariantSelectorForm($form_state, $component),
+      "slots" => $this->buildSlotsForm($form_state, $component, $context),
+      "forms" => $this->buildPropsForm($form_state, $component, $context),
+    ];
+  }
+
+  /**
+   * Build components selector widget.
+   */
+  protected function buildComponentSelectorForm(): array {
+    $components = \Drupal::service("plugin.manager.sdc")->getDefinitions();
+    // @todo getGroupedDefinitions?
+    $options = [];
+    foreach ($components as $component_id => $component) {
+      $options[$component_id] = $component["name"];
+    }
+    return [
+      "#type" => "select",
+      "#title" => t("Component"),
+      "#options" => $options,
+    ];
+  }
+
+  /**
+   *
+   */
+  protected function buildVariantSelectorForm(FormStateInterface $form_state, Component $component): array {
+    $definition = $component->getPluginDefinition();
+    if (!isset($definition["variants"])) {
+      return [];
+    }
+    $options = [];
+    foreach ($definition["variants"] as $variant_id => $variant) {
+      $options[$variant_id] = $variant["title"];
+    }
+    return [
+      "#type" => "select",
+      "#title" => t("Variant"),
+      "#options" => $options,
+    ];
+  }
+
+  /**
+   *
+   */
+  protected function buildSlotsForm(FormStateInterface $form_state, Component $component, array $context): array {
+    return [];
+  }
+
+  /**
+   *
+   */
+  protected function buildPropsForm(FormStateInterface $form_state, Component $component, array $context): array {
+    $sub_sources_form_value = $context['form_values'];
+    $form = [];
+    $sub_sources = [];
+    foreach ($component->metadata->schema['properties'] as $prop_id => $prop) {
+      $sources = $prop['ui_patterns']['source'];
+      if (count($sources) == 1) {
+        /** @var \Drupal\ui_patterns\SourcePluginBase $default_source */
+        $default_source = current($sources);
+        $configuration = $default_source->getConfiguration();
+        if (isset($sub_sources_form_value[$prop_id])) {
+          $configuration['form_value'] = $sub_sources_form_value[$prop_id];
+          $default_source->setConfiguration($configuration);
+        }
+        $form[$prop_id] = $default_source->buildConfigurationForm($form, $form_state);
+        $sub_sources[$prop_id] = $default_source;
+      }
+      elseif (count($sources) > 1) {
+        $options = [];
+        foreach ($sources as $source) {
+          $options[$source->getPluginId()] = $source->label();
+        }
+        $form[$prop_id] = [
+          "#type" => "select",
+          "#title" => $prop["title"],
+          "#options" => $options,
+        ];
+        // @todo dynamically load source form on select.
+        // @todo $sub_sources?
+      }
+    }
+    if (!$form_state->has('sub_sources')) {
+      $form_state->set('sub_sources', $sub_sources);
+    }
+    return $form;
+  }
+
+  /**
+   *
+   */
+  protected function submitComponentForm($form, FormStateInterface $form_state, array $context):array {
+    $sub_sources = $form_state->get('sub_sources');
+    $sub_values = [];
+    foreach ($sub_sources as $prop_id => $sub_source) {
+      $sub_source->submitConfigurationForm($form['ui_patterns'][$prop_id], $form_state);
+      $sub_values[$prop_id] = $sub_source->getConfiguration()['form_value'];
+    }
+    return $sub_values;
+  }
+
+}
diff --git a/src/Plugin/Deriver/AbstractPatternsDeriver.php b/src/Plugin/Deriver/AbstractPatternsDeriver.php
deleted file mode 100644
index f8e84b94..00000000
--- a/src/Plugin/Deriver/AbstractPatternsDeriver.php
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Plugin\Deriver;
-
-use Drupal\Component\Plugin\Derivative\DeriverBase;
-use Drupal\Core\Messenger\MessengerInterface;
-use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
-use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Drupal\Core\TypedData\TypedDataManager;
-use Drupal\ui_patterns\Definition\PatternDefinition;
-use Drupal\ui_patterns\TypedData\PatternDataDefinition;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Base class for all other UI Patterns derivers.
- *
- * @package Drupal\ui_patterns\Deriver
- */
-abstract class AbstractPatternsDeriver extends DeriverBase implements PatternsDeriverInterface, ContainerDeriverInterface {
-
-  use StringTranslationTrait;
-
-  /**
-   * Typed data manager service.
-   *
-   * @var \Drupal\Core\TypedData\TypedDataManager
-   */
-  protected $typedDataManager;
-
-  /**
-   * The messenger.
-   *
-   * @var \Drupal\Core\Messenger\MessengerInterface
-   */
-  protected $messenger;
-
-  /**
-   * AbstractPatternsDeriver constructor.
-   */
-  public function __construct($base_plugin_id, TypedDataManager $typed_data_manager, MessengerInterface $messenger) {
-    $this->basePluginId = $base_plugin_id;
-    $this->typedDataManager = $typed_data_manager;
-    $this->messenger = $messenger;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, $base_plugin_id) {
-    return new static(
-      $base_plugin_id,
-      $container->get('typed_data_manager'),
-      $container->get('messenger')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getDerivativeDefinitions($base_plugin_definition) {
-    foreach ($this->getPatterns() as $pattern) {
-      $pattern->setDeriver($base_plugin_definition['deriver']);
-      $pattern->setClass($base_plugin_definition['class']);
-      if ($this->isValidPatternDefinition($pattern)) {
-        $this->derivatives[$pattern->id()] = $pattern;
-      }
-    }
-    return $this->derivatives;
-  }
-
-  /**
-   * Get pattern data object.
-   *
-   * @param array $definition
-   *   Pattern definition array.
-   *
-   * @return \Drupal\ui_patterns\Definition\PatternDefinition
-   *   Pattern definition object.
-   */
-  protected function getPatternDefinition(array $definition = []) {
-    return new PatternDefinition($definition);
-  }
-
-  /**
-   * Validate pattern definition.
-   *
-   * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition
-   *   Pattern definition.
-   *
-   * @return bool
-   *   Whereas current pattern definition is valid or not.
-   */
-  protected function isValidPatternDefinition(PatternDefinition $definition) {
-    $data_definition = PatternDataDefinition::create();
-    $violations = $this->typedDataManager->create($data_definition, $definition->toArray())->validate();
-    if ($violations->count()) {
-      /** @var \Symfony\Component\Validator\ConstraintViolation $violation */
-      $this->messenger->addError($this->t("Pattern ':id' is skipped because of the following validation error(s):", [':id' => $definition->id()]));
-      foreach ($violations as $violation) {
-        $message = $this->t('Validation error on ":id.:property": :message', [
-          ':id' => $definition->id(),
-          ':property' => $violation->getPropertyPath(),
-          ':message' => $violation->getMessage(),
-        ]);
-        $this->messenger->addError($message);
-      }
-      return FALSE;
-    }
-    return TRUE;
-  }
-
-}
diff --git a/src/Plugin/Deriver/AbstractYamlPatternsDeriver.php b/src/Plugin/Deriver/AbstractYamlPatternsDeriver.php
deleted file mode 100644
index 98db5032..00000000
--- a/src/Plugin/Deriver/AbstractYamlPatternsDeriver.php
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Plugin\Deriver;
-
-use Drupal\Core\File\FileSystemInterface;
-use Drupal\Core\Messenger\MessengerInterface;
-use Drupal\Core\Site\Settings;
-use Drupal\Core\TypedData\TypedDataManager;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Class AbstractYamlPatternsDeriver.
- *
- * Derive pattern plugin definitions stored in YAML files.
- *
- * @package Drupal\ui_patterns\Deriver
- */
-abstract class AbstractYamlPatternsDeriver extends AbstractPatternsDeriver implements YamlPatternsDeriverInterface {
-
-  /**
-   * File system service.
-   *
-   * @var \Drupal\Core\File\FileSystemInterface
-   */
-  protected $fileSystem;
-
-  /**
-   * Constructor.
-   *
-   * @param string $base_plugin_id
-   *   The base plugin ID.
-   * @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
-   *   Typed data manager service.
-   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
-   *   Messenger.
-   * @param \Drupal\Core\File\FileSystemInterface $file_system
-   *   File system service.
-   */
-  public function __construct($base_plugin_id, TypedDataManager $typed_data_manager, MessengerInterface $messenger, FileSystemInterface $file_system) {
-    parent::__construct($base_plugin_id, $typed_data_manager, $messenger);
-
-    $this->fileSystem = $file_system;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, $base_plugin_id) {
-    return new static(
-      $base_plugin_id,
-      $container->get('typed_data_manager'),
-      $container->get('messenger'),
-      $container->get('file_system')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function fileScanDirectory($directory) {
-    if (!is_dir($directory)) {
-      return [];
-    }
-    $options = ['nomask' => $this->getNoMask()];
-    $extensions = $this->getFileExtensions();
-    $extensions = array_map('preg_quote', $extensions);
-    $extensions = implode('|', $extensions);
-    $files = $this->fileSystem->scanDirectory($directory, "/{$extensions}$/", $options);
-    // In different file systems order of files in a folder can be different
-    // that can break tests. So let's sort them alphabetically manually.
-    ksort($files);
-    return $files;
-  }
-
-  /**
-   * Returns a regular expression for directories to be excluded in a file scan.
-   *
-   * @return string
-   *   Regular expression.
-   */
-  protected function getNoMask() {
-    $ignore = Settings::get('file_scan_ignore_directories', []);
-    // We add 'tests' directory to the ones found in settings.
-    $ignore[] = 'tests';
-    array_walk($ignore, function (&$value) {
-      $value = preg_quote($value, '/');
-    });
-    return '/^' . implode('|', $ignore) . '$/';
-  }
-
-}
diff --git a/src/Plugin/Deriver/PatternsDeriverInterface.php b/src/Plugin/Deriver/PatternsDeriverInterface.php
deleted file mode 100644
index bf3f4aff..00000000
--- a/src/Plugin/Deriver/PatternsDeriverInterface.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Plugin\Deriver;
-
-/**
- * Deriver interface for pattern definitions.
- *
- * @package Drupal\ui_patterns\Plugin\Deriver
- */
-interface PatternsDeriverInterface {
-
-  /**
-   * Get pattern definition objects.
-   *
-   * @return \Drupal\ui_patterns\Definition\PatternDefinition[]
-   *   List of pattern definitions contained in the file.
-   */
-  public function getPatterns();
-
-}
diff --git a/src/Plugin/Deriver/YamlPatternsDeriverInterface.php b/src/Plugin/Deriver/YamlPatternsDeriverInterface.php
deleted file mode 100644
index a5d37ade..00000000
--- a/src/Plugin/Deriver/YamlPatternsDeriverInterface.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Plugin\Deriver;
-
-/**
- * Deriver interface for YAML-based pattern definitions.
- *
- * @package Drupal\ui_patterns\Plugin\Deriver
- */
-interface YamlPatternsDeriverInterface extends PatternsDeriverInterface {
-
-  /**
-   * Get list of possible yaml definition file extensions.
-   *
-   * @return string[]
-   *   List of allowed file extensions.
-   */
-  public function getFileExtensions();
-
-  /**
-   * Wrapper method for global function call.
-   *
-   * @see file.inc
-   */
-  public function fileScanDirectory($directory);
-
-}
diff --git a/src/Plugin/PatternBase.php b/src/Plugin/PatternBase.php
deleted file mode 100644
index 90993cf1..00000000
--- a/src/Plugin/PatternBase.php
+++ /dev/null
@@ -1,165 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Plugin;
-
-use Drupal\Component\Plugin\PluginBase;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\ui_patterns\Definition\PatternDefinition;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * UI Patterns plugin base class.
- *
- * @package Drupal\ui_patterns\Plugin
- */
-abstract class PatternBase extends PluginBase implements PatternInterface, ContainerFactoryPluginInterface {
-
-  /**
-   * The app root.
-   *
-   * @var string
-   */
-  protected $root;
-
-  /**
-   * Module handler.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler;
-
-  /**
-   * UiPatternsManager constructor.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, $root, ModuleHandlerInterface $module_handler) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->root = $root;
-    $this->moduleHandler = $module_handler;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('app.root'),
-      $container->get('module_handler')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getThemeImplementation() {
-    $definition = $this->getPluginDefinition();
-    $item = [];
-    $item += $this->processVariables($definition);
-    $item += $this->processUseProperty($definition);
-    return [
-      $definition['theme hook'] => $item,
-    ];
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getLibraryDefinitions() {
-    // @codingStandardsIgnoreStart
-    $libraries = [];
-    $definition = $this->getPluginDefinition();
-
-    // Get only locally defined libraries.
-    $items = array_filter($definition['libraries'], function ($library) {
-      return is_array($library);
-    });
-
-    // Attach pattern base path to assets.
-    if (!empty($definition['base path'])) {
-      $base_path = str_replace($this->root, '', $definition['base path']);
-      $this->processLibraries($items, $base_path);
-    }
-
-    // Produce final libraries array.
-    $id = $definition['id'];
-    array_walk($items, function ($value) use (&$libraries, $id) {
-      $libraries[$id . '.' . key($value)] = reset($value);
-    });
-
-    // @codingStandardsIgnoreEnd
-    return $libraries;
-  }
-
-  /**
-   * Process libraries.
-   *
-   * @param array|string $libraries
-   *   List of dependencies or "dependencies:" root property.
-   * @param string $base_path
-   *   Pattern base path.
-   * @param string $parent
-   *   Item parent set in previous recursive iteration, if any.
-   */
-  protected function processLibraries(&$libraries, $base_path, $parent = '') {
-    if (!is_string($libraries)) {
-      $parents = ['js', 'base', 'layout', 'component', 'state', 'theme'];
-      $_libraries = $libraries;
-      foreach ($_libraries as $name => $values) {
-        $is_asset = in_array($parent, $parents, TRUE);
-        $is_external = isset($values['type']) && $values['type'] == 'external';
-        if ($is_asset && !$is_external) {
-          $libraries[$base_path . DIRECTORY_SEPARATOR . $name] = $values;
-          unset($libraries[$name]);
-        }
-        elseif (!$is_asset) {
-          $this->processLibraries($libraries[$name], $base_path, $name);
-        }
-      }
-    }
-  }
-
-  /**
-   * Process 'use' definition property.
-   *
-   * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition
-   *   Pattern definition array.
-   *
-   * @return array
-   *   Processed hook definition portion.
-   */
-  protected function processUseProperty(PatternDefinition $definition) {
-    $return = [];
-    if ($definition->hasUse()) {
-      $return = [
-        'path' => $this->moduleHandler->getModule('ui_patterns')->getPath() . '/templates',
-        'template' => 'patterns-use-wrapper',
-      ];
-    }
-    return $return;
-  }
-
-  /**
-   * Process theme variables.
-   *
-   * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition
-   *   Pattern definition array.
-   *
-   * @return array
-   *   Processed hook definition portion.
-   */
-  protected function processVariables(PatternDefinition $definition) {
-    $return = [];
-    foreach ($definition->getFields() as $field) {
-      $return['variables'][$field->getName()] = NULL;
-    }
-    $return['variables']['attributes'] = [];
-    $return['variables']['context'] = [];
-    $return['variables']['variant'] = '';
-    $return['variables']['use'] = '';
-    return $return;
-  }
-
-}
diff --git a/src/Plugin/PatternInterface.php b/src/Plugin/PatternInterface.php
deleted file mode 100644
index df5ceff8..00000000
--- a/src/Plugin/PatternInterface.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Plugin;
-
-/**
- * Interface for UI Pattern plugins.
- *
- * @package Drupal\ui_patterns
- */
-interface PatternInterface {
-
-  /**
-   * Get theme implementation for current pattern.
-   *
-   * @see ui_patterns_theme()
-   *
-   * @return array
-   *   Theme implementation array.
-   */
-  public function getThemeImplementation();
-
-  /**
-   * Get library definitions for current pattern.
-   *
-   * @see ui_patterns_library_info_build()
-   *
-   * @return array
-   *   Library definitions array.
-   */
-  public function getLibraryDefinitions();
-
-}
diff --git a/src/Plugin/PatternSourceBase.php b/src/Plugin/PatternSourceBase.php
deleted file mode 100644
index 560f69a3..00000000
--- a/src/Plugin/PatternSourceBase.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Plugin;
-
-use Drupal\Component\Plugin\Exception\PluginException;
-use Drupal\Component\Plugin\PluginBase;
-use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Drupal\ui_patterns\Definition\PatternSourceField;
-use Drupal\Component\Plugin\ConfigurableInterface;
-use Drupal\Component\Plugin\PluginInspectionInterface;
-
-/**
- * Base class for UI Patterns Source plugins.
- */
-abstract class PatternSourceBase extends PluginBase implements PatternSourceInterface, PluginInspectionInterface, ConfigurableInterface {
-
-  use StringTranslationTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getSourceField($name, $label) {
-    return new PatternSourceField($name, $label, $this->pluginDefinition['id'], $this->pluginDefinition['label']);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getConfiguration() {
-    return $this->configuration;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setConfiguration(array $configuration) {
-    $this->configuration = $configuration;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function defaultConfiguration() {
-    return [
-      'context' => [],
-    ];
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getContextProperty($name) {
-    $configuration = $this->getConfiguration();
-    if (isset($configuration['context'][$name]) && !empty($configuration['context'][$name])) {
-      return $configuration['context'][$name];
-    }
-    $reflection = new \ReflectionClass($this);
-    $message = sprintf("Context property '%s' from %s is missing or empty.", $name, $reflection->name);
-    throw new PluginException($message);
-  }
-
-}
diff --git a/src/Plugin/PatternSourceInterface.php b/src/Plugin/PatternSourceInterface.php
deleted file mode 100644
index a98016cc..00000000
--- a/src/Plugin/PatternSourceInterface.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Plugin;
-
-/**
- * Defines an interface for UI Patterns Source plugins.
- */
-interface PatternSourceInterface {
-
-  /**
-   * Source field factory method.
-   *
-   * @param string $name
-   *   Machine name.
-   * @param string $label
-   *   Human readable label.
-   *
-   * @return \Drupal\ui_patterns\Definition\PatternSourceField
-   *   Source field instance.
-   */
-  public function getSourceField($name, $label);
-
-  /**
-   * Return list of source fields.
-   *
-   * @return \Drupal\ui_patterns\Definition\PatternSourceField[]
-   *   List of source fields.
-   */
-  public function getSourceFields();
-
-  /**
-   * Get context property value, if any.
-   *
-   * @param string $name
-   *   Context property name.
-   *
-   * @return mixed
-   *   Context property value.
-   */
-  public function getContextProperty($name);
-
-}
diff --git a/src/Plugin/UiPatterns/PropType/AttributesPropType.php b/src/Plugin/UiPatterns/PropType/AttributesPropType.php
new file mode 100644
index 00000000..853dc725
--- /dev/null
+++ b/src/Plugin/UiPatterns/PropType/AttributesPropType.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\PropType;
+
+use Drupal\ui_patterns\PropTypePluginBase;
+
+/**
+ * Provides a 'Attributes' PropType.
+ *
+ * @PropType(
+ *   id = "attributes",
+ *   label = @Translation("Attributes"),
+ *   description = @Translation("TBD"),
+ *   priority = 10,
+ *   schema = {
+ *     "type": "object",
+ *     "patternProperties": {
+ *       ".+": {
+ *         "anyOf": {
+ *           {
+ *             "type": {"string", "number"}
+ *           },
+ *           {
+ *             "type": "array",
+ *             "items": {
+ *               "anyOf": {{ "type": "number" }, { "type": "string" }}
+ *             }
+ *           }
+ *         }
+ *       }
+ *     }
+ *   }
+ * )
+ */
+class AttributesPropType extends PropTypePluginBase {
+
+}
diff --git a/src/Plugin/UiPatterns/PropType/BooleanPropType.php b/src/Plugin/UiPatterns/PropType/BooleanPropType.php
new file mode 100644
index 00000000..6b75bed9
--- /dev/null
+++ b/src/Plugin/UiPatterns/PropType/BooleanPropType.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\PropType;
+
+use Drupal\ui_patterns\PropTypePluginBase;
+
+/**
+ * Provides a 'boolean' PropType.
+ *
+ * @PropType(
+ *   id = "boolean",
+ *   label = @Translation("Boolean"),
+ *   description = @Translation("TBD"),
+ *   priority = 1,
+ *   schema = {
+ *     "type": "boolean"
+ *   }
+ * )
+ */
+class BooleanPropType extends PropTypePluginBase {
+
+}
diff --git a/src/Plugin/UiPatterns/PropType/ColorPropType.php b/src/Plugin/UiPatterns/PropType/ColorPropType.php
new file mode 100644
index 00000000..33ab4ccd
--- /dev/null
+++ b/src/Plugin/UiPatterns/PropType/ColorPropType.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\PropType;
+
+use Drupal\ui_patterns\PropTypePluginBase;
+
+/**
+ * Provides a 'Color' PropType.
+ *
+ * @PropType(
+ *   id = "color",
+ *   label = @Translation("Color"),
+ *   description = @Translation("TBD"),
+ *   priority = 10,
+ *   schema = {
+ *     "type": "string",
+ *     "pattern": "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
+ *   }
+ * )
+ */
+class ColorPropType extends PropTypePluginBase {
+
+}
diff --git a/src/Plugin/UiPatterns/PropType/EnumPropType.php b/src/Plugin/UiPatterns/PropType/EnumPropType.php
new file mode 100644
index 00000000..348a8c03
--- /dev/null
+++ b/src/Plugin/UiPatterns/PropType/EnumPropType.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\PropType;
+
+use Drupal\ui_patterns\PropTypePluginBase;
+
+/**
+ * Provides a 'enum' PropType.
+ *
+ * @PropType(
+ *   id = "enum",
+ *   label = @Translation("Enum"),
+ *   description = @Translation("TBD"),
+ *   priority = 10,
+ *   schema = {
+ *     "type": {"string", "number", "integer"},
+ *     "enum": {},
+ *   }
+ * )
+ */
+class EnumPropType extends PropTypePluginBase {
+
+}
diff --git a/src/Plugin/UiPatterns/PropType/LinksPropType.php b/src/Plugin/UiPatterns/PropType/LinksPropType.php
new file mode 100644
index 00000000..472d68f9
--- /dev/null
+++ b/src/Plugin/UiPatterns/PropType/LinksPropType.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\PropType;
+
+use Drupal\ui_patterns\PropTypePluginBase;
+
+/**
+ * Provides a 'links' PropType.
+ *
+ * @PropType(
+ *   id = "links",
+ *   label = @Translation("Links"),
+ *   description = @Translation("TBD"),
+ *   priority = 10,
+ *   schema = {
+ *     "type": "array",
+ *     "items": {
+ *       "type": "object",
+ *       "properties": {
+ *         "title": {"type": "string"},
+ *         "attributes": { "$ref": "ui-patterns://attributes" },
+ *         "below": { "$ref": "ui-patterns://links" }
+ *       }
+ *     }
+ *   }
+ * )
+ */
+class LinksPropType extends PropTypePluginBase {
+
+}
diff --git a/src/Plugin/UiPatterns/PropType/MachineNamePropType.php b/src/Plugin/UiPatterns/PropType/MachineNamePropType.php
new file mode 100644
index 00000000..b9f16b0b
--- /dev/null
+++ b/src/Plugin/UiPatterns/PropType/MachineNamePropType.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\PropType;
+
+use Drupal\ui_patterns\PropTypePluginBase;
+
+/**
+ * Provides a 'Machine name' PropType.
+ *
+ * @PropType(
+ *   id = "machine_name",
+ *   label = @Translation("Machine name"),
+ *   description = @Translation("TBD"),
+ *   priority = 100,
+ *   schema = {
+ *     "type": "string",
+ *     "pattern": "^[A-Za-z]+\w*$"
+ *   }
+ * )
+ */
+class MachineNamePropType extends PropTypePluginBase {
+
+}
diff --git a/src/Plugin/UiPatterns/PropType/NumberPropType.php b/src/Plugin/UiPatterns/PropType/NumberPropType.php
new file mode 100644
index 00000000..8c52b78e
--- /dev/null
+++ b/src/Plugin/UiPatterns/PropType/NumberPropType.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\PropType;
+
+use Drupal\ui_patterns\PropTypePluginBase;
+
+/**
+ * Provides a 'number' PropType.
+ *
+ * @PropType(
+ *   id = "number",
+ *   label = @Translation("Number"),
+ *   description = @Translation("TBD"),
+ *   priority = 1,
+ *   schema = {
+ *     "type": {"number", "integer"}
+ *   }
+ * )
+ */
+class NumberPropType extends PropTypePluginBase {
+
+}
diff --git a/src/Plugin/UiPatterns/PropType/StringPropType.php b/src/Plugin/UiPatterns/PropType/StringPropType.php
new file mode 100644
index 00000000..b6ed6752
--- /dev/null
+++ b/src/Plugin/UiPatterns/PropType/StringPropType.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\PropType;
+
+use Drupal\ui_patterns\PropTypePluginBase;
+
+/**
+ * Provides a 'string' PropType.
+ *
+ * @PropType(
+ *   id = "string",
+ *   label = @Translation("String"),
+ *   description = @Translation("TBD"),
+ *   priority = 1,
+ *   schema = {
+ *     "type": "string"
+ *   }
+ * )
+ */
+class StringPropType extends PropTypePluginBase {
+
+}
diff --git a/src/Plugin/UiPatterns/PropType/UnKnowPropType.php b/src/Plugin/UiPatterns/PropType/UnKnowPropType.php
new file mode 100644
index 00000000..149c00d6
--- /dev/null
+++ b/src/Plugin/UiPatterns/PropType/UnKnowPropType.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\PropType;
+
+use Drupal\ui_patterns\PropTypePluginBase;
+
+/**
+ * Provides a 'unknown_prop_type' PropType.
+ *
+ * @PropType(
+ *   id = "unknown_prop_type",
+ *   label = @Translation("Prop Type"),
+ *   description = @Translation("TBD"),
+ *   priority = 10,
+ *   schema = {
+ *   }
+ * )
+ */
+class UnKnowPropType extends PropTypePluginBase {
+
+}
diff --git a/src/Plugin/UiPatterns/PropType/UrlPropType.php b/src/Plugin/UiPatterns/PropType/UrlPropType.php
new file mode 100644
index 00000000..b6684f42
--- /dev/null
+++ b/src/Plugin/UiPatterns/PropType/UrlPropType.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\PropType;
+
+use Drupal\ui_patterns\PropTypePluginBase;
+
+/**
+ * Provides a 'Url' PropType.
+ *
+ * @PropType(
+ *   id = "url",
+ *   label = @Translation("Url"),
+ *   description = @Translation("TBD"),
+ *   priority = 10,
+ *   schema = {
+ *     "type": "string",
+ *     "format": "iri-reference"
+ *   }
+ * )
+ */
+class UrlPropType extends PropTypePluginBase {
+
+}
diff --git a/src/Plugin/UiPatterns/Source/BreadcrumbSource.php b/src/Plugin/UiPatterns/Source/BreadcrumbSource.php
new file mode 100644
index 00000000..c2942a3a
--- /dev/null
+++ b/src/Plugin/UiPatterns/Source/BreadcrumbSource.php
@@ -0,0 +1,85 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\Source;
+
+use Drupal\ui_patterns\SourcePluginBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Plugin implementation of the source_provider.
+ *
+ * @Source(
+ *   id = "breadcrumb",
+ *   label = @Translation("Breadcrumb"),
+ *   description = @Translation("Foo description."),
+ *   prop_types = {
+ *     "links"
+ *   }
+ * )
+ */
+final class BreadcrumbSource extends SourcePluginBase {
+
+  /**
+   * The current route match.
+   *
+   * @var \Drupal\Core\Routing\RouteMatchInterface
+   */
+  protected $routeMatch;
+
+  /**
+   * The breadcrumb manager.
+   *
+   * @var \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface
+   */
+  protected $breadcrumbManager;
+
+  /**
+   *
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    $plugin = new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('module_handler'),
+      $container->get('entity_type.manager'),
+    );
+    /** @var \Drupal\Core\StringTranslation\TranslationInterface $translation */
+    $translation = $container->get('string_translation');
+    $plugin->setStringTranslation($translation);
+    $plugin->breadcrumbManager = $container->get('breadcrumb');
+    $plugin->routeMatch = $container->get('current_route_match');
+    return $plugin;
+  }
+
+  /**
+   *
+   */
+  public function getData(): mixed {
+    $breadcrumb = $this->breadcrumbManager->build($this->routeMatch);
+    $renderable = $breadcrumb->toRenderable();
+    if (isset($renderable["#cache"])) {
+      $this->cacheArray = $renderable["#cache"];
+    }
+    $links = [];
+    foreach ($breadcrumb->getLinks() as $link) {
+      $links[] = [
+        "title" => $link->getText(),
+        "url" => $link->getUrl()->toString(),
+      ];
+    }
+    return $links;
+
+    return [];
+  }
+
+  /**
+   *
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/src/Plugin/UiPatterns/Source/ColorWidget.php b/src/Plugin/UiPatterns/Source/ColorWidget.php
new file mode 100644
index 00000000..3c2cadd3
--- /dev/null
+++ b/src/Plugin/UiPatterns/Source/ColorWidget.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\Source;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Plugin implementation of the source_provider.
+ *
+ * @Source(
+ *   id = "color",
+ *   label = @Translation("Color"),
+ *   description = @Translation("Form elmeent for choosing a color."),
+ *   prop_types = {
+ *     "color"
+ *   }
+ * )
+ */
+final class ColorWidget extends SourcePluginBase {
+
+  /**
+   *
+   */
+  public function getData(): mixed {
+    return 'Nice Site name';
+  }
+
+  /**
+   *
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    return [
+      '#type' => 'color',
+      '#title' => $this->propDefinition['title'],
+      '#default_value' => $this->configuration['form_value'],
+    ];
+  }
+
+  /**
+   *
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/src/Plugin/UiPatterns/Source/FieldSource.php b/src/Plugin/UiPatterns/Source/FieldSource.php
deleted file mode 100644
index 7ff73013..00000000
--- a/src/Plugin/UiPatterns/Source/FieldSource.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\Plugin\UiPatterns\Source;
-
-use Drupal\Core\Entity\EntityFieldManager;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\ui_patterns\Plugin\PatternSourceBase;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Defines Fields API pattern source plugin.
- *
- * @UiPatternsSource(
- *   id = "fields",
- *   label = @Translation("Fields"),
- *   provider = "field",
- *   tags = {
- *     "entity_display"
- *   }
- * )
- */
-class FieldSource extends PatternSourceBase implements ContainerFactoryPluginInterface {
-
-  /**
-   * Entity manager service.
-   *
-   * @var \Drupal\Core\Entity\EntityFieldManager
-   */
-  protected $entityFieldManager;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityFieldManager $entity_field_manager) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->entityFieldManager = $entity_field_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('entity_field.manager')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getSourceFields() {
-    $sources = [];
-    $fields = $this->entityFieldManager->getFieldDefinitions($this->getContextProperty('entity_type'), $this->getContextProperty('entity_bundle'));
-
-    /** @var \Drupal\Core\Field\FieldDefinitionInterface $field */
-    foreach ($fields as $field) {
-      if (!$this->getContextProperty('limit')) {
-        $sources[] = $this->getSourceField($field->getName(), $field->getLabel());
-      }
-      elseif (in_array($field->getName(), $this->getContextProperty('limit'))) {
-        $sources[] = $this->getSourceField($field->getName(), $field->getLabel());
-      }
-    }
-    return $sources;
-  }
-
-}
diff --git a/src/Plugin/UiPatterns/Source/MachineNameWidget.php b/src/Plugin/UiPatterns/Source/MachineNameWidget.php
new file mode 100644
index 00000000..94e84865
--- /dev/null
+++ b/src/Plugin/UiPatterns/Source/MachineNameWidget.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\Source;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Plugin implementation of the source_provider.
+ *
+ * @Source(
+ *   id = "machine_name",
+ *   label = @Translation("Machine name"),
+ *   description = @Translation("Validated to ensure that the name does not contain disallowed characters."),
+ *   prop_types = {
+ *     "machine_name"
+ *   }
+ * )
+ */
+final class MachineNameWidget extends SourcePluginBase {
+
+  /**
+   *
+   */
+  public function getData(): mixed {
+    return 'abc_123';
+  }
+
+  /**
+   *
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    return [
+      '#type' => 'machine_name',
+      '#title' => $this->propDefinition['title'],
+      '#default_value' => $this->configuration['form_value'],
+    ];
+  }
+
+  /**
+   *
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/src/Plugin/UiPatterns/Source/MenuSource.php b/src/Plugin/UiPatterns/Source/MenuSource.php
new file mode 100644
index 00000000..d58a0263
--- /dev/null
+++ b/src/Plugin/UiPatterns/Source/MenuSource.php
@@ -0,0 +1,72 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\Source;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Plugin implementation of the source_provider.
+ *
+ * @Source(
+ *   id = "menu",
+ *   label = @Translation("Menu"),
+ *   description = @Translation("Foo description."),
+ *   prop_types = {
+ *     "links"
+ *   }
+ * )
+ */
+final class MenuSource extends SourcePluginBase {
+
+  /**
+   *
+   */
+  public function getData(): mixed {
+    return [];
+  }
+
+  /**
+   *
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $value = $value ?? [];
+    $form["menu"] = [
+      '#type' => 'select',
+      '#title' => 'Menu',
+      '#options' => $this->getMenuList(),
+      '#default_value' => \array_key_exists("menu", $value) ? $value["menu"] : "",
+    ];
+    $options = range(0, $this->menuLinkTree->maxDepth());
+    unset($options[0]);
+    $form['level'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Initial visibility level'),
+      '#default_value' => \array_key_exists("level", $value) ? $value["level"] : 1,
+      '#options' => $options,
+      '#required' => TRUE,
+    ];
+    $options[0] = $this->t('Unlimited');
+    $form['depth'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Number of levels to display'),
+      '#default_value' => \array_key_exists("depth", $value) ? $value["depth"] : 0,
+      '#options' => $options,
+      '#description' => $this->t('This maximum number includes the initial level and the final display is dependant of the pattern template.'),
+      '#required' => TRUE,
+    ];
+
+    return $form;
+
+  }
+
+  /**
+   *
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/src/Plugin/UiPatterns/Source/NumberWidget.php b/src/Plugin/UiPatterns/Source/NumberWidget.php
new file mode 100644
index 00000000..a5a4252f
--- /dev/null
+++ b/src/Plugin/UiPatterns/Source/NumberWidget.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\Source;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Plugin implementation of the source_provider.
+ *
+ * @Source(
+ *   id = "number",
+ *   label = @Translation("Number"),
+ *   description = @Translation("Numeric input, with special numeric validation."),
+ *   prop_types = {
+ *     "number"
+ *   }
+ * )
+ */
+final class NumberWidget extends SourcePluginBase {
+
+  /**
+   *
+   */
+  public function getData(): mixed {
+    return 42;
+  }
+
+  /**
+   *
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    return [
+      '#type' => 'number',
+      '#title' => $this->propDefinition['title'],
+      '#default_value' => $this->configuration['form_value'],
+    ];
+  }
+
+  /**
+   *
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/src/Plugin/UiPatterns/Source/SelectWidget.php b/src/Plugin/UiPatterns/Source/SelectWidget.php
new file mode 100644
index 00000000..e3e84d7e
--- /dev/null
+++ b/src/Plugin/UiPatterns/Source/SelectWidget.php
@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\Source;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Plugin implementation of the source.
+ *
+ * @Source(
+ *   id = "select",
+ *   label = @Translation("Select"),
+ *   description = @Translation("A drop-down menu or scrolling selection box."),
+ *   prop_types = {
+ *     "enum"
+ *   }
+ * )
+ */
+final class SelectWidget extends SourcePluginBase {
+
+  /**
+   *
+   */
+  public function getData(): mixed {
+    return 'Nice Site name';
+  }
+
+  /**
+   *
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $options = [
+      "foo",
+      "poo",
+    ];
+    return [
+      '#type' => 'select',
+      '#title' => $this->propDefinition['title'],
+      '#default_value' => $this->configuration['form_value'],
+      "#options" => $options,
+    ];
+  }
+
+  /**
+   *
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/src/Plugin/UiPatterns/Source/SiteNameSource.php b/src/Plugin/UiPatterns/Source/SiteNameSource.php
new file mode 100644
index 00000000..682fc70f
--- /dev/null
+++ b/src/Plugin/UiPatterns/Source/SiteNameSource.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\Source;
+
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Plugin implementation of the source_provider.
+ *
+ * @Source(
+ *   id = "sitename",
+ *   label = @Translation("Sitename"),
+ *   description = @Translation("Foo description."),
+ *   prop_types = {
+ *     "string"
+ *   }
+ * )
+ */
+final class SiteNameSource extends SourcePluginBase {
+
+  /**
+   *
+   */
+  public function getData(): mixed {
+    return \Drupal::config('system.site') > get('name');
+  }
+
+  /**
+   *
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/src/Plugin/UiPatterns/Source/TextfieldWidget.php b/src/Plugin/UiPatterns/Source/TextfieldWidget.php
new file mode 100644
index 00000000..110fe0e3
--- /dev/null
+++ b/src/Plugin/UiPatterns/Source/TextfieldWidget.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\Source;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Plugin implementation of the source_provider.
+ *
+ * @Source(
+ *   id = "textfield",
+ *   label = @Translation("Textfield"),
+ *   description = @Translation("One-line text field."),
+ *   prop_types = {
+ *     "string"
+ *   }
+ * )
+ */
+final class TextfieldWidget extends SourcePluginBase {
+
+  /**
+   *
+   */
+  public function getData(): mixed {
+    return 'Nice Site name';
+  }
+
+  /**
+   *
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    return [
+      '#type' => 'textfield',
+      '#title' => $this->propDefinition['title'],
+      '#default_value' => $this->configuration['form_value'],
+    ];
+  }
+
+  /**
+   *
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/src/Plugin/UiPatterns/Source/UrlWidget.php b/src/Plugin/UiPatterns/Source/UrlWidget.php
new file mode 100644
index 00000000..d140506f
--- /dev/null
+++ b/src/Plugin/UiPatterns/Source/UrlWidget.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns\Plugin\UiPatterns\Source;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Plugin implementation of the source_provider.
+ *
+ * @Source(
+ *   id = "url",
+ *   label = @Translation("Url"),
+ *   description = @Translation("Input of a URL."),
+ *   prop_types = {
+ *     "url"
+ *   }
+ * )
+ */
+final class UrlWidget extends SourcePluginBase {
+
+  /**
+   *
+   */
+  public function getData(): mixed {
+    return 'https://example.org';
+  }
+
+  /**
+   *
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    return [
+      '#type' => 'url',
+      '#title' => $this->propDefinition['title'],
+      '#default_value' => $this->configuration['form_value'],
+    ];
+  }
+
+  /**
+   *
+   */
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/src/PropTypeInterface.php b/src/PropTypeInterface.php
new file mode 100644
index 00000000..09b3db06
--- /dev/null
+++ b/src/PropTypeInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Drupal\ui_patterns;
+
+/**
+ * Interface for prop_type plugins.
+ */
+interface PropTypeInterface {
+
+  /**
+   * Returns the translated plugin label.
+   *
+   * @return string
+   *   The translated title.
+   */
+  public function label();
+
+  /**
+   * Returns the schema.
+   *
+   * @return array
+   *   The schema.
+   */
+  public function getSchema():array;
+
+}
diff --git a/src/PropTypePluginBase.php b/src/PropTypePluginBase.php
new file mode 100644
index 00000000..f143854b
--- /dev/null
+++ b/src/PropTypePluginBase.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Drupal\ui_patterns;
+
+use Drupal\Component\Plugin\PluginBase;
+
+/**
+ * Base class for prop_type plugins.
+ */
+abstract class PropTypePluginBase extends PluginBase implements PropTypeInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function label() {
+    // Cast the label to a string since it is a TranslatableMarkup object.
+    return (string) $this->pluginDefinition['label'];
+  }
+
+  /**
+   *
+   */
+  public function getSchema():array {
+    return (array) $this->pluginDefinition['schema'];
+  }
+
+}
diff --git a/src/PropTypePluginManager.php b/src/PropTypePluginManager.php
new file mode 100644
index 00000000..4c29e339
--- /dev/null
+++ b/src/PropTypePluginManager.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Drupal\ui_patterns;
+
+use Drupal\Component\Plugin\FallbackPluginManagerInterface;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\ui_patterns\Utils\SchemaCompatibilityChecker;
+
+/**
+ * PropType plugin manager.
+ */
+class PropTypePluginManager extends DefaultPluginManager implements FallbackPluginManagerInterface {
+
+  /**
+   * Constructs PropTypePluginManager object.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct(
+      'Plugin/UiPatterns/PropType',
+      $namespaces,
+      $module_handler,
+      'Drupal\ui_patterns\PropTypeInterface',
+      'Drupal\ui_patterns\Annotation\PropType'
+    );
+    $this->alterInfo('prop_type_info');
+    $this->setCacheBackend($cache_backend, 'ui_patterns_prop_type_plugins');
+  }
+
+  /**
+   *
+   */
+  public function getPropTypePlugin(array $prop_schema): ?PropTypeInterface {
+    $definition = $this->getPropTypeDefinition($prop_schema);
+    if ($definition !== NULL) {
+      return $this->createInstance($definition['id'], []);
+    }
+    return $this->createInstance('unknown_prop_type', []);
+  }
+
+  /**
+   *
+   */
+  public function getSortedDefinitions() {
+    $definitions = $this->getDefinitions();
+    usort($definitions, function ($a, $b) {
+      return $a['priority'] ?? 1 > $b['priority'] ?? 1;
+    });
+    return $definitions;
+  }
+
+  /**
+   *
+   */
+  public function getPropTypeDefinition(array $prop_schema): ?array {
+    if (isset($prop_schema['$ref']) && str_contains($prop_schema['$ref'], "ui-patterns://")) {
+      $prop_type_id = str_replace("ui-patterns://", "", $prop_schema['$ref']);
+      return $this->getDefinition($prop_type_id);
+    }
+    $definitions = $this->getSortedDefinitions();
+    $compatibilityChecker = new SchemaCompatibilityChecker();
+    foreach ($definitions as $definition) {
+      if ($compatibilityChecker->isCompatible($prop_schema, $definition['schema'])) {
+        return $definition;
+      }
+    }
+    return NULL;
+  }
+
+  /**
+   *
+   */
+  public function getFallbackPluginId($plugin_id, array $configuration = []) {
+    return 'unknown_prop_type';
+  }
+
+}
diff --git a/src/Sdc/ComponentPluginManagerDecorator.php b/src/Sdc/ComponentPluginManagerDecorator.php
new file mode 100644
index 00000000..4ac91ff8
--- /dev/null
+++ b/src/Sdc/ComponentPluginManagerDecorator.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace Drupal\ui_patterns\Sdc;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Extension\ThemeHandlerInterface;
+use Drupal\Core\File\FileSystemInterface;
+use Drupal\Core\Theme\ThemeManagerInterface;
+use Drupal\sdc\Component\ComponentValidator;
+use Drupal\sdc\Component\SchemaCompatibilityChecker;
+use Drupal\sdc\ComponentNegotiator;
+use Drupal\sdc\ComponentPluginManager;
+use Drupal\sdc\Plugin\Component;
+use Drupal\ui_patterns\PropTypePluginManager;
+use Drupal\ui_patterns\SourcePluginManager;
+
+/**
+ * Plugin Manager for *.ui_patterns.yml configuration files.
+ *
+ * Plugin Manager overwrites getDiscovery() to provide a decorated
+ * Discovery. Decoration of the service seems not possible for me.
+ * Probably there is more gentle way.
+ *
+ * @see plugin_api
+ *
+ * @internal
+ */
+abstract class ComponentPluginManagerDecorator extends ComponentPluginManager {
+
+  /**
+   *
+   */
+  public function __construct(
+    protected ComponentPluginManager $parentSdcPluginManager,
+    protected PropTypePluginManager $propTypePluginManager,
+    protected SourcePluginManager $sourcePluginManager,
+    ModuleHandlerInterface $module_handler,
+    ThemeHandlerInterface $themeHandler,
+    CacheBackendInterface $cacheBackend,
+    ConfigFactoryInterface $configFactory,
+    ThemeManagerInterface $themeManager,
+    ComponentNegotiator $componentNegotiator,
+    FileSystemInterface $fileSystem,
+    SchemaCompatibilityChecker $compatibilityChecker,
+    ComponentValidator $componentValidator,
+    string $appRoot,
+  ) {
+    parent::__construct(
+      $module_handler,
+      $themeHandler,
+      $cacheBackend,
+      $configFactory,
+      $themeManager,
+      $componentNegotiator,
+      $fileSystem,
+      $compatibilityChecker,
+      $componentValidator,
+      $appRoot
+    );
+    $this->setCacheBackend($cacheBackend, $this->getCacheKey());
+  }
+
+  /**
+   *
+   */
+  public function createInstance($plugin_id, array $configuration = []): Component {
+    if (parent::hasDefinition($plugin_id)) {
+      return parent::createInstance($plugin_id, $configuration);
+    }
+    else {
+      return $this->parentSdcPluginManager->createInstance($plugin_id, $configuration);
+    }
+  }
+
+  /**
+   * Returns the cache key for the decorated service.
+   *
+   * @return string
+   *   The cache key.
+   */
+  abstract protected function getCacheKey();
+
+  /**
+   * {@inheritdoc}
+   */
+  public function find(string $component_id): Component {
+    if (parent::hasDefinition($component_id)) {
+      return parent::find($component_id);
+    }
+    return $this->parentSdcPluginManager->find($component_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllComponents(): array {
+    $original_components = $this->parentSdcPluginManager->getAllComponents();
+    return array_merge($original_components, parent::getAllComponents());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinitions() {
+    $decorated_definitions = parent::getDefinitions();
+    $original_definitions = $this->parentSdcPluginManager->getDefinitions();
+    return array_merge($original_definitions, $decorated_definitions);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinition($plugin_id, $exception_on_invalid = TRUE) {
+    $original_definition = parent::getDefinition($plugin_id, FALSE);
+    return $original_definition ?? $this->parentSdcPluginManager->getDefinition($plugin_id, $exception_on_invalid);
+  }
+
+}
diff --git a/src/Sdc/UiPatternsSdcPluginManager.php b/src/Sdc/UiPatternsSdcPluginManager.php
new file mode 100644
index 00000000..b5b123ac
--- /dev/null
+++ b/src/Sdc/UiPatternsSdcPluginManager.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace Drupal\ui_patterns\Sdc;
+
+/**
+ * Plugin Manager for....
+ *
+ * @see plugin_api
+ *
+ * @internal
+ */
+class UiPatternsSdcPluginManager extends ComponentPluginManagerDecorator {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getCacheKey() {
+    return 'ui_patterns';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterDefinitions(&$definitions) {
+    parent::alterDefinitions($definitions);
+    foreach ($definitions as $component_id => $definition) {
+      if (!isset($definition['props'])) {
+        continue;
+      }
+      if (!isset($definition['props']['properties'])) {
+        continue;
+      }
+      foreach ($definition['props']['properties'] as $prop_id => $prop) {
+        $prop_type = $this->propTypePluginManager->getPropTypePlugin($prop);
+        $sources = $this->sourcePluginManager->getSourcePlugins($prop_type->getPluginId(), $prop_id, $prop);
+        $prop['ui_patterns']['type_definition'] = $prop_type;
+        $prop['ui_patterns']['source'] = $sources;
+        $definition['props']['properties'][$prop_id] = $prop;
+      }
+      $definitions[$component_id] = $definition;
+    }
+  }
+
+  /**
+   * Do we move this method to ui_patterns_legacy?
+   */
+  public function getNamespacedId(string $component_id): string {
+    $parts = explode(":", $component_id);
+    if (count(array_filter($parts)) === 2) {
+      // Already namespaced.
+      return $component_id;
+    }
+    if (count(array_filter($parts)) > 2) {
+      // Unexpected situation.
+      return $component_id;
+    }
+    $components = $this->getAllComponents();
+    // @todo Search first in current active theme, then parents themes, then modules.
+    foreach ($components as $component) {
+      if ($component->getPluginDefinition()["machineName"] === $component_id) {
+        return $component->getPluginId();
+      }
+    }
+    return $component_id;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getGroupedDefinitions(?array $definitions = NULL): array {
+    $definitions = $definitions ?: $this->getDefinitions();
+    $groups = [];
+    foreach ($definitions as $id => $definition) {
+      $group = $definition["group"] ?? "Other";
+      $groups[$group][$id] = $definition;
+    }
+    return $groups;
+  }
+
+  /**
+   * Stories slots have no "#" prefix in render arrays. Let's add them.
+   * A bit like UI Patterns 1.x's PatternPreview::getPreviewMarkup()
+   * This method belongs here because used by both ui_patterns_library and
+   * ui_patterns_legacy.
+   */
+  public static function processStoriesSlots(array $slots): array {
+    foreach ($slots as $slot_id => $slot) {
+      if (!is_array($slot)) {
+        continue;
+      }
+      if (array_is_list($slot)) {
+        $slots[$slot_id] = self::processStoriesSlots($slot);
+      }
+      $slot_keys = array_keys($slot);
+      $render_keys = ["theme", "type", "markup", "plain_text"];
+      if (count(array_intersect($slot_keys, $render_keys)) > 0) {
+        foreach ($slot as $key => $value) {
+          if (is_array($value)) {
+            $value = self::processStoriesSlots($value);
+          }
+          $slots[$slot_id]["#" . $key] = $value;
+          unset($slots[$slot_id][$key]);
+        }
+      }
+    }
+    return $slots;
+  }
+
+}
diff --git a/src/SourceInterface.php b/src/SourceInterface.php
new file mode 100644
index 00000000..5a7113b0
--- /dev/null
+++ b/src/SourceInterface.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns;
+
+use Drupal\Component\Plugin\ConfigurableInterface;
+use Drupal\Core\Plugin\PluginFormInterface;
+
+/**
+ * Interface for source_provider plugins.
+ */
+interface SourceInterface extends PluginFormInterface, ConfigurableInterface {
+
+  /**
+   * Returns the translated plugin label.
+   */
+  public function label(): string;
+
+  /**
+   *
+   */
+  public function getData(): mixed;
+
+  /**
+   *
+   */
+  public function getPropId(): string;
+
+  /**
+   *
+   */
+  public function getPropDefinition(): mixed;
+
+}
diff --git a/src/SourcePluginBase.php b/src/SourcePluginBase.php
new file mode 100644
index 00000000..1c4bea10
--- /dev/null
+++ b/src/SourcePluginBase.php
@@ -0,0 +1,108 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\ui_patterns;
+
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Base class for source_provider plugins.
+ */
+abstract class SourcePluginBase extends PluginBase implements SourceInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->setConfiguration($configuration);
+  }
+
+  protected array $propDefinition;
+
+  protected $propId;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function label(): string {
+    // Cast the label to a string since it is a TranslatableMarkup object.
+    return (string) $this->pluginDefinition['label'];
+  }
+
+  /**
+   *
+   */
+  public function buildConfigurationForm(
+    array $form,
+    FormStateInterface $form_state
+  ) {
+    return [];
+  }
+
+  /**
+   *
+   */
+  public function validateConfigurationForm(
+    array &$form,
+    FormStateInterface $form_state
+  ) {
+
+  }
+
+  /**
+   *
+   */
+  public function submitConfigurationForm(
+    array &$form,
+    FormStateInterface $form_state
+  ) {
+    $parents = $form['#parents'];
+    unset($parents[0]);
+    $value = $form_state->getValue($parents);
+    $this->configuration['form_value'] = $value;
+  }
+
+  /**
+   *
+   */
+  public function getConfiguration() {
+    return $this->configuration;
+  }
+
+  /**
+   *
+   */
+  public function setConfiguration(array $configuration) {
+    if (isset($configuration['prop_definition'])) {
+      $this->propDefinition = $configuration['prop_definition'];
+    }
+    if (isset($configuration['prop_id'])) {
+      $this->propId = $configuration['prop_id'];
+    }
+    $this->configuration = $configuration;
+  }
+
+  /**
+   *
+   */
+  abstract public function defaultConfiguration();
+
+  /**
+   *
+   */
+  public function getPropId(): string {
+    return $this->propId;
+  }
+
+  /**
+   *
+   */
+  public function getPropDefinition(): array {
+    return $this->propDefinition;
+  }
+
+}
diff --git a/src/SourcePluginManager.php b/src/SourcePluginManager.php
new file mode 100644
index 00000000..2bfe443c
--- /dev/null
+++ b/src/SourcePluginManager.php
@@ -0,0 +1,71 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\ui_patterns;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\ui_patterns\Annotation\Source;
+
+/**
+ * Source plugin manager.
+ */
+final class SourcePluginManager extends DefaultPluginManager {
+
+  /**
+   * Constructs the object.
+   */
+  public function __construct(
+    \Traversable $namespaces,
+    CacheBackendInterface $cache_backend,
+    ModuleHandlerInterface $module_handler
+  ) {
+    parent::__construct(
+      'Plugin/UiPatterns/Source',
+      $namespaces,
+      $module_handler,
+      SourceInterface::class,
+      Source::class
+    );
+    $this->alterInfo('ui_patterns_source_info');
+    $this->setCacheBackend($cache_backend, 'ui_patterns_source_plugins');
+  }
+
+  /**
+   *
+   */
+  public function getSourceDefinitions($prop_type_id) {
+    $definitions = $this->getDefinitions();
+    $sources = [];
+    foreach ($definitions as $definition) {
+      if (isset($definition['prop_types']) && in_array(
+          $prop_type_id,
+          $definition['prop_types']
+        )) {
+        $sources[] = $definition;
+      }
+    }
+    return $sources;
+  }
+
+  /**
+   *
+   */
+  public function getSourcePlugins($prop_type_id, $prop_id, $prop_definition):array {
+    $definitions = $this->getSourceDefinitions($prop_type_id);
+    $sources = [];
+    foreach ($definitions as $definition) {
+      $sources[] = $this->createInstance(
+        $definition['id'],
+        [
+          'prop_id' => $prop_id,
+          'prop_definition' => $prop_definition,
+        ]
+      );
+    }
+    return $sources;
+  }
+
+}
diff --git a/src/StreamWrapper/UiPatternsPropTypeStreamWrapper.php b/src/StreamWrapper/UiPatternsPropTypeStreamWrapper.php
new file mode 100644
index 00000000..41fcb0a7
--- /dev/null
+++ b/src/StreamWrapper/UiPatternsPropTypeStreamWrapper.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Drupal\ui_patterns\StreamWrapper;
+
+use Drupal\Core\StreamWrapper\LocalReadOnlyStream;
+
+/**
+ * Defines the read-only ui-patterns:// stream wrapper for theme files.
+ */
+class UiPatternsPropTypeStreamWrapper extends LocalReadOnlyStream {
+
+  /**
+   *
+   */
+  public function stream_open($uri, $mode, $options, &$opened_path) {
+
+    $plugin_id = str_replace('ui-patterns://', '', $uri);
+    $plugin = \Drupal::service('plugin.manager.ui_patterns_prop_type')->getDefinition($plugin_id);
+    $stream = fopen('php://memory', 'r+');
+    fwrite($stream, json_encode($plugin['schema']));
+    rewind($stream);
+    $this->handle = $stream;
+    return $stream;
+  }
+
+  /**
+   *
+   */
+  public function getDirectoryPath() {
+    return NULL;
+  }
+
+  /**
+   *
+   */
+  public function getName() {
+    return 'ui_patterns';
+  }
+
+  /**
+   *
+   */
+  public function getDescription() {
+    return 'ui_patterns';
+  }
+
+  /**
+   *
+   */
+  public function getExternalUrl() {
+    return NULL;
+  }
+
+}
diff --git a/src/Template/AttributesFilterTrait.php b/src/Template/AttributesFilterTrait.php
new file mode 100644
index 00000000..49c2668c
--- /dev/null
+++ b/src/Template/AttributesFilterTrait.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace Drupal\ui_patterns\Template;
+
+use Drupal\Core\Template\Attribute;
+use Drupal\Core\Template\AttributeHelper;
+
+/**
+ * Methods for the set_attribute and add_class filters.
+ *
+ * In a trait, to be usable in other places.
+ */
+trait AttributesFilterTrait {
+
+  /**
+   * Add a value into the class attributes of a given element.
+   *
+   * @param mixed $element
+   *   A render array.
+   * @param string[]|string ...$classes
+   *   The classes to add on element. Arguments can include string keys directly
+   *   or arrays of string keys.
+   *
+   * @return mixed
+   *   The element with the given class(es) in attributes or the unchanged
+   *   element if passed value is not an array.
+   *
+   * @see Drupal\Core\Template\TwigExtension::addClass()
+   */
+  public function addClass(mixed $element, ...$classes): mixed {
+    if (!\is_array($element)) {
+      return $element;
+    }
+    if ($this->arrayIsList($element)) {
+      foreach ($element as $index => $item) {
+        if (!\is_array($item)) {
+          continue;
+        }
+        $element[$index] = $this->addClass($item, ...$classes);
+      }
+      return $element;
+    }
+    $attributes = new Attribute($element['#attributes'] ?? []);
+    $attributes->addClass(...$classes);
+    $element['#attributes'] = $attributes->toArray();
+
+    // Make sure element gets rendered again.
+    unset($element['#printed']);
+
+    return $element;
+  }
+
+  /**
+   * Set attribute on a given element.
+   *
+   * @param mixed $element
+   *   A render array.
+   * @param string $name
+   *   The attribute name.
+   * @param mixed $value
+   *   (optional) The attribute value.
+   *
+   * @return mixed
+   *   The element with the given sanitized attribute's value or the unchanged
+   *   element if passed value is not an array.
+   *
+   * @see Drupal\Core\Template\TwigExtension::setAttribute()
+   */
+  public function setAttribute(mixed $element, string $name, mixed $value = NULL): mixed {
+    if (!\is_array($element)) {
+      return $element;
+    }
+    if ($this->arrayIsList($element)) {
+      foreach ($element as $index => $item) {
+        if (!\is_array($item)) {
+          continue;
+        }
+        $element[$index] = $this->setAttribute($item, $name, $value);
+      }
+      return $element;
+    }
+    $element['#attributes'] = AttributeHelper::mergeCollections(
+      $element['#attributes'] ?? [],
+      new Attribute([$name => $value])
+    );
+
+    // Make sure element gets rendered again.
+    unset($element['#printed']);
+
+    return $element;
+  }
+
+  /**
+   * Checks whether a given array is a list.
+   *
+   * Same as array_is_list() but compatible with PHP8.
+   *
+   * @param array $array
+   *   The array being evaluated.
+   *
+   * @return bool
+   *   Returns true if array is a list, false otherwise.
+   *
+   * @see https://www.php.net/manual/en/function.array-is-list.php#126794
+   */
+  private function arrayIsList(array $array): bool {
+    $i = -1;
+    foreach ($array as $k => $v) {
+      ++$i;
+      if ($k !== $i) {
+        return FALSE;
+      }
+    }
+    return TRUE;
+  }
+
+}
diff --git a/src/Template/TwigExtension.php b/src/Template/TwigExtension.php
index b98cf4cb..d946895b 100644
--- a/src/Template/TwigExtension.php
+++ b/src/Template/TwigExtension.php
@@ -2,12 +2,18 @@
 
 namespace Drupal\ui_patterns\Template;
 
+use Twig\Extension\AbstractExtension;
+use Twig\TwigFilter;
+use Twig\TwigFunction;
+
 /**
  * Twig extension providing UI Patterns-specific functionalities.
  *
  * @package Drupal\ui_patterns\Template
  */
-class TwigExtension extends \Twig_Extension {
+class TwigExtension extends AbstractExtension {
+
+  use AttributesFilterTrait;
 
   /**
    * {@inheritdoc}
@@ -21,59 +27,44 @@ public function getName() {
    */
   public function getFunctions() {
     return [
-      new \Twig_SimpleFunction('pattern', [
+      new TwigFunction('component', [
         $this,
-        'renderPattern',
-      ]),
-      new \Twig_SimpleFunction('pattern_preview', [
-        $this,
-        'renderPatternPreview',
+        'renderComponent',
       ]),
     ];
   }
 
   /**
-   * Render given pattern.
-   *
-   * @param string $id
-   *   Pattern ID.
-   * @param array $fields
-   *   Pattern fields.
-   * @param string $variant
-   *   Variant name.
-   *
-   * @return array
-   *   Pattern render array.
-   *
-   * @see \Drupal\ui_patterns\Element\Pattern
+   * {@inheritdoc}
    */
-  public function renderPattern($id, array $fields = [], $variant = "") {
+  public function getFilters() {
     return [
-      '#type' => 'pattern',
-      '#id' => $id,
-      '#fields' => $fields,
-      '#variant' => $variant,
+      new TwigFilter('add_class', [$this, 'addClass']),
+      new TwigFilter('set_attribute', [$this, 'setAttribute']),
     ];
   }
 
   /**
-   * Render given pattern.
+   * Render given component.
    *
-   * @param string $id
-   *   Pattern ID.
-   * @param string $variant
-   *   Variant name.
+   * @param string $component_id
+   *   Component ID.
+   * @param array $slots
+   *   Pattern slots.
+   * @param array $props
+   *   Pattern props.
    *
    * @return array
    *   Pattern render array.
    *
-   * @see \Drupal\ui_patterns\Element\Pattern
+   * @see \Drupal\sdc\Element\ComponentElement
    */
-  public function renderPatternPreview($id, $variant = "") {
+  public function renderComponent(string $component_id, array $slots = [], array $props = []) {
     return [
-      '#type' => 'pattern_preview',
-      '#id' => $id,
-      '#variant' => $variant,
+      '#type' => 'component',
+      '#component' => $component_id,
+      '#slots' => $slots,
+      '#props' => $props,
     ];
   }
 
diff --git a/src/TypedData/PatternDataDefinition.php b/src/TypedData/PatternDataDefinition.php
deleted file mode 100644
index 8270128a..00000000
--- a/src/TypedData/PatternDataDefinition.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns\TypedData;
-
-use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\TypedData\ListDataDefinition;
-use Drupal\Core\TypedData\MapDataDefinition;
-
-/**
- * Class PatternDefinition.
- *
- * @package Drupal\ui_patterns\Plugin\DataType
- */
-class PatternDataDefinition extends MapDataDefinition {
-
-  /**
-   * Valid machine name string.
-   */
-  const MACHINE_NAME = '/^(?!(%s)$)(?=[A-Za-z0-9_-]+$).*$/';
-
-  /**
-   * Reserved words.
-   *
-   * @var array
-   */
-  protected $reserved = [
-    'id',
-    'type',
-    'theme',
-    'context',
-    'use',
-    'attributes',
-  ];
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getPropertyDefinitions() {
-    $this->setMainPropertyName('id')
-      ->setPropertyDefinition('id', $this->getMachineNameDefinition()->setRequired(TRUE))
-      ->setPropertyDefinition('label', DataDefinition::create('string')->setRequired(TRUE))
-      ->setPropertyDefinition('base path', DataDefinition::create('string')->setRequired(TRUE))
-      ->setPropertyDefinition('file name', DataDefinition::create('string')->setRequired(TRUE))
-      ->setPropertyDefinition('provider', DataDefinition::create('string')->setRequired(TRUE))
-      ->setPropertyDefinition('fields', $this->getFieldsDefinition())
-      ->setPropertyDefinition('variants', $this->getVariantsDefinition())
-      ->setPropertyDefinition('theme hook', DataDefinition::create('string')->setRequired(TRUE))
-      ->setPropertyDefinition('description', DataDefinition::create('string'))
-      ->setPropertyDefinition('use', DataDefinition::create('string'))
-      ->setPropertyDefinition('tags', ListDataDefinition::create('string'))
-      ->setPropertyDefinition('custom theme hook', DataDefinition::create('boolean'))
-      ->setPropertyDefinition('template', DataDefinition::create('string'))
-      ->setPropertyDefinition('libraries', DataDefinition::create('any'));
-    return $this->propertyDefinitions;
-  }
-
-  /**
-   * Get valid machine name definition.
-   *
-   * @return \Drupal\Core\TypedData\DataDefinition
-   *   Data definition instance.
-   */
-  protected function getMachineNameDefinition() {
-    return DataDefinition::create('string')
-      ->addConstraint('Regex', sprintf(self::MACHINE_NAME, implode('|', $this->reserved)));
-  }
-
-  /**
-   * Get definition for 'field' property.
-   *
-   * @return \Drupal\Core\TypedData\ListDataDefinition
-   *   Data definition instance.
-   */
-  protected function getFieldsDefinition() {
-    return new ListDataDefinition([], MapDataDefinition::create()
-      ->setPropertyDefinition('name', $this->getMachineNameDefinition()->setRequired(TRUE))
-      ->setPropertyDefinition('label', DataDefinition::create('string')->setRequired(TRUE))
-      ->setPropertyDefinition('type', $this->getMachineNameDefinition())
-      ->setPropertyDefinition('description', DataDefinition::create('string'))
-      ->setPropertyDefinition('preview', DataDefinition::create('any')));
-  }
-
-  /**
-   * Get definition for 'variant' property.
-   *
-   * @return \Drupal\Core\TypedData\ListDataDefinition
-   *   Data definition instance.
-   */
-  protected function getVariantsDefinition() {
-    return new ListDataDefinition([], MapDataDefinition::create()
-      ->setPropertyDefinition('name', $this->getMachineNameDefinition()->setRequired(TRUE))
-      ->setPropertyDefinition('label', DataDefinition::create('string')->setRequired(TRUE))
-      ->setPropertyDefinition('description', DataDefinition::create('string')));
-  }
-
-}
diff --git a/src/UiPatterns.php b/src/UiPatterns.php
deleted file mode 100644
index c480b712..00000000
--- a/src/UiPatterns.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns;
-
-/**
- * UI Patterns factory class.
- *
- * @package Drupal\ui_patterns
- */
-class UiPatterns {
-
-  /**
-   * Get pattern manager instance.
-   *
-   * @return \Drupal\ui_patterns\UiPatternsManager
-   *   UI Patterns manager instance.
-   */
-  public static function getManager() {
-    return \Drupal::service('plugin.manager.ui_patterns');
-  }
-
-  /**
-   * Get pattern field sources manager instance.
-   *
-   * @return \Drupal\ui_patterns\UiPatternsSourceManager
-   *   UI Patterns field sources manager instance.
-   */
-  public static function getSourceManager() {
-    return \Drupal::service('plugin.manager.ui_patterns_source');
-  }
-
-  /**
-   * Get pattern definition.
-   *
-   * @param string $id
-   *   Pattern ID.
-   *
-   * @return \Drupal\ui_patterns\Definition\PatternDefinition
-   *   Pattern object instance.
-   */
-  public static function getPatternDefinition($id) {
-    return \Drupal::service('plugin.manager.ui_patterns')->getDefinition($id);
-  }
-
-  /**
-   * Get pattern definitions.
-   *
-   * @return \Drupal\ui_patterns\Definition\PatternDefinition[]
-   *   Pattern object instance.
-   */
-  public static function getPatternDefinitions() {
-    return \Drupal::service('plugin.manager.ui_patterns')->getDefinitions();
-  }
-
-}
diff --git a/src/UiPatternsManager.php b/src/UiPatternsManager.php
deleted file mode 100644
index 08d4ff6b..00000000
--- a/src/UiPatternsManager.php
+++ /dev/null
@@ -1,110 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns;
-
-use Drupal\Component\Plugin\PluginManagerInterface;
-use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Extension\ThemeHandlerInterface;
-use Drupal\Core\Plugin\DefaultPluginManager;
-
-/**
- * Provides the default ui_patterns manager.
- *
- * @method \Drupal\ui_patterns\Definition\PatternDefinition getDefinition($plugin_id, $exception_on_invalid = TRUE)
- */
-class UiPatternsManager extends DefaultPluginManager implements PluginManagerInterface {
-
-  use StringTranslationTrait;
-
-  /**
-   * The theme handler.
-   *
-   * @var \Drupal\Core\Extension\ThemeHandlerInterface
-   */
-  protected $themeHandler;
-
-  /**
-   * An array of pattern theme hooks for fast lookup on not cached pages.
-   *
-   * @var array
-   */
-  protected $patternHooks = [];
-
-  /**
-   * UiPatternsManager constructor.
-   */
-  public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, CacheBackendInterface $cache_backend) {
-    parent::__construct('Plugin/UiPatterns/Pattern', $namespaces, $module_handler, 'Drupal\ui_patterns\Plugin\PatternInterface', 'Drupal\ui_patterns\Annotation\UiPattern');
-    $this->moduleHandler = $module_handler;
-    $this->themeHandler = $theme_handler;
-    $this->alterInfo('ui_patterns_info');
-    $this->setCacheBackend($cache_backend, 'ui_patterns', ['ui_patterns']);
-  }
-
-  /**
-   * Get pattern objects.
-   *
-   * @return \Drupal\ui_patterns\Plugin\PatternBase[]
-   *   Pattern objects.
-   */
-  public function getPatterns() {
-    $patterns = [];
-    foreach ($this->getDefinitions() as $definition) {
-      $patterns[] = $this->getFactory()->createInstance($definition->id());
-    }
-    return $patterns;
-  }
-
-  /**
-   * Return pattern definitions.
-   *
-   * @return \Drupal\ui_patterns\Definition\PatternDefinition[]
-   *   Pattern definitions.
-   */
-  public function getDefinitions() {
-    $definitions = $this->getCachedDefinitions();
-    if (!isset($definitions)) {
-      // Remove derivative id from pattern definitions keys.
-      // @todo make sure validation takes care of ensuring ids are unique.
-      $definitions = [];
-      foreach ($this->findDefinitions() as $id => $definition) {
-        $definitions[$definition['id']] = $definition;
-        unset($definitions[$id]);
-      }
-      $this->setCachedDefinitions($definitions);
-    }
-    return $definitions;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getPatternsOptions() {
-    return array_map(function ($option) {
-      return $option['label'];
-    }, $this->getDefinitions());
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function isPatternHook($hook) {
-    // Improve performance on not cached pages.
-    if (empty($this->patternHooks)) {
-      foreach ($this->getDefinitions() as $definition) {
-        $this->patternHooks[$definition->getThemeHook()] = $definition->getThemeHook();
-      }
-    }
-    return !empty($this->patternHooks[$hook]);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function providerExists($provider) {
-    return $this->moduleHandler->moduleExists($provider) || $this->themeHandler->themeExists($provider);
-  }
-
-}
diff --git a/src/UiPatternsSourceManager.php b/src/UiPatternsSourceManager.php
deleted file mode 100644
index d9cccc6a..00000000
--- a/src/UiPatternsSourceManager.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns;
-
-use Drupal\Core\Plugin\DefaultPluginManager;
-use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-
-/**
- * Provides the UI Patterns Source plugin manager.
- */
-class UiPatternsSourceManager extends DefaultPluginManager {
-
-  /**
-   * Constructor for UiPatternsSourceManager objects.
-   */
-  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/UiPatterns/Source', $namespaces, $module_handler, 'Drupal\ui_patterns\Plugin\PatternSourceInterface', 'Drupal\ui_patterns\Annotation\UiPatternsSource');
-    $this->alterInfo('ui_patterns_ui_patterns_source_info');
-    $this->setCacheBackend($cache_backend, 'ui_patterns_ui_patterns_source_plugins');
-  }
-
-  /**
-   * Filter definitions by given tag.
-   *
-   * @param string $tag
-   *   Tag used on plugin annotation.
-   *
-   * @return array
-   *   List of definitions tagged with given tag.
-   */
-  public function getDefinitionsByTag($tag) {
-    return array_filter($this->getDefinitions(), function ($definition) use ($tag) {
-      return in_array($tag, $definition['tags']);
-    });
-  }
-
-  /**
-   * Get field source definitions by specified tags.
-   *
-   * @param string $tag
-   *   Field source tag.
-   * @param array $context
-   *   Plugin context.
-   *
-   * @return \Drupal\ui_patterns\Definition\PatternSourceField[]
-   *   List of source fields.
-   */
-  public function getFieldsByTag($tag, array $context) {
-    /** @var \Drupal\ui_patterns\Plugin\PatternSourceInterface $plugin */
-    $fields = [];
-    foreach ($this->getDefinitionsByTag($tag) as $id => $definition) {
-      $plugin = $this->createInstance($id, ['context' => $context]);
-      foreach ($plugin->getSourceFields() as $field) {
-        $fields[$field->getFieldKey()] = $field;
-      }
-    }
-
-    return $fields;
-  }
-
-}
diff --git a/src/Utils/SchemaCompatibilityChecker.php b/src/Utils/SchemaCompatibilityChecker.php
new file mode 100644
index 00000000..0bb5937d
--- /dev/null
+++ b/src/Utils/SchemaCompatibilityChecker.php
@@ -0,0 +1,391 @@
+<?php
+
+namespace Drupal\ui_patterns\Utils;
+
+/**
+ * Checks whether two schemas are compatible.
+ *
+ * Used for prop typing.
+ *
+ * Not the same as Drupal\sdc\Component\SchemaCompatibilityChecker which has
+ * different rules and a different goal: validating replace mechanism.
+ */
+class SchemaCompatibilityChecker {
+
+  /**
+   * Checks if the second schema is compatible with the first one.
+   *
+   * @param array $checked_schema
+   *   The schema that should be compatible with the other one.
+   * @param array $reference_schema
+   *   The schema to check compatibility against.
+   *
+   * @return bool
+   */
+  public function isCompatible(array $checked_schema, array $reference_schema): bool {
+    $checked_schema = $this->canonicalize($checked_schema);
+    $reference_schema = $this->canonicalize($reference_schema);
+    if ($this->isSame($checked_schema, $reference_schema)) {
+      return TRUE;
+    }
+    if (isset($checked_schema["type"]) && isset($reference_schema["type"])) {
+      return $this->isTypeCompatible($checked_schema, $reference_schema);
+    }
+    if (isset($checked_schema["anyOf"]) || isset($reference_schema["anyOf"])) {
+      return $this->isAnyOfCompatible($checked_schema, $reference_schema);
+    }
+    return FALSE;
+  }
+
+  /**
+   *
+   */
+  protected function isSame($checked_schema, $reference_schema): bool {
+    return (serialize($checked_schema) === serialize($reference_schema));
+  }
+
+  /**
+   *
+   */
+  protected function isTypeCompatible($checked_schema, $reference_schema): bool {
+    if (is_array($checked_schema["type"]) || is_array($checked_schema["type"])) {
+      // Because of self::resolveMultipleTypes() we are not supposed to meet this
+      // situation.
+      return FALSE;
+    }
+    if ($checked_schema["type"] !== $reference_schema["type"]) {
+      // Integers are numbers, but numbers are not always integer.
+      if (!($checked_schema["type"] === "integer" && $reference_schema["type"] === "number")) {
+        return FALSE;
+      }
+    }
+    // Now we know $checked_schema and $reference_schema have the same type.
+    // So, testing $checked_schema type is enough.
+    return match ($checked_schema["type"]) {
+      'null' => TRUE,
+      'boolean' => TRUE,
+      'object' => $this->isObjectCompatible($checked_schema, $reference_schema),
+      'array' => $this->isArrayCompatible($checked_schema, $reference_schema),
+      'number' => $this->isNumberCompatible($checked_schema, $reference_schema),
+      'integer' => $this->isIntegerCompatible($checked_schema, $reference_schema),
+      'string' => $this->isStringCompatible($checked_schema, $reference_schema),
+    };
+  }
+
+  /**
+   *
+   */
+  protected function isAnyOfCompatible($checked_schema, $reference_schema): bool {
+    if (isset($reference_schema["anyOf"])) {
+      foreach ($reference_schema["anyOf"] as $schema) {
+        if ($this->isCompatible($checked_schema, $schema)) {
+          return TRUE;
+        }
+      }
+    }
+    if (isset($checked_schema["anyOf"])) {
+      foreach ($checked_schema["anyOf"] as $schema) {
+        if ($this->isCompatible($schema, $reference_schema)) {
+          return TRUE;
+        }
+      }
+    }
+    return FALSE;
+  }
+
+  /**
+   *
+   */
+  protected function isObjectCompatible(array $checked_schema, array $reference_schema): bool {
+    // FALSE if at least one of those tests is FALSE.
+    if (!isset($checked_schema["properties"]) && isset($reference_schema["properties"])) {
+      return FALSE;
+    }
+    if (!isset($checked_schema["patternProperties"]) && isset($reference_schema["patternProperties"])) {
+      return FALSE;
+    }
+    if (isset($checked_schema["properties"]) && isset($reference_schema["properties"])) {
+      // @todo
+    }
+    if (isset($checked_schema["patternProperties"]) && isset($reference_schema["patternProperties"])) {
+      // @todo
+    }
+    return TRUE;
+  }
+
+  /**
+   * Check if different arrays are compatible.
+   */
+  protected function isArrayCompatible(array $checked_schema, array $reference_schema): bool {
+    // FALSE if at least one of those tests is FALSE.
+    if (!isset($checked_schema["items"]) && isset($reference_schema["items"])) {
+      return FALSE;
+    }
+    // https://json-schema.org/understanding-json-schema/reference/array#items
+    if (isset($checked_schema["items"]) && isset($reference_schema["items"])) {
+      if (!$this->isCompatible($checked_schema["items"], $reference_schema["items"])) {
+        return FALSE;
+      }
+    }
+    // @todo https://json-schema.org/understanding-json-schema/reference/array#contains
+    // @todo https://json-schema.org/understanding-json-schema/reference/array#mincontains-maxcontains
+    // @todo https://json-schema.org/understanding-json-schema/reference/array#length
+    // @todo https://json-schema.org/understanding-json-schema/reference/array#uniqueness
+    return TRUE;
+  }
+
+  /**
+   * Check if different numbers are compatible.
+   */
+  protected function isNumberCompatible(array $checked_schema, array $reference_schema): bool {
+    if ($reference_schema["type"] === "integer") {
+      // Integers are always numbers, but numbers are not always integer.
+      return FALSE;
+    }
+    return $this->isNumericCompatible($checked_schema, $reference_schema);
+  }
+
+  /**
+   * Check if different integers are compatible.
+   */
+  protected function isIntegerCompatible(array $checked_schema, array $reference_schema): bool {
+    return $this->isNumericCompatible($checked_schema, $reference_schema);
+  }
+
+  /**
+   * Rules shared by numbers and integers.
+   */
+  protected function isNumericCompatible(array $checked_schema, array $reference_schema): bool {
+    // FALSE if at least one of those tests is FALSE.
+    if (array_key_exists("enum", $reference_schema)) {
+      if (!$this->isEnumCompatible($checked_schema, $reference_schema)) {
+        return FALSE;
+      }
+    }
+    // @todo https://json-schema.org/understanding-json-schema/reference/numeric#multiples
+    // @todo https://json-schema.org/understanding-json-schema/reference/numeric#range
+    return TRUE;
+  }
+
+  /**
+   * Check if different strings are compatible.
+   */
+  protected function isStringCompatible(array $checked_schema, array $reference_schema): bool {
+    // FALSE if at least one of those tests is FALSE.
+    if (array_key_exists("format", $reference_schema)) {
+      if (!$this->isStringFormatCompatible($checked_schema, $reference_schema)) {
+        return FALSE;
+      }
+    }
+    if (array_key_exists("enum", $reference_schema)) {
+      if (!$this->isEnumCompatible($checked_schema, $reference_schema)) {
+        return FALSE;
+      }
+    }
+    if (array_key_exists("minLength", $reference_schema)) {
+      if (!$this->isMinLengthCompatible($checked_schema, $reference_schema)) {
+        return FALSE;
+      }
+    }
+    if (array_key_exists("maxLength", $reference_schema)) {
+      if (!$this->isMaxLengthCompatible($checked_schema, $reference_schema)) {
+        return FALSE;
+      }
+    }
+    if (array_key_exists("pattern", $reference_schema)) {
+      if (!$this->isStringPatternCompatible($checked_schema, $reference_schema)) {
+        return FALSE;
+      }
+    }
+    return TRUE;
+  }
+
+  /**
+   * See: https://json-schema.org/understanding-json-schema/reference/string#regexp.
+   */
+  protected function isStringPatternCompatible(array $checked_schema, array $reference_schema): bool {
+    if (!array_key_exists("pattern", $checked_schema)) {
+      return FALSE;
+    }
+    return FALSE;
+    // Is checked schema pattern and sub pattern of reference schema?
+    $example = $this->generateExampleFromPattern($checked_schema["pattern"]);
+    $result = preg_match("/" . $reference_schema["pattern"] . "/", $example);
+    if ($result === 1) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   *
+   */
+  protected function generateExampleFromPattern(string $pattern): string {
+    // ilario-pierbattista/reverse-regex.
+    return $result;
+  }
+
+  /**
+   * See: https://json-schema.org/understanding-json-schema/reference/string#format.
+   */
+  protected function isStringFormatCompatible(array $checked_schema, array $reference_schema): bool {
+    if (!array_key_exists("format", $checked_schema)) {
+      return FALSE;
+    }
+    $checked_format = $checked_schema["format"];
+    $reference_format = $reference_schema["format"];
+    if ($checked_format == $reference_format) {
+      return TRUE;
+    }
+    // Ex: an uri is also a valid uri-reference
+    // Ex: an uri-reference is also a valid iri-reference.
+    $compatibility_map = [
+      "uri" => [
+        "uri-reference",
+        "iri-reference",
+        "iri",
+      ],
+      "iri" => [
+        "iri-reference",
+      ],
+      "uri-reference" => [
+        "iri-reference",
+      ],
+      "email" => [
+        "idn-email",
+      ],
+      // @todo add others formats.
+    ];
+    if (array_key_exists($checked_format, $compatibility_map)) {
+      return in_array($reference_format, $compatibility_map[$checked_format]);
+    }
+    return FALSE;
+  }
+
+  /**
+   *
+   */
+  protected function isMinLengthCompatible(array $checked_schema, array $reference_schema): bool {
+    if (!array_key_exists("minLength", $checked_schema)) {
+      return FALSE;
+    }
+    return ($checked_schema["minLength"] >= $reference_schema["minLength"]);
+  }
+
+  /**
+   *
+   */
+  protected function isMaxLengthCompatible(array $checked_schema, array $reference_schema): bool {
+    if (!array_key_exists("maxLength", $checked_schema)) {
+      return FALSE;
+    }
+    return ($checked_schema["maxLength"] <= $reference_schema["maxLength"]);
+  }
+
+  /**
+   *
+   */
+  protected function isEnumCompatible(array $checked_schema, array $reference_schema): bool {
+    if (!array_key_exists("enum", $checked_schema)) {
+      return FALSE;
+    }
+    if (empty($reference_schema["enum"])) {
+      return TRUE;
+    }
+    if (count($checked_schema["enum"]) === count($reference_schema["enum"])) {
+      $diff = array_diff($checked_schema["enum"], $reference_schema["enum"]);
+      return ($diff === []);
+    }
+    if (count($checked_schema["enum"]) > count($reference_schema["enum"])) {
+      return FALSE;
+    }
+    if (count($checked_schema["enum"]) < count($reference_schema["enum"])) {
+      $diff = array_diff($reference_schema["enum"], $checked_schema["enum"]);
+      return (count($diff) >= 0);
+    }
+    return FALSE;
+  }
+
+  /**
+   * @todo Make it public and unit testable independently?
+   */
+  protected function canonicalize(array $schema): array {
+    $schema = $this->keepOnlyUsefulProperties($schema);
+    if (array_key_exists("type", $schema)) {
+      $schema = $this->canonicalizeType($schema);
+    }
+    if (array_key_exists("anyOf", $schema)) {
+      foreach ($schema["anyOf"] as $index => $sub_schema) {
+        $schema["anyOf"][$index] = $this->canonicalize($sub_schema);
+      }
+    }
+    ksort($schema);
+    return $schema;
+  }
+
+  /**
+   *
+   */
+  protected function canonicalizeType(array $schema): array {
+    if (!isset($schema["type"])) {
+      return $schema;
+    }
+    if (is_array($schema["type"])) {
+      $schema = $this->resolveMultipleTypes($schema);
+      return $this->canonicalize($schema);
+    }
+    if ($schema["type"] === "object" && isset($schema["properties"])) {
+      foreach ($schema["properties"] as $property_id => $property) {
+        $schema["properties"][$property_id] = $this->canonicalize($property);
+      }
+    }
+    if ($schema["type"] === "array" && isset($schema["items"])) {
+      $schema["items"] = $this->canonicalize($schema["items"]);
+    }
+    return $schema;
+  }
+
+  /**
+   *
+   */
+  protected function resolveMultipleTypes(array $schema): array {
+    if (!is_array($schema["type"])) {
+      return $schema;
+    }
+    $schemas = [
+      "anyOf" => [],
+    ];
+    foreach ($schema["type"] as $index => $type) {
+      $sub_schema = $schema;
+      $sub_schema["type"] = $type;
+      $schemas["anyOf"][$index] = $sub_schema;
+    }
+    return $schemas;
+  }
+
+  /**
+   *
+   */
+  protected function keepOnlyUsefulProperties(array $schema): array {
+    $keys = [
+      "anyOf", "allOf", "oneOf", "not", "enum", "type", '$ref', "constant",
+    ];
+    $keys_by_type = [
+      "string" => ["minLength", "maxLength", "pattern", "format"],
+      "number" => ["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf"],
+      "integer" => ["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf"],
+      "boolean" => [],
+      "null" => [],
+      "array" => ["minItems", "maxItems", "items", "additionalItems", "uniqueItems"],
+      "object" => ["properties", "additionalProperties", "required", "minProperties", "maxProperties", "dependencies", "patternProperties"],
+    ];
+    if (isset($schema["type"]) && is_string($schema["type"])) {
+      $type = $schema["type"];
+      if (array_key_exists($type, $keys_by_type)) {
+        $keys = array_merge($keys, $keys_by_type[$type]);
+      }
+    }
+    return array_intersect_key($schema, array_flip($keys));
+  }
+
+}
diff --git a/templates/patterns-destination.html.twig b/templates/patterns-destination.html.twig
deleted file mode 100644
index 5a53e11f..00000000
--- a/templates/patterns-destination.html.twig
+++ /dev/null
@@ -1,11 +0,0 @@
-{#
-/**
- * @file
- * Render destination in case of multiple sources mapped to it.
- */
-#}
-{% if sources is not empty %}
-  {% for source in sources %}
-    {{ source }}
-  {% endfor %}
-{% endif %}
diff --git a/templates/patterns-use-wrapper.html.twig b/templates/patterns-use-wrapper.html.twig
deleted file mode 100644
index 9cb6d41d..00000000
--- a/templates/patterns-use-wrapper.html.twig
+++ /dev/null
@@ -1,9 +0,0 @@
-{#
-/**
- * @file
- * Simple wrapper used when an external Twig template is specified using the 'use:' property.
- *
- * @see \Drupal\ui_patterns\Element\Pattern::processUse().
- */
-#}
-{% include use %}
diff --git a/tests/README.md b/tests/README.md
deleted file mode 100644
index 26a16505..00000000
--- a/tests/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Please refer to the tests documentation [here](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/tests.html).
diff --git a/tests/modules/ui_patterns_field_source_test/src/Plugin/UiPatterns/Source/TestSource.php b/tests/modules/ui_patterns_field_source_test/src/Plugin/UiPatterns/Source/TestSource.php
deleted file mode 100644
index 004bb9bc..00000000
--- a/tests/modules/ui_patterns_field_source_test/src/Plugin/UiPatterns/Source/TestSource.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-namespace Drupal\ui_patterns_field_source_test\Plugin\UiPatterns\Source;
-
-use Drupal\ui_patterns\Plugin\PatternSourceBase;
-
-/**
- * Defines Fields API pattern source plugin.
- *
- * @UiPatternsSource(
- *   id = "test_source",
- *   label = @Translation("Test source"),
- *   provider = "ui_patterns_field_source_test",
- *   tags = {
- *     "test"
- *   }
- * )
- */
-class TestSource extends PatternSourceBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getSourceFields() {
-    return [
-      $this->getSourceField('field_1', 'Field 1'),
-      $this->getSourceField('field_2', 'Field 2'),
-      $this->getSourceField('field_3', 'Field 3'),
-      $this->getSourceField('field_4', 'Field 4'),
-      $this->getSourceField('field_5', 'Field 5'),
-    ];
-  }
-
-}
diff --git a/tests/modules/ui_patterns_field_source_test/ui_patterns_field_source_test.info.yml b/tests/modules/ui_patterns_field_source_test/ui_patterns_field_source_test.info.yml
deleted file mode 100644
index 7f770e42..00000000
--- a/tests/modules/ui_patterns_field_source_test/ui_patterns_field_source_test.info.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-name: 'UI Patterns field source test'
-type: module
-core: 8.x
-core_version_requirement: ^8 || ^9
diff --git a/tests/modules/ui_patterns_render_test/templates/foo-bar.ui_patterns.yml b/tests/modules/ui_patterns_render_test/templates/foo-bar.ui_patterns.yml
deleted file mode 100644
index 94addb38..00000000
--- a/tests/modules/ui_patterns_render_test/templates/foo-bar.ui_patterns.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-foo-bar:
-  label: Foo Bar
-  variants:
-    default:
-      label: Default
diff --git a/tests/modules/ui_patterns_render_test/templates/foo.ui_patterns.yml b/tests/modules/ui_patterns_render_test/templates/foo.ui_patterns.yml
deleted file mode 100644
index c699a913..00000000
--- a/tests/modules/ui_patterns_render_test/templates/foo.ui_patterns.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-foo:
-  label: Foo
-  variants:
-    default:
-      label: Default
diff --git a/tests/modules/ui_patterns_render_test/templates/pattern-foo-bar.html.twig b/tests/modules/ui_patterns_render_test/templates/pattern-foo-bar.html.twig
deleted file mode 100644
index 76c7ac2d..00000000
--- a/tests/modules/ui_patterns_render_test/templates/pattern-foo-bar.html.twig
+++ /dev/null
@@ -1 +0,0 @@
-Foo Bar
diff --git a/tests/modules/ui_patterns_render_test/ui_patterns_render_test.info.yml b/tests/modules/ui_patterns_render_test/ui_patterns_render_test.info.yml
deleted file mode 100644
index df40108b..00000000
--- a/tests/modules/ui_patterns_render_test/ui_patterns_render_test.info.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-name: 'UI Patterns Render Test'
-type: module
-core: 8.x
-core_version_requirement: ^8 || ^9
diff --git a/tests/modules/ui_patterns_test/components/README.md b/tests/modules/ui_patterns_test/components/README.md
new file mode 100644
index 00000000..c1f8b88a
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/README.md
@@ -0,0 +1,77 @@
+# alert
+
+A medium complexity component for usual testing, using component() Twig function.
+
+❌ Not a valid SDC component because of the component() Twig function.
+
+# blockquote
+
+A simple component for basic testing.
+No props. No variants.
+
+✅ Valid SDC component. The additions can be ignored.
+
+I was forced to write this anyway, because the component come from a module:
+
+```
+props:
+  type: object
+  properties: {}
+```
+
+# button
+
+A lot of variants. Props (with implicit typing) & slots.
+With explicit template path (local, without namespace, the filename from UI Patterns 1.x)
+
+❌ Not a valid SDC component, because of the explicit template path.
+
+SDC do a fatal error:
+
+> Drupal\sdc\Exception\InvalidComponentException: Unable to find the Twig template for the component "ui_patterns_test:button".
+
+# card
+
+A complex component with a template by variant. A story calling another component ("button").
+
+Props with implicit typing and default values.
+
+✅ Valid SDC component. The additions can be ignored.
+
+# close_button
+
+Here in order to test the component() Twig function in `alert`.
+
+Only props, no slots.
+
+There was initially an issue about the use of "\_" and "-" in component ID and template filename. See issue: [SDC should use dashes in file names](https://www.drupal.org/project/drupal/issues/3379527), but we renamed the template.
+
+✅ Valid SDC component. The additions can be ignored.
+
+# figure
+
+1 prop (with explicit typing) and slots. No variants.
+
+Replace `replaced_figure`
+
+❓ Not sure if the explicit typing will make this component a valid SDC component.
+
+# my-widget
+
+A more "traditional" SDC example with JSON schema examples instead of stories, and Twig blocks for slots.
+
+2 variants.
+
+✅ Valid SDC component. The additions can be ignored.
+
+# progress
+
+With explicit template path (in a subfolder, without namespace, expected filename)
+
+❌ Not a valid SDC component, because of explicit template path.
+
+# replaced_figure
+
+To test replacement mechanism
+
+✅ Valid SDC component.
diff --git a/tests/modules/ui_patterns_test/components/alert/alert.component.yml b/tests/modules/ui_patterns_test/components/alert/alert.component.yml
new file mode 100644
index 00000000..438a7713
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/alert/alert.component.yml
@@ -0,0 +1,45 @@
+$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
+name: "Alert"
+description: "Provide contextual feedback messages for typical user actions with the handful of available and flexible alert messages."
+links:
+  - "https://getbootstrap.com/docs/5.3/components/alerts/"
+variants:
+  primary:
+    title: "Primary"
+  secondary:
+    title: "Secondary"
+  success:
+    title: "Success"
+  danger:
+    title: "Danger"
+  warning:
+    title: "Warning"
+  info:
+    title: "Info"
+  light:
+    title: "Light"
+  dark:
+    title: "Dark"
+props:
+  type: object
+  properties:
+    dismissible:
+      title: "Dismissible?"
+      description: "It is possible to dismiss any alert inline."
+      type: "boolean"
+slots:
+  heading:
+    title: "Heading"
+    description: "The alert heading. Optional."
+  message:
+    title: "Message"
+    description: "The alert message."
+stories:
+  preview:
+    title: "Preview"
+    description: "The default preview from UI Patterns 1.x"
+    props:
+      dismissible: True
+    slots:
+      heading: "Well done!"
+      message: "A simple alert. Check it out!"
diff --git a/tests/modules/ui_patterns_test/components/alert/alert.twig b/tests/modules/ui_patterns_test/components/alert/alert.twig
new file mode 100644
index 00000000..45a9e07a
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/alert/alert.twig
@@ -0,0 +1,22 @@
+{% if variant and variant|lower != 'default' %}
+  {% set attributes = attributes.addClass('alert-' ~ variant|lower|replace({'_': '-'})) %}
+{% endif %}
+
+{% if dismissible %}
+  {% set attributes = attributes.addClass(['alert-dismissible', 'fade', 'show']) %}
+{% endif %}
+
+<div{{ attributes.addClass('alert').setAttribute('role', 'alert') }}>
+  {% if heading %}
+  <h4 class="alert-heading">{{ heading }}</h4>
+  {% endif %}
+  {{ message|add_class('alert-link') }}
+  {% if dismissible %}
+    {{ component('ui_patterns_test:close_button', {}, {
+      attributes: create_attribute({
+        'data-bs-dismiss': 'alert'
+      }),
+      aria_label: 'Close'|t,
+    }) }}
+  {% endif %}
+</div>
diff --git a/tests/modules/ui_patterns_test/components/blockquote/blockquote.component.yml b/tests/modules/ui_patterns_test/components/blockquote/blockquote.component.yml
new file mode 100644
index 00000000..94089024
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/blockquote/blockquote.component.yml
@@ -0,0 +1,37 @@
+$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
+name: Blockquote
+description: "For quoting blocks of content from another source within your document."
+links:
+  - "https://getbootstrap.com/docs/5.3/content/typography/#blockquotes"
+group: "Typography"
+props:
+  type: object
+  properties:
+    dummy_prop:
+      title: "Dummy Prop"
+      description: "The attributes to customize the figcaption tag."
+      type: "object"
+slots:
+  content:
+    title: "Content"
+    description: "The quote."
+  footer:
+    title: "Footer"
+    description: "For identifying the source. Wrap the name of the source work in <cite>."
+stories:
+  preview:
+    title: "Preview"
+    description: "The default preview from UI Patterns 1.x"
+    slots:
+      content:
+        type: "html_tag"
+        tag: "p"
+        value: "A well-known quote, contained in a blockquote element."
+      footer:
+        - type: "markup"
+          markup: "Someone famous in "
+        - type: "html_tag"
+          tag: "cite"
+          value: "Source Title"
+          attributes:
+            title: "Source Title"
diff --git a/tests/modules/ui_patterns_test/components/blockquote/blockquote.twig b/tests/modules/ui_patterns_test/components/blockquote/blockquote.twig
new file mode 100644
index 00000000..5721b60a
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/blockquote/blockquote.twig
@@ -0,0 +1,14 @@
+{% if footer %}
+  <figure{{ attributes }}>
+    <blockquote class="blockquote">
+      {{ content }}
+    </blockquote>
+    <figcaption class="blockquote-footer">
+      {{ footer }}
+    </figcaption>
+  </figure>
+{% else %}
+  <blockquote{{ attributes.addClass('blockquote') }}>
+    {{ content }}
+  </blockquote>
+{% endif %}
diff --git a/tests/modules/ui_patterns_test/components/button/button.component.yml b/tests/modules/ui_patterns_test/components/button/button.component.yml
new file mode 100644
index 00000000..9bc44c32
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/button/button.component.yml
@@ -0,0 +1,137 @@
+$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
+name: "Button"
+description: "For actions in forms, dialogs, and more with support for multiple sizes, states, and more."
+links:
+  - "https://getbootstrap.com/docs/5.3/components/buttons/"
+group: "Button"
+template: pattern-button.html.twig
+variants:
+  default:
+    title: "Default"
+    description: "No 'btn' class added."
+  primary__sm:
+    title: "Primary small"
+  secondary__sm:
+    title: "Secondary small"
+  success__sm:
+    title: "Success small"
+  danger__sm:
+    title: "Danger small"
+  warning__sm:
+    title: "Warning small"
+  info__sm:
+    title: "Info small"
+  light__sm:
+    title: "Light small"
+  dark__sm:
+    title: "Dark small"
+  link__sm:
+    title: "Link small"
+  primary:
+    title: "Primary"
+  secondary:
+    title: "Secondary"
+  success:
+    title: "Success"
+  danger:
+    title: "Danger"
+  warning:
+    title: "Warning"
+  info:
+    title: "Info"
+  light:
+    title: "Light"
+  dark:
+    title: "Dark"
+  link:
+    title: "Link"
+  primary__lg:
+    title: "Primary large"
+  secondary__lg:
+    title: "Secondary large"
+  success__lg:
+    title: "Success large"
+  danger__lg:
+    title: "Danger large"
+  warning__lg:
+    title: "Warning large"
+  info__lg:
+    title: "Info large"
+  light__lg:
+    title: "Light large"
+  dark__lg:
+    title: "Dark large"
+  link__lg:
+    title: "Link large"
+  outline_primary__sm:
+    title: "Outline Primary small"
+  outline_secondary__sm:
+    title: "Outline Secondary small"
+  outline_success__sm:
+    title: "Outline Success small"
+  outline_danger__sm:
+    title: "Outline Danger small"
+  outline_warning__sm:
+    title: "Outline Warning small"
+  outline_info__sm:
+    title: "Outline Info small"
+  outline_light__sm:
+    title: "Outline Light small"
+  outline_dark__sm:
+    title: "Outline Dark small"
+  outline_primary:
+    title: "Outline Primary"
+  outline_secondary:
+    title: "Outline Secondary"
+  outline_success:
+    title: "Outline Success"
+  outline_danger:
+    title: "Outline Danger"
+  outline_warning:
+    title: "Outline Warning"
+  outline_info:
+    title: "Outline Info"
+  outline_light:
+    title: "Outline Light"
+  outline_dark:
+    title: "Outline Dark"
+  outline_primary__lg:
+    title: "Outline Primary large"
+  outline_secondary__lg:
+    title: "Outline Secondary large"
+  outline_success__lg:
+    title: "Outline Success large"
+  outline_danger__lg:
+    title: "Outline Danger large"
+  outline_warning__lg:
+    title: "Outline Warning large"
+  outline_info__lg:
+    title: "Outline Info large"
+  outline_light__lg:
+    title: "Outline Light large"
+  outline_dark__lg:
+    title: "Outline Dark large"
+props:
+  type: object
+  properties:
+    disabled:
+      title: "Disabled?"
+      description: "Is the button disabled?"
+      type: boolean
+    url:
+      title: "URL"
+      type: string
+      format: uri-reference
+slots:
+  label:
+    title: "Label"
+    description: "The button label."
+stories:
+  preview:
+    title: "Preview"
+    description: "The default preview from UI Patterns 1.x"
+    props:
+      disabled: false
+      url: "https://example.com"
+    slots:
+      label: "Submit"
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/js/library_two_2.js b/tests/modules/ui_patterns_test/components/button/button.twig
similarity index 100%
rename from modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/js/library_two_2.js
rename to tests/modules/ui_patterns_test/components/button/button.twig
diff --git a/tests/modules/ui_patterns_test/components/button/pattern-button.html.twig b/tests/modules/ui_patterns_test/components/button/pattern-button.html.twig
new file mode 100644
index 00000000..5fc3ddb9
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/button/pattern-button.html.twig
@@ -0,0 +1,31 @@
+{% if variant and variant|lower != 'default' and variant|lower != 'dropdown_item' %}
+  {% set variants = variant|split('__')|map(v => v|lower|replace({(v): 'btn-' ~ v})|replace({'_': '-'})) %}
+  {% set attributes = attributes.addClass(variants) %}
+  {% set attributes = attributes.addClass('btn') %}
+{% endif %}
+
+{% set attributes = (variant|lower == 'dropdown_item') ? attributes.addClass('dropdown-item') : attributes %}
+
+{% if label_visually_hidden %}
+  {% set label %}
+    <span class="visually-hidden">
+      {{ label }}
+    </span>
+  {% endset %}
+{% endif %}
+
+{% if url or attributes.href %}
+  {% set url = url|default(attributes.href) %}
+  {% set attributes = attributes.setAttribute('href', url) %}
+  {% if disabled %}
+    {% set attributes = attributes.setAttribute('href', false).setAttribute('tabindex', '-1').setAttribute('aria-disabled', 'true').addClass('disabled') %}
+  {% endif %}
+
+  <a{{ attributes.setAttribute('role', 'button') }}>{{ label }}</a>
+{% else %}
+  {% if disabled %}
+    {% set attributes = attributes.setAttribute('disabled', '') %}
+  {% endif %}
+
+  <button{{ attributes.setAttribute('type', 'button') }}>{{ label }}</button>
+{% endif %}
diff --git a/tests/modules/ui_patterns_test/components/card/card--horizontal.twig b/tests/modules/ui_patterns_test/components/card/card--horizontal.twig
new file mode 100644
index 00000000..9e579b35
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/card/card--horizontal.twig
@@ -0,0 +1,20 @@
+<div{{ attributes.addClass('card') }}>
+  <div class="row g-0">
+    <div class="{{ image_col_classes|default('col-md-4') }}">
+      {{ image|add_class('img-fluid rounded-start') }}
+    </div>
+    <div class="{{ content_col_classes|default('col-md-8') }}">
+      {% if header %}
+      <div class="card-header">
+        {{ header }}
+      </div>
+      {% endif %}
+      {{ content }}
+      {% if footer %}
+      <div class="card-footer">
+        {{ footer }}
+      </div>
+      {% endif %}
+    </div>
+  </div>
+</div>
diff --git a/tests/modules/ui_patterns_test/components/card/card.component.yml b/tests/modules/ui_patterns_test/components/card/card.component.yml
new file mode 100644
index 00000000..4783fdfe
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/card/card.component.yml
@@ -0,0 +1,75 @@
+$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
+name: "Card"
+description: "A card is a flexible and extensible content container. It includes options for headers and footers, a wide variety of content, contextual background colors, and powerful display options."
+links:
+  - "https://getbootstrap.com/docs/5.3/components/card/"
+group: "Card"
+variants:
+  default:
+    title: "Default"
+  horizontal:
+    title: "Horizontal"
+props:
+  type: object
+  properties:
+    image_position:
+      title: "Image position"
+      description: "Only for default variant."
+      type: "string"
+      enum:
+        - top
+        - bottom
+    image_col_classes:
+      title: "Image column classes"
+      description: "Only for horizontal variant."
+      type: "string"
+      default: "col-md-4"
+    content_col_classes:
+      title: "Content column classes"
+      description: "Only for horizontal variant."
+      type: "string"
+      default: "col-md-8"
+slots:
+  image:
+    title: "Image"
+  header:
+    title: "Header"
+  content:
+    title: "Content"
+  footer:
+    title: "Footer"
+stories:
+  preview:
+    title: "Preview"
+    description: "The default preview from UI Patterns 1.x"
+    props:
+      image_position: "top"
+      image_col_classes: "col-md-4"
+      content_col_classes: "col-md-8"
+    slots:
+      image:
+        theme: "image"
+        uri: "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZm9udC1zaXplOiAxLjEyNXJlbTsgdGV4dC1hbmNob3I6IG1pZGRsZTsgdXNlci1zZWxlY3Q6IG5vbmU7IiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIyNTQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgc2xpY2UiIGZvY3VzYWJsZT0iZmFsc2UiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iUGxhY2Vob2xkZXI6IEltYWdlIGNhcCI+PHRpdGxlPlBsYWNlaG9sZGVyPC90aXRsZT48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjODY4ZTk2Ij48L3JlY3Q+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZpbGw9IiNkZWUyZTYiIGR5PSIuM2VtIj5JbWFnZSBjYXA8L3RleHQ+PC9zdmc+Cg=="
+        alt: "&copy; 2017 John Smith photography"
+      header: "Featured"
+      content:
+        - type: "component"
+          component: "ui_patterns_test:button"
+          props:
+            variant: "primary"
+          slots:
+            label: "Go somewhere"
+        - type: "html_tag"
+          tag: "a"
+          value: "Card link"
+          attributes:
+            href: "#"
+        - type: "html_tag"
+          tag: "a"
+          value: "Another link"
+          attributes:
+            href: "#"
+      footer:
+        type: "html_tag"
+        tag: "span"
+        value: "2 days ago"
diff --git a/tests/modules/ui_patterns_test/components/card/card.twig b/tests/modules/ui_patterns_test/components/card/card.twig
new file mode 100644
index 00000000..e7e33a55
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/card/card.twig
@@ -0,0 +1,19 @@
+<div{{ attributes.addClass('card') }}>
+  {% if image and image_position != 'bottom' %}
+    {{ image|add_class('card-img-top') }}
+  {% endif %}
+  {% if header %}
+  <div class="card-header">
+    {{ header }}
+  </div>
+  {% endif %}
+  {{ content }}
+  {% if footer %}
+  <div class="card-footer">
+    {{ footer }}
+  </div>
+  {% endif %}
+  {% if image and image_position == 'bottom' %}
+    {{ image|add_class('card-img-bottom') }}
+  {% endif %}
+</div>
diff --git a/tests/modules/ui_patterns_test/components/close_button/close_button.component.yml b/tests/modules/ui_patterns_test/components/close_button/close_button.component.yml
new file mode 100644
index 00000000..a2f5abe6
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/close_button/close_button.component.yml
@@ -0,0 +1,29 @@
+$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
+name: "Close button"
+description: "A generic close button for dismissing content like modals and alerts."
+links:
+  - "https://getbootstrap.com/docs/5.3/components/close-button"
+group: "Button"
+variants:
+  default:
+    title: "Default"
+  white:
+    title: "White (deprecated)"
+props:
+  type: object
+  properties:
+    disabled:
+      title: "Disabled?"
+      description: "Is the button disabled?"
+      type: "boolean"
+    aria_label:
+      title: "Aria label"
+      description: "Name of the close button for assistive technology."
+      type: "string"
+stories:
+  preview:
+    title: "Preview"
+    description: "The default preview from UI Patterns 1.x"
+    props:
+      disabled: false
+      aria_label: "Close"
diff --git a/tests/modules/ui_patterns_test/components/close_button/close_button.twig b/tests/modules/ui_patterns_test/components/close_button/close_button.twig
new file mode 100644
index 00000000..d1350276
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/close_button/close_button.twig
@@ -0,0 +1,11 @@
+{% if variant and variant|lower != 'default' %}
+  {% set attributes = attributes.addClass('btn-close-' ~ variant) %}
+{% endif %}
+{% set attributes = attributes.addClass('btn-close') %}
+
+{% set attributes = attributes.setAttribute('aria-label', aria_label|default('Close'|t)) %}
+{% if disabled %}
+  {% set attributes = attributes.setAttribute('disabled', disabled) %}
+{% endif %}
+
+<button{{ attributes.setAttribute('type', 'button') }}></button>
diff --git a/tests/modules/ui_patterns_test/components/figure/figure.component.yml b/tests/modules/ui_patterns_test/components/figure/figure.component.yml
new file mode 100644
index 00000000..097957e3
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/figure/figure.component.yml
@@ -0,0 +1,33 @@
+$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
+name: "Figure"
+description: "Used to display a piece of self-contained content (illustrations, diagrams, photos, code, etc) along with an optional caption. This content can be removed from the document without affecting the meaning of the document."
+links:
+  - "https://getbootstrap.com/docs/5.3/content/figures/"
+replaces: "ui_patterns_tests:replaced_figure"
+props:
+  type: object
+  properties:
+    figcaption_attributes:
+      title: "Figcaption attributes"
+      description: "The attributes to customize the figcaption tag."
+      "$ref": "ui-patterns://attributes"
+slots:
+  image:
+    title: "Image"
+    description: "The content of the figure."
+  caption:
+    title: "Caption"
+    description: "The caption that appears under the content."
+stories:
+  preview:
+    title: "Preview"
+    description: "The default preview from UI Patterns 1.x"
+    props:
+      figcaption_attributes:
+        class: ["text-end"]
+    slots:
+      image:
+        theme: "image"
+        uri: "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZm9udC1zaXplOiAxLjEyNXJlbTsgdGV4dC1hbmNob3I6IG1pZGRsZTsgdXNlci1zZWxlY3Q6IG5vbmU7IiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIyNTQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgc2xpY2UiIGZvY3VzYWJsZT0iZmFsc2UiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iUGxhY2Vob2xkZXI6IEltYWdlIGNhcCI+PHRpdGxlPlBsYWNlaG9sZGVyPC90aXRsZT48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjODY4ZTk2Ij48L3JlY3Q+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZpbGw9IiNkZWUyZTYiIGR5PSIuM2VtIj5JbWFnZSBjYXA8L3RleHQ+PC9zdmc+Cg=="
+        alt: "&copy; 2017 John Smith photography"
+      caption: "A caption for the above image."
diff --git a/tests/modules/ui_patterns_test/components/figure/figure.twig b/tests/modules/ui_patterns_test/components/figure/figure.twig
new file mode 100644
index 00000000..21dd87df
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/figure/figure.twig
@@ -0,0 +1,6 @@
+<figure{{ attributes.addClass('figure') }}>
+  {{ image|add_class('figure-img') }}
+  <figcaption{{ figcaption_attributes.addClass('figure-caption') }}>
+    {{ caption }}
+  </figcaption>
+</figure>
diff --git a/tests/modules/ui_patterns_test/components/my-widget/my-widget.component.yml b/tests/modules/ui_patterns_test/components/my-widget/my-widget.component.yml
new file mode 100644
index 00000000..4fe76844
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/my-widget/my-widget.component.yml
@@ -0,0 +1,47 @@
+$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
+name: Widget
+description: Widget Test with title and a CTA link
+libraryOverrides:
+  dependencies:
+    - core/drupal
+variants:
+  default:
+    label: Default
+  other:
+    label: other
+props:
+  type: object
+  properties:
+    heading:
+      title: Heading
+      description: The title for the banner text.
+      type: string
+      default: "Asdf"
+      examples:
+        - Join us at The Conference
+    ctaText:
+      title: CTA Text
+      type: string
+      examples:
+        - Click me!
+    ctaHref:
+      title: CTA Href
+      type: string
+      examples:
+        - "https://www.example.org"
+    ctaTarget:
+      title: CTA Target
+      type: string
+      enum:
+        - ""
+        - _blank
+    image:
+      title: Media Image
+      description: Background image for the banner.
+      type: string
+slots:
+  widget_body:
+    title: Body
+    description: The content of the banner.
+    examples:
+      - <p>Foo is <strong>NOT</strong> bar.</p>
diff --git a/tests/modules/ui_patterns_test/components/my-widget/my-widget.css b/tests/modules/ui_patterns_test/components/my-widget/my-widget.css
new file mode 100644
index 00000000..ea702632
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/my-widget/my-widget.css
@@ -0,0 +1,24 @@
+.component--my-widget {
+  position: relative;
+  width: 80%;
+  max-width: 1200px;
+  padding: 2em;
+  color: white;
+  background: black;
+  background-size: cover;
+}
+.component--my-widget--header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+.component--my-widget--header h3 {
+  margin: 0;
+  font-size: 2em;
+}
+.component--my-widget--body > * {
+  margin-bottom: 0;
+}
+.component--my-widget--body > :first-child {
+  margin-top: 1em;
+}
diff --git a/tests/modules/ui_patterns_test/components/my-widget/my-widget.twig b/tests/modules/ui_patterns_test/components/my-widget/my-widget.twig
new file mode 100644
index 00000000..4d44e888
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/my-widget/my-widget.twig
@@ -0,0 +1,16 @@
+{# Prepare presentational attributes #}
+{% if image is not empty %}
+  {% set attributes = attributes.setAttribute('style', 'background-image: linear-gradient(to right, black, black, rgba(0, 0, 0, 70%), transparent), url("' ~ image ~ '");') %}
+{% endif %}
+
+{# Markup for the component #}
+<div {{ attributes }}>
+  <div class="component--my-widget--header">
+    <h3>Heading: {{ heading }}</h3>
+  </div>
+  <div class="component--my-widget--body">
+    {% block widget_body %}
+      {{ widget_body }}
+    {% endblock %}
+  </div>
+</div>
diff --git a/tests/modules/ui_patterns_test/components/progress/path/to/template/progress.twig b/tests/modules/ui_patterns_test/components/progress/path/to/template/progress.twig
new file mode 100644
index 00000000..b7bc09f6
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/progress/path/to/template/progress.twig
@@ -0,0 +1,31 @@
+{% if variant and variant|lower != 'default' %}
+  {% set variants = variant|split('__')|map(v => v|lower|replace({(v): 'progress-bar-' ~ v})|replace({'_': '-'})) %}
+  {% set attributes = attributes.addClass(variants) %}
+{% endif %}
+
+{% set wrapper_attributes = create_attribute() %}
+{# Handle wrapper ID. #}
+{% if attributes.hasAttribute('id') %}
+  {% set wrapper_attributes = wrapper_attributes.setAttribute('id', attributes.offsetGet('id')) %}
+  {% set attributes = attributes.removeAttribute('id') %}
+{% endif %}
+
+{% set wrapper_attributes = bar_height ? wrapper_attributes.setAttribute('style', 'height:' ~ bar_height ~ 'px') : wrapper_attributes %}
+{% set percent = percent|default(0) %}
+{% set min = min|default(0) %}
+{% set max = max|default(100) %}
+{% set width = (percent * 100) // max %}
+
+<div{{ wrapper_attributes.addClass('progress')}}>
+  <div{{ attributes
+      .addClass('progress-bar')
+      .setAttribute('role', 'progressbar')
+      .setAttribute('aria-label', aria_label|default('Progress bar'|t))
+      .setAttribute('aria-valuenow', percent)
+      .setAttribute('aria-valuemin', min)
+      .setAttribute('aria-valuemax', max)
+      .setAttribute('style', 'width: ' ~ width ~ '%')
+    }}>
+    {{ label }}
+  </div>
+</div>
diff --git a/tests/modules/ui_patterns_test/components/progress/progress.component.yml b/tests/modules/ui_patterns_test/components/progress/progress.component.yml
new file mode 100644
index 00000000..b565e160
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/progress/progress.component.yml
@@ -0,0 +1,50 @@
+$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
+name: "Progress"
+description: "The progress element displays an indicator showing the completion progress of a task, typically in the form of a bar. Progress components are built with two HTML elements, some CSS to set the width, and a few attributes. Bootstrap does not use the HTML5 <progress> element, ensuring you can stack progress bars, animate them, and place text labels over them."
+links:
+  - "https://getbootstrap.com/docs/5.3/components/progress/"
+template: path/to/template/progress.twig
+variants:
+  default:
+    title: "Default"
+  striped:
+    title: "Striped"
+  striped__animated:
+    title: "Animated stripes"
+props:
+  type: object
+  properties:
+    aria_label:
+      title: "Aria label"
+      description: "Name of the progress bar for assistive technology."
+      type: "string"
+    percent:
+      title: "Total progress (%)"
+      description: "Width of the progress element representing total progress (25%, 50%, etc.)."
+      type: "integer"
+    min:
+      title: "Minimum value"
+      description: "Minimum value of the progress element (default is 0). Used for an aria attribute."
+      type: "integer"
+    max:
+      title: "Maximum value"
+      description: "Maximum value of the progress element (default is 100). Used for an aria attribute."
+      type: "integer"
+    bar_height:
+      title: "Height"
+      description: "Height of progress element in pixels (px). Leave empty for default height."
+      type: "integer"
+slots:
+  label:
+    title: "Label"
+    description: "Text shown inside the progress bar."
+stories:
+  preview:
+    title: "Preview"
+    description: "The default preview from UI Patterns 1.x"
+    props:
+      percent: 50
+      min: 0
+      max: 100
+    slots:
+      label: "Label"
diff --git a/tests/modules/ui_patterns_render_test/templates/pattern-foo.html.twig b/tests/modules/ui_patterns_test/components/progress/progress.twig
similarity index 100%
rename from tests/modules/ui_patterns_render_test/templates/pattern-foo.html.twig
rename to tests/modules/ui_patterns_test/components/progress/progress.twig
diff --git a/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.component.yml b/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.component.yml
new file mode 100644
index 00000000..33b1b241
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.component.yml
@@ -0,0 +1,102 @@
+$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
+name: "Prop types tests"
+description: "A card is a flexible and extensible content container. It includes options for headers and footers, a wide variety of content, contextual background colors, and powerful display options."
+props:
+  type: object
+  properties:
+    string:
+      title: "String"
+      type: "string"
+    string_enum:
+      title: "String with enum"
+      type: "string"
+      enum:
+        - top
+        - bottom
+    string_length:
+      title: "String with length"
+      type: "string"
+      maxLength: 10
+    boolean:
+      title: "Boolean"
+      type: "boolean"
+    integer:
+      title: "Integer"
+      type: "integer"
+    number_1:
+      title: "Number"
+      type: "number"
+    number_2:
+      title: "Number with min max"
+      type: "number"
+      minimum: 2
+      maximum: 10
+    attributes:
+      title: "Attributes"
+      type: object
+      patternProperties:
+        ".+":
+          anyOf:
+            - type:
+                - string
+                - number
+            - type: array
+              items:
+                anyOf:
+                  - type: number
+                  - type: string
+    url_1:
+      title: "Url (explicit typing)"
+      $ref: "ui-patterns://url"
+    url_2:
+      title: "Url (implicit typing, exact type)"
+      type: "string"
+      format: "iri-reference"
+    url_3:
+      title: "Url (implicit typing, other type)"
+      description: Test format comaptibility
+      type: "string"
+      format: "uri"
+    color:
+      title: "Color RGB (6 or 3 hex)"
+      type: "string"
+      pattern: "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
+    color_6:
+      title: "Color RGB (6 hex)"
+      description: Test Regexp comaptibility API
+      type: "string"
+      pattern: "^#[A-Fa-f0-9]{6}$"
+    color_3:
+      title: "Color RGB (3 hex)"
+      description: Test Regexp comaptibility API
+      type: "string"
+      pattern: "^#[A-Fa-f0-9]{3}$"
+    object:
+      title: "Undefined (Empty object)"
+      type: "object"
+    array:
+      title: "Empty array"
+      type: "array"
+    links:
+      title: "Links"
+      type: array
+      items:
+        type: object
+        properties:
+          title: { type: string }
+          attributes: { $ref: "ui-patterns://attributes" }
+          below: { $ref: "ui-patterns://links" }
+    links_2:
+      title: "Links with extra property"
+      type: array
+      items:
+        type: object
+        properties:
+          title: { type: string }
+          attributes: { $ref: "ui-patterns://attributes" }
+          below: { $ref: "ui-patterns://links" }
+          extra: { type: string }
+    machine_name:
+      title: "Machine name"
+      type: string
+      pattern: '^[A-Za-z]+\w*$'
diff --git a/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.twig b/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.twig
new file mode 100644
index 00000000..8511efcc
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.twig
@@ -0,0 +1 @@
+<p>Empty template</p>
diff --git a/tests/modules/ui_patterns_test/components/replaced_figure/replaced_figure.component.yml b/tests/modules/ui_patterns_test/components/replaced_figure/replaced_figure.component.yml
new file mode 100644
index 00000000..d4ea2091
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/replaced_figure/replaced_figure.component.yml
@@ -0,0 +1,13 @@
+$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
+name: "Figure without caption (replaced by the 'real' Figure)"
+props:
+  type: object
+  properties:
+    figcaption_attributes:
+      title: "Figcaption attributes"
+      description: "The attributes to customize the figcaption tag."
+      "$ref": "ui-patterns://attributes"
+slots:
+  image:
+    title: "Image"
+    description: "The content of the figure."
diff --git a/tests/modules/ui_patterns_test/components/replaced_figure/replaced_figure.twig b/tests/modules/ui_patterns_test/components/replaced_figure/replaced_figure.twig
new file mode 100644
index 00000000..5c9670dc
--- /dev/null
+++ b/tests/modules/ui_patterns_test/components/replaced_figure/replaced_figure.twig
@@ -0,0 +1,3 @@
+<figure{{ attributes.addClass('figure') }}>
+  {{ image|add_class('figure-img') }}
+</figure>
diff --git a/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php b/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php
new file mode 100644
index 00000000..61e42bf7
--- /dev/null
+++ b/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php
@@ -0,0 +1,29 @@
+<?php declare(strict_types = 1);
+
+namespace Drupal\ui_patterns_test\Plugin\UiPatterns\Source;
+
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Plugin implementation of the source_provider.
+ *
+ * @Source(
+ *   id = "foo",
+ *   label = @Translation("Foo"),
+ *   description = @Translation("Foo description."),
+ *   prop_types = {
+ *     "string"
+ *   }
+ * )
+ */
+final class Foo extends SourcePluginBase {
+
+  public function getData(): mixed {
+    return 'foo';
+  }
+
+  public function defaultConfiguration() {
+    return [];
+  }
+
+}
diff --git a/tests/modules/ui_patterns_test/ui_patterns_test.info.yml b/tests/modules/ui_patterns_test/ui_patterns_test.info.yml
new file mode 100644
index 00000000..f58ce5ef
--- /dev/null
+++ b/tests/modules/ui_patterns_test/ui_patterns_test.info.yml
@@ -0,0 +1,4 @@
+name: 'UI Patterns Test'
+type: module
+description: 'Provides test plugin.'
+package: 'Testing'
diff --git a/tests/src/Functional/UiPatternsPreviewRenderTest.php b/tests/src/Functional/UiPatternsPreviewRenderTest.php
deleted file mode 100644
index 038fc2f0..00000000
--- a/tests/src/Functional/UiPatternsPreviewRenderTest.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Functional;
-
-use Drupal\Tests\BrowserTestBase;
-use Drupal\Tests\ui_patterns\Traits\TwigDebugTrait;
-
-/**
- * Test pattern preview rendering.
- *
- * @group ui_patterns
- */
-class UiPatternsPreviewRenderTest extends BrowserTestBase {
-
-  /**
-   * Default theme. See https://www.drupal.org/node/3083055.
-   *
-   * @var string
-   */
-  protected $defaultTheme = 'stark';
-
-  /**
-   * Disable schema validation when running tests.
-   *
-   * @var bool
-   *
-   * @todo Fix this by providing actual schema validation.
-   */
-  protected $strictConfigSchema = FALSE;
-
-  use TwigDebugTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = [
-    'ui_patterns',
-    'ui_patterns_library',
-    'ui_patterns_render_test',
-  ];
-
-  /**
-   * Tests pattern preview suggestions.
-   */
-  public function testPatternPreviewSuggestions() {
-    $assert_session = $this->assertSession();
-
-    $this->enableTwigDebugMode();
-
-    $user = $this->drupalCreateUser([], NULL, TRUE);
-    $this->drupalLogin($user);
-
-    $this->drupalGet('/patterns');
-
-    // Assert correct variant suggestions.
-    $suggestions = [
-      'pattern-foo--variant-default--preview.html.twig',
-      'pattern-foo--variant-default.html.twig',
-      'pattern-foo--preview.html.twig',
-      'pattern-foo.html.twig',
-      'pattern-foo-bar--variant-default--preview.html.twig',
-      'pattern-foo-bar--variant-default.html.twig',
-      'pattern-foo-bar--preview.html.twig',
-      'pattern-foo-bar.html.twig',
-    ];
-    foreach ($suggestions as $suggestion) {
-      $assert_session->responseContains($suggestion);
-    }
-  }
-
-}
diff --git a/tests/src/Kernel/AbstractUiPatternsTest.php b/tests/src/Kernel/AbstractUiPatternsTest.php
deleted file mode 100644
index 24ecfe72..00000000
--- a/tests/src/Kernel/AbstractUiPatternsTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Kernel;
-
-use Drupal\Component\Serialization\Yaml;
-use Drupal\KernelTests\KernelTestBase;
-
-/**
- * Abstract base test class.
- *
- * @group ui_patterns
- *
- * @package Drupal\Tests\ui_patterns\Kernel
- */
-abstract class AbstractUiPatternsTest extends KernelTestBase {
-
-  /**
-   * Get fixtures base path.
-   *
-   * @return string
-   *   Fixtures base path.
-   */
-  protected function getFixturePath() {
-    return realpath(__DIR__ . '/../fixtures');
-  }
-
-  /**
-   * Get fixture content.
-   *
-   * @param string $filepath
-   *   File path.
-   *
-   * @return array
-   *   A set of test data.
-   */
-  protected function getFixtureContent($filepath) {
-    return Yaml::decode(file_get_contents($this->getFixturePath() . '/' . $filepath));
-  }
-
-}
diff --git a/tests/src/Kernel/Plugin/Deriver/YamlDeriverTest.php b/tests/src/Kernel/Plugin/Deriver/YamlDeriverTest.php
deleted file mode 100644
index a7bb4053..00000000
--- a/tests/src/Kernel/Plugin/Deriver/YamlDeriverTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Kernel\Plugin\Deriver;
-
-use Drupal\Tests\ui_patterns\Kernel\AbstractUiPatternsTest;
-use Drupal\ui_patterns\UiPatterns;
-
-/**
- * @coversDefaultClass \Drupal\ui_patterns\Plugin\Deriver\AbstractYamlPatternsDeriver
- *
- * @group ui_patterns
- */
-class YamlDeriverTest extends AbstractUiPatternsTest {
-
-  /**
-   * {@inheritdoc}
-   */
-  public static $modules = [
-    'system',
-    'ui_patterns',
-    'ui_patterns_library',
-  ];
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function setUp(): void {
-    parent::setUp();
-
-    // Theme with existing patterns has to be enabled.
-    $default_theme = 'ui_patterns_library_theme_test';
-    $this->container->get('theme_installer')->install([$default_theme]);
-    $this->container->get('config.factory')->getEditable('system.theme')->set('default', $default_theme)->save();
-  }
-
-  /**
-   * Test get derivative definitions.
-   *
-   * @covers ::getDerivativeDefinitions
-   */
-  public function testGetDerivativeDefinitions() {
-    UiPatterns::getManager()->clearCachedDefinitions();
-    foreach (UiPatterns::getManager()->getDefinitions() as $definition) {
-      $this->assertNotEmpty($definition->id(), 'Pattern definition id is empty');
-      $this->assertNotEmpty($definition->getProvider(), 'Pattern definition provider is empty');
-      $this->assertNotEmpty($definition->getBasePath(), 'Pattern definition base path is empty');
-    }
-  }
-
-}
diff --git a/tests/src/Kernel/Plugin/PatternBaseTest.php b/tests/src/Kernel/Plugin/PatternBaseTest.php
deleted file mode 100644
index 5298b389..00000000
--- a/tests/src/Kernel/Plugin/PatternBaseTest.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Kernel\Plugin;
-
-use Drupal\Tests\ui_patterns\Kernel\AbstractUiPatternsTest;
-use Drupal\ui_patterns\Plugin\PatternBase;
-
-/**
- * @coversDefaultClass \Drupal\ui_patterns\Plugin\PatternBase
- *
- * @group ui_patterns
- */
-class PatternBaseTest extends AbstractUiPatternsTest {
-
-  /**
-   * Test hookLibraryInfoBuild.
-   *
-   * @dataProvider hookLibraryInfoBuildDataProvider
-   *
-   * @covers ::getLibraryDefinitions
-   */
-  public function testHookLibraryInfoBuild($actual, $expected) {
-    $pattern = $this->getUiPatternBaseMock($actual);
-    /** @var \Drupal\ui_patterns\Plugin\PatternBase $pattern */
-    $libraries = $pattern->getLibraryDefinitions();
-    $this->assertEquals($expected, $libraries);
-  }
-
-  /**
-   * Data provider for rendering tests.
-   *
-   * The actual data is read from fixtures stored in a YAML configuration.
-   *
-   * @return array
-   *   A set of dump data for testing.
-   */
-  public function hookLibraryInfoBuildDataProvider() {
-    return $this->getFixtureContent('libraries.yml');
-  }
-
-  /**
-   * Get PatternBase mock.
-   *
-   * @param array $plugin_definition
-   *   Plugin definition.
-   * @param array $methods
-   *   List of methods to mock.
-   *
-   * @return \PHPUnit\Framework\MockObject\MockObject
-   *   Mock object.
-   */
-  protected function getUiPatternBaseMock(array $plugin_definition = [], array $methods = []) {
-    return $this->getMockForAbstractClass(PatternBase::class, [
-      [],
-      'plugin_id',
-      $plugin_definition,
-      \Drupal::service('app.root'),
-      \Drupal::service('module_handler'),
-    ], '', TRUE, TRUE, TRUE, $methods);
-  }
-
-}
diff --git a/tests/src/Kernel/PropTypePluginManagerTest.php b/tests/src/Kernel/PropTypePluginManagerTest.php
new file mode 100644
index 00000000..2ce78e06
--- /dev/null
+++ b/tests/src/Kernel/PropTypePluginManagerTest.php
@@ -0,0 +1,37 @@
+<?php declare(strict_types = 1);
+
+namespace Drupal\Tests\ui_patterns\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\ui_patterns\Plugin\UiPatterns\PropType\StringPropType;
+
+/**
+ * Test description.
+ *
+ * @group ui_patterns
+ */
+final class PropTypePluginManagerTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['sdc', 'ui_patterns', 'ui_patterns_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+  }
+
+  /**
+   * Test callback.
+   */
+  public function testGetPropTypePlugin(): void {
+    /** @var \Drupal\ui_patterns\PropTypePluginManager $prop_type_plugin_manager */
+    $prop_type_plugin_manager = \Drupal::service('plugin.manager.ui_patterns_prop_type');
+    $plugin_type = $prop_type_plugin_manager->getPropTypePlugin(['type' => 'string']);
+    self::assertInstanceOf(StringPropType::class, $plugin_type);
+  }
+
+}
diff --git a/tests/src/Kernel/SourcePluginManagerTest.php b/tests/src/Kernel/SourcePluginManagerTest.php
new file mode 100644
index 00000000..68f94c75
--- /dev/null
+++ b/tests/src/Kernel/SourcePluginManagerTest.php
@@ -0,0 +1,44 @@
+<?php declare(strict_types = 1);
+
+namespace Drupal\Tests\ui_patterns\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\ui_patterns\SourcePluginBase;
+
+/**
+ * Test description.
+ *
+ * @group ui_patterns
+ */
+final class SourcePluginManagerTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['sdc', 'ui_patterns', 'ui_patterns_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+  }
+
+  /**
+   * Test callback.
+   */
+  public function testGetSourcePlugins(): void {
+    /** @var \Drupal\ui_patterns\SourcePluginManager $source_provider_plugin_manager */
+    $source_plugin_manager = \Drupal::service('plugin.manager.ui_patterns_source');
+    $sources = $source_plugin_manager->getSourcePlugins('string', 'test', ['title' => 'test title']);
+    /** @var SourcePluginBase $source */
+    foreach ($sources as $source) {
+      self::assertNotNull($source);
+      self::assertInstanceOf(SourcePluginBase::class, $source);
+      self::assertNotNull($source->getPropId());
+      self::assertNotNull($source->getPropDefinition());
+    }
+    self::assertGreaterThan(1, $sources);
+  }
+
+}
diff --git a/tests/src/Kernel/TypedData/PatternDataDefinitionTest.php b/tests/src/Kernel/TypedData/PatternDataDefinitionTest.php
deleted file mode 100644
index ebc90cc1..00000000
--- a/tests/src/Kernel/TypedData/PatternDataDefinitionTest.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Kernel\TypedData;
-
-use Drupal\Tests\ui_patterns\Kernel\AbstractUiPatternsTest;
-use Drupal\ui_patterns\TypedData\PatternDataDefinition;
-
-/**
- * @coversDefaultClass \Drupal\ui_patterns\TypedData\PatternDataDefinition
- *
- * @group ui_patterns
- */
-class PatternDataDefinitionTest extends AbstractUiPatternsTest {
-
-  /**
-   * Test plugin validation.
-   *
-   * @dataProvider validationProvider
-   */
-  public function testValidation($data, $expected) {
-    $definition = PatternDataDefinition::create();
-    $violations = \Drupal::typedDataManager()->create($definition, $data)->validate();
-
-    $actual = [];
-    foreach ($violations as $violation) {
-      $actual[] = $violation->getPropertyPath() . ': ' . $violation->getMessage();
-    }
-    $this->assertEquals($expected, $actual);
-  }
-
-  /**
-   * Return validation data.
-   *
-   * @return array
-   *   Pattern validation data.
-   */
-  public function validationProvider() {
-    return $this->getFixtureContent('validation.yml');
-  }
-
-}
diff --git a/tests/src/Kernel/UiPatternsManagerTest.php b/tests/src/Kernel/UiPatternsManagerTest.php
deleted file mode 100644
index a7f3198f..00000000
--- a/tests/src/Kernel/UiPatternsManagerTest.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Kernel;
-
-use Drupal\ui_patterns\UiPatterns;
-
-/**
- * @coversDefaultClass \Drupal\ui_patterns\UiPatternsManager
- *
- * @group ui_patterns
- */
-class UiPatternsManagerTest extends AbstractUiPatternsTest {
-
-  /**
-   * {@inheritdoc}
-   */
-  public static $modules = [
-    'system',
-    'ui_patterns',
-    'ui_patterns_library',
-  ];
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function setUp(): void {
-    parent::setUp();
-
-    // Theme with existing patterns has to be enabled.
-    $default_theme = 'ui_patterns_library_theme_test';
-    $this->container->get('theme_installer')->install([$default_theme]);
-    $this->container->get('config.factory')->getEditable('system.theme')->set('default', $default_theme)->save();
-  }
-
-  /**
-   * Test UiPatternsManager::getPatternDefinition.
-   *
-   * @covers ::getPatterns
-   */
-  public function testGetPattern() {
-    $manager = UiPatterns::getManager();
-    $definitions = $manager->getDefinitions();
-
-    foreach ($manager->getPatterns() as $pattern) {
-      $this->assertEquals($definitions[$pattern->getPluginId()]->id(), $pattern->getBaseId());
-    }
-  }
-
-}
diff --git a/tests/src/Kernel/UiPatternsPreviewTest.php b/tests/src/Kernel/UiPatternsPreviewTest.php
deleted file mode 100644
index affbfae1..00000000
--- a/tests/src/Kernel/UiPatternsPreviewTest.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Unit\Element;
-
-use Drupal\Tests\ui_patterns\Kernel\AbstractUiPatternsTest;
-use Drupal\Tests\ui_patterns\Traits\RenderTrait;
-
-/**
- * @coversDefaultClass \Drupal\ui_patterns\Element\PatternPreview
- *
- * @group ui_patterns
- */
-class UiPatternsPreviewTest extends AbstractUiPatternsTest {
-
-  use RenderTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  public static $modules = [
-    'system',
-    'ui_patterns',
-    'ui_patterns_library',
-    'ui_patterns_render_test',
-  ];
-
-  /**
-   * Test processContext.
-   *
-   * @dataProvider processContextDataProvider
-   *
-   * @covers ::processContext
-   */
-  public function testProcessContext($render, $expected) {
-    $this->renderRoot($render);
-    $this->assertEquals($expected, $render["#context"]->getType());
-  }
-
-  /**
-   * Data provider for Process Context tests.
-   *
-   * The actual data is read from fixtures stored in a YAML configuration.
-   *
-   * @return array
-   *   A set of dump data for testing.
-   */
-  public function processContextDataProvider() {
-    return $this->getFixtureContent('preview_process.yml');
-  }
-
-}
diff --git a/tests/src/Kernel/UiPatternsSourceManagerTest.php b/tests/src/Kernel/UiPatternsSourceManagerTest.php
deleted file mode 100644
index dd218a56..00000000
--- a/tests/src/Kernel/UiPatternsSourceManagerTest.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Kernel;
-
-/**
- * @coversDefaultClass \Drupal\ui_patterns\UiPatternsSourceManager
- *
- * @group ui_patterns
- */
-class UiPatternsSourceManagerTest extends AbstractUiPatternsTest {
-
-  /**
-   * {@inheritdoc}
-   */
-  public static $modules = [
-    'ui_patterns',
-    'ui_patterns_field_source_test',
-  ];
-
-  /**
-   * Test processDefinition.
-   *
-   * @covers ::processDefinition
-   */
-  public function testProcessDefinition() {
-    /** @var \Drupal\ui_patterns\UiPatternsSourceManager $service */
-    $plugin_manager = \Drupal::service('plugin.manager.ui_patterns_source');
-
-    $definitions = $plugin_manager->getDefinitions();
-    $this->assertNotEmpty($definitions);
-    $this->assertArrayHasKey('test_source', $definitions);
-
-    $expected = [
-      ['name' => 'field_1', 'label' => 'Field 1'],
-      ['name' => 'field_2', 'label' => 'Field 2'],
-      ['name' => 'field_3', 'label' => 'Field 3'],
-      ['name' => 'field_4', 'label' => 'Field 4'],
-      ['name' => 'field_5', 'label' => 'Field 5'],
-    ];
-
-    /** @var \Drupal\ui_patterns\Plugin\PatternSourceBase $plugin */
-    $plugin = $plugin_manager->createInstance('test_source');
-    foreach ($plugin->getSourceFields() as $key => $field) {
-      $this->assertEquals($expected[$key]['name'], $field->getFieldName());
-      $this->assertEquals($expected[$key]['label'], $field->getFieldLabel());
-    }
-  }
-
-}
diff --git a/tests/src/Traits/RenderTrait.php b/tests/src/Traits/RenderTrait.php
deleted file mode 100644
index 1ad8a160..00000000
--- a/tests/src/Traits/RenderTrait.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Traits;
-
-/**
- * Helper rendering trait.
- */
-trait RenderTrait {
-
-  /**
-   * Renders final HTML given a structured array tree.
-   *
-   * @param array $elements
-   *   The structured array describing the data to be rendered.
-   *
-   * @return string
-   *   The rendered HTML.
-   *
-   * @throws \Exception
-   *   When called from inside another renderRoot() call.
-   *
-   * @see \Drupal\Core\Render\RendererInterface::render()
-   */
-  protected function renderRoot(array &$elements) {
-    return (string) $this->container->get('renderer')->renderRoot($elements);
-  }
-
-}
diff --git a/tests/src/Traits/TwigDebugTrait.php b/tests/src/Traits/TwigDebugTrait.php
deleted file mode 100644
index d37416e2..00000000
--- a/tests/src/Traits/TwigDebugTrait.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Traits;
-
-/**
- * Provides shortcut to enable Twig debug mode on \Drupal\Tests\BrowserTestBase.
- */
-trait TwigDebugTrait {
-
-  /**
-   * Enable Twig debug mode.
-   */
-  public function enableTwigDebugMode() {
-    // Enable debug, rebuild the service container, and clear all caches.
-    $parameters = $this->container->getParameter('twig.config');
-    $parameters['debug'] = TRUE;
-    $this->setContainerParameter('twig.config', $parameters);
-    $this->rebuildContainer();
-    $this->resetAll();
-  }
-
-}
diff --git a/tests/src/Unit/AbstractUiPatternsTest.php b/tests/src/Unit/AbstractUiPatternsTest.php
deleted file mode 100644
index 0667fe3f..00000000
--- a/tests/src/Unit/AbstractUiPatternsTest.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Unit;
-
-use Drupal\Tests\UnitTestCase;
-
-/**
- * Abstract base test class.
- *
- * @group ui_patterns
- *
- * @package Drupal\Tests\ui_patterns\Unit
- */
-abstract class AbstractUiPatternsTest extends UnitTestCase {
-
-  /**
-   * Get fixtures base path.
-   *
-   * @return string
-   *   Fixtures base path.
-   */
-  protected function getFixturePath() {
-    return realpath(dirname(__FILE__) . '/../fixtures');
-  }
-
-}
diff --git a/tests/src/Unit/Definition/PatternDefinitionTest.php b/tests/src/Unit/Definition/PatternDefinitionTest.php
deleted file mode 100644
index dfaca61c..00000000
--- a/tests/src/Unit/Definition/PatternDefinitionTest.php
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Unit\Definition;
-
-use Drupal\Component\Serialization\Yaml;
-use Drupal\Tests\ui_patterns\Unit\AbstractUiPatternsTest;
-use Drupal\ui_patterns\Definition\PatternDefinition;
-
-/**
- * @coversDefaultClass \Drupal\ui_patterns\Definition\PatternDefinition
- *
- * @group ui_patterns
- */
-class PatternDefinitionTest extends AbstractUiPatternsTest {
-
-  /**
-   * Test getters.
-   *
-   * @dataProvider definitionGettersProvider
-   */
-  public function testGettersSetters($getter, $name, $value) {
-    $pattern_definition = new PatternDefinition([$name => $value]);
-    $this->assertEquals(call_user_func([$pattern_definition, $getter]), $value);
-  }
-
-  /**
-   * Test field singleton.
-   *
-   * @dataProvider definitionGettersProvider
-   */
-  public function testFields() {
-    $fields = [
-      'name' => [
-        'name' => 'name',
-        'label' => 'Label',
-      ],
-    ];
-    $pattern_definition = new PatternDefinition();
-    $pattern_definition->setFields($fields);
-    $this->assertEquals(
-      [
-        $fields['name']['label'],
-        $fields['name']['name'],
-        NULL,
-        NULL,
-        NULL,
-      ],
-      [
-        $pattern_definition->getField('name')->getLabel(),
-        $pattern_definition->getField('name')->getName(),
-        $pattern_definition->getField('name')->getType(),
-        $pattern_definition->getField('name')->getDescription(),
-        $pattern_definition->getField('name')->getPreview(),
-      ]);
-
-    $pattern_definition->getField('name')->setType('type');
-    $pattern_definition->getField('name')->setPreview('preview');
-    $pattern_definition->getField('name')->setDescription('description');
-
-    $this->assertEquals(
-      [
-        'type',
-        'description',
-        'preview',
-      ],
-      [
-        $pattern_definition->getField('name')->getType(),
-        $pattern_definition->getField('name')->getDescription(),
-        $pattern_definition->getField('name')->getPreview(),
-      ]);
-  }
-
-  /**
-   * Test fields processing.
-   *
-   * @dataProvider fieldsProcessingProvider
-   */
-  public function testFieldsProcessing($actual, $expected) {
-    $pattern_definition = new PatternDefinition();
-    $data = $pattern_definition->setFields($actual)->toArray();
-    $this->assertEquals($expected, $data['fields']);
-  }
-
-  /**
-   * Provider.
-   *
-   * @return array
-   *   Data.
-   */
-  public function fieldsProcessingProvider() {
-    return Yaml::decode(file_get_contents($this->getFixturePath() . '/definition/fields_processing.yml'));
-  }
-
-  /**
-   * Test fields processing.
-   *
-   * @dataProvider variantsProcessingProvider
-   */
-  public function testVariantsProcessing($actual, $expected) {
-    $pattern_definition = new PatternDefinition();
-    $data = $pattern_definition->setVariants($actual)->toArray();
-    $this->assertEquals($expected, $data['variants']);
-  }
-
-  /**
-   * Provider.
-   *
-   * @return array
-   *   Data.
-   */
-  public function variantsProcessingProvider() {
-    return Yaml::decode(file_get_contents($this->getFixturePath() . '/definition/variants_processing.yml'));
-  }
-
-  /**
-   * Provider.
-   *
-   * @return array
-   *   Data.
-   */
-  public function definitionGettersProvider() {
-    return [
-      ['getProvider', 'provider', 'my_module'],
-      ['id', 'id', 'pattern_id'],
-      ['getLabel', 'label', 'Pattern label'],
-      ['getDescription', 'description', 'Pattern description.'],
-      ['getUse', 'use', 'template.twig'],
-      ['hasCustomThemeHook', 'custom theme hook', FALSE],
-      ['getThemeHook', 'theme hook', 'eme hook: custom_my_theme_hook'],
-      ['getTemplate', 'template', 'my-template.html.twig'],
-      ['getFileName', 'file name', '/path/to/filename.ui_patterns.yml'],
-      ['getClass', 'class', '\Drupal\ui_patterns\MyClass'],
-      ['getBasePath', 'base path', '/path/to'],
-      ['getTags', 'tags', ['a', 'b']],
-    ];
-  }
-
-}
diff --git a/tests/src/Unit/Element/PatternPreviewTest.php b/tests/src/Unit/Element/PatternPreviewTest.php
deleted file mode 100644
index 3bd41623..00000000
--- a/tests/src/Unit/Element/PatternPreviewTest.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ui_patterns\Unit\Element;
-
-use Drupal\Component\Serialization\Yaml;
-use Drupal\Tests\ui_patterns\Unit\AbstractUiPatternsTest;
-use Drupal\ui_patterns\Element\PatternPreview;
-
-/**
- * @coversDefaultClass \Drupal\ui_patterns\Element\PatternPreview
- *
- * @group ui_patterns
- */
-class PatternPreviewTest extends AbstractUiPatternsTest {
-
-  /**
-   * Test getPreviewMarkup.
-   *
-   * @covers ::getPreviewMarkup
-   */
-  public function testPreviewMarkup() {
-    $assertions = Yaml::decode(file_get_contents($this->getFixturePath() . '/preview_markup.yml'));
-    foreach ($assertions as $assertion) {
-      $result = PatternPreview::getPreviewMarkup($assertion['actual']);
-      $this->assertEquals($assertion['expected'], $result);
-    }
-  }
-
-}
diff --git a/tests/src/Unit/SchemaCompatibilityCheckerTest.php b/tests/src/Unit/SchemaCompatibilityCheckerTest.php
new file mode 100644
index 00000000..19e07c2f
--- /dev/null
+++ b/tests/src/Unit/SchemaCompatibilityCheckerTest.php
@@ -0,0 +1,49 @@
+<?php declare(strict_types = 1);
+
+namespace Drupal\Tests\ui_patterns\Unit;
+
+use Drupal\Tests\UnitTestCase;
+use Drupal\ui_patterns\Utils\SchemaCompatibilityChecker;
+use Drupal\Component\Serialization\Yaml;
+
+/**
+ * Test description.
+ *
+ * @group ui_patterns
+ */
+final class SchemaCompatibilityCheckerTest extends UnitTestCase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+  }
+
+  /**
+   * Tests something.
+   *
+   * @dataProvider provideIsCompatibleData
+   */
+  public function testIsCompatible($test_name, $checked_schema, $reference_schema, $expected_result): void {
+    $validator = new SchemaCompatibilityChecker();
+    self::assertEquals($expected_result, $validator->isCompatible($checked_schema, $reference_schema));
+  }
+
+  public function provideIsCompatibleData() {
+    $data = [];
+    $sources = Yaml::decode(file_get_contents(__DIR__ . "/schema_compatibility_checker_data.yml"));
+    foreach ($sources as $source) {
+      foreach ($source["tests"] as $test) {
+        $data[] = [
+          $source["label"] . ": " . $test["label"] . " is " . ($test["result"] ? "OK" : "KO"),
+          $test["schema"],
+          $source["schema"],
+          (bool) $test["result"],
+        ];
+      }
+    };
+    return $data;
+  }
+
+}
diff --git a/tests/src/Unit/schema_compatibility_checker_data.yml b/tests/src/Unit/schema_compatibility_checker_data.yml
new file mode 100644
index 00000000..837ee0f1
--- /dev/null
+++ b/tests/src/Unit/schema_compatibility_checker_data.yml
@@ -0,0 +1,227 @@
+# String prop type
+- label: "String prop type"
+  schema:
+    type: string
+  tests:
+    - label: "String"
+      schema:
+        type: string
+      result: true
+    - label: "String with a format"
+      schema:
+        type: string
+        format: whatever
+      result: true
+    - label: "String with max length"
+      schema:
+        type: string
+        maxLength: 10
+      result: true
+
+# Other string tests
+- label: "String with max length"
+  schema:
+    type: string
+    maxLength: 10
+  tests:
+    - label: "No length"
+      schema:
+        type: string
+      result: false
+    - label: "Same max length"
+      schema:
+        type: string
+        maxLength: 10
+      result: true
+    - label: "Smaller max length"
+      schema:
+        type: string
+        maxLength: 5
+      result: true
+    - label: "Larger max length"
+      schema:
+        type: string
+        maxLength: 20
+      result: false
+- label: "String with min length"
+  schema:
+    type: string
+    minLength: 10
+  tests:
+    - label: "No length"
+      schema:
+        type: string
+      result: false
+    - label: "Same min length"
+      schema:
+        type: string
+        minLength: 10
+      result: true
+    - label: "Smaller min length"
+      schema:
+        type: string
+        minLength: 5
+      result: false
+    - label: "Larger min length"
+      schema:
+        type: string
+        minLength: 20
+      result: true
+
+# Enum prop type
+- label: "Enum prop type"
+  schema:
+    type: [string, number, integer]
+    enum: []
+  tests:
+    - label: "Empty enum"
+      schema:
+        type: string
+        enum: []
+      result: true
+    - label: "With strings"
+      schema:
+        type: string
+        enum:
+          - foo
+          - bar
+          - baz
+      result: true
+    - label: "With numbers"
+      schema:
+        type: number
+        enum:
+          - 4
+          - 5
+          - 8
+      result: true
+
+# Other enum tests
+- label: "Enum with 2 strings"
+  schema:
+    type: string
+    enum:
+      - foo
+      - bar
+  tests:
+    - label: "Same enum"
+      schema:
+        type: string
+        enum:
+          - foo
+          - bar
+      result: true
+    - label: "Enum with an addition"
+      schema:
+        type: string
+        enum:
+          - foo
+          - bar
+          - baz
+      result: false
+    - label: "Enum with a removal"
+      schema:
+        type: string
+        enum:
+          - foo
+      result: true
+
+# Number prop type
+- label: "Number prop type"
+  schema:
+    type: number
+  tests:
+    - label: "Number"
+      schema:
+        type: number
+      result: true
+    - label: "Integer"
+      schema:
+        type: integer
+      result: true
+
+# Url prop type
+- label: "URL prop type"
+  schema:
+    type: string
+    format: iri-reference
+  tests:
+    - label: "IRI Reference"
+      schema:
+        type: string
+        format: iri-reference
+      result: true
+    - label: "URI reference"
+      schema:
+        type: string
+        format: uri-reference
+      result: true
+    - label: "IRI"
+      schema:
+        type: string
+        format: iri
+      result: true
+    - label: "URI"
+      schema:
+        type: string
+        format: uri
+      result: true
+
+# Color prop type
+- label: "Color prop type"
+  schema:
+    type: string
+    pattern: "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
+  tests:
+    - label: "Color (6 or 3 hex)"
+      schema:
+        type: string
+        pattern: "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
+      result: true
+#    - label: "Color (6)"
+#      schema:
+#        type: string
+#        pattern: "^#([A-Fa-f0-9]{6})$"
+#      result: true
+#    - label: "Color (3)"
+#      schema:
+#        type: string
+#        pattern: "^#([A-Fa-f0-9]{3})$"
+#      result: true
+
+# Links prop type
+- label: "Links prop type"
+  schema:
+    type: array
+    items:
+      type: object
+      properties:
+        title: { type: string }
+        attributes: { $ref: "ui-patterns://attributes" }
+        below: { $ref: "ui-patterns://links" }
+  tests:
+    - label: "Links"
+      schema:
+        type: array
+        items:
+          type: object
+          properties:
+            title: { type: string }
+            attributes: { $ref: "ui-patterns://attributes" }
+            below: { $ref: "ui-patterns://links" }
+      result: true
+    - label: "Links with extra property on"
+      schema:
+        type: array
+        items:
+          type: object
+          properties:
+            title: { type: string }
+            attributes: { $ref: "ui-patterns://attributes" }
+            below: { $ref: "ui-patterns://links" }
+            extra: { type: string }
+      result: true
+    - label: "Simple array"
+      schema:
+        type: array
+      result: false
diff --git a/tests/src/fixtures/definition/fields_processing.yml b/tests/src/fixtures/definition/fields_processing.yml
deleted file mode 100644
index 97219e18..00000000
--- a/tests/src/fixtures/definition/fields_processing.yml
+++ /dev/null
@@ -1,58 +0,0 @@
-- actual:
-    - field1
-    - field2
-  expected:
-    field1:
-      name: field1
-      label: field1
-      description: ~
-      type: ~
-      preview: ~
-      escape: TRUE
-    field2:
-      name: field2
-      label: field2
-      description: ~
-      type: ~
-      preview: ~
-      escape: TRUE
-- actual:
-    field1: Field 1
-    field2: Field 2
-  expected:
-    field1:
-      name: field1
-      label: Field 1
-      description: ~
-      type: ~
-      preview: ~
-      escape: TRUE
-    field2:
-      name: field2
-      label: Field 2
-      description: ~
-      type: ~
-      preview: ~
-      escape: TRUE
-- actual:
-    field1:
-      label: field1
-  expected:
-    field1:
-      name: field1
-      label: field1
-      description: ~
-      type: ~
-      preview: ~
-      escape: TRUE
-- actual:
-    - name: field1
-      label: Field 1
-  expected:
-    field1:
-      name: field1
-      label: Field 1
-      description: ~
-      type: ~
-      preview: ~
-      escape: TRUE
diff --git a/tests/src/fixtures/definition/variants_processing.yml b/tests/src/fixtures/definition/variants_processing.yml
deleted file mode 100644
index d70600f2..00000000
--- a/tests/src/fixtures/definition/variants_processing.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-- actual:
-    - variant1
-    - variant2
-  expected:
-    variant1:
-      name: variant1
-      label: variant1
-      description: ~
-    variant2:
-      name: variant2
-      label: variant2
-      description: ~
-- actual:
-    variant1: Variant 1
-    variant2: Variant 2
-  expected:
-    variant1:
-      name: variant1
-      label: Variant 1
-      description: ~
-    variant2:
-      name: variant2
-      label: Variant 2
-      description: ~
-- actual:
-    variant1:
-      label: variant1
-  expected:
-    variant1:
-      name: variant1
-      label: variant1
-      description: ~
-- actual:
-    - name: variant1
-      label: Variant 1
-  expected:
-    variant1:
-      name: variant1
-      label: Variant 1
-      description: ~
diff --git a/tests/src/fixtures/libraries.yml b/tests/src/fixtures/libraries.yml
deleted file mode 100644
index 19acdd1e..00000000
--- a/tests/src/fixtures/libraries.yml
+++ /dev/null
@@ -1,105 +0,0 @@
-- actual:
-    id: pattern_name
-    base path: /pattern/base/path
-    libraries:
-      - drupal/library_one
-      - drupal/library_two
-      - library_one:
-          css:
-            component:
-              library_one.css: {}
-              library_two.css: {}
-              library_three.css: {}
-            theme:
-              library_one.css: {}
-              library_two.css: {}
-          js:
-            library_one.js: {}
-      - library_two:
-          css:
-            component:
-              library_one.css: {}
-          js:
-            library_two.js: {}
-  expected:
-    pattern_name.library_one:
-      css:
-        component:
-          /pattern/base/path/library_one.css: {}
-          /pattern/base/path/library_two.css: {}
-          /pattern/base/path/library_three.css: {}
-        theme:
-          /pattern/base/path/library_one.css: {}
-          /pattern/base/path/library_two.css: {}
-      js:
-        /pattern/base/path/library_one.js: {}
-    pattern_name.library_two:
-      css:
-         component:
-           /pattern/base/path/library_one.css: {}
-      js:
-        /pattern/base/path/library_two.js: {}
-- actual:
-    id: pattern_name
-    base path: /pattern/base/path
-    libraries:
-      - library_one:
-          css:
-            component:
-              library_one.css: {}
-              http://example.com/external.min.css: { type: external, minified: true }
-              library_two.css: {}
-            theme:
-              http://example.com/external.min.css: { type: external, minified: true }
-          js:
-            library_one.js: {}
-            http://example.com/external.min.js: { type: external, minified: true }
-            library_two.js: {}
-  expected:
-    pattern_name.library_one:
-      css:
-        component:
-          /pattern/base/path/library_one.css: {}
-          http://example.com/external.min.css: { type: external, minified: true }
-          /pattern/base/path/library_two.css: {}
-        theme:
-          http://example.com/external.min.css: { type: external, minified: true }
-      js:
-        /pattern/base/path/library_one.js: {}
-        http://example.com/external.min.js: { type: external, minified: true }
-        /pattern/base/path/library_two.js: {}
-- actual:
-    id: pattern_name
-    libraries:
-      - drupal/library_one
-      - drupal/library_two
-  expected: []
-- actual:
-    id: pattern_name
-    base path: ''
-    libraries:
-      - library_one:
-          css:
-            component:
-              library_one.css: {}
-          js:
-            library_one.js: {}
-      - library_two:
-          css:
-            component:
-              library_one.css: {}
-          js:
-            library_two.js: {}
-  expected:
-    pattern_name.library_one:
-      css:
-        component:
-          library_one.css: {}
-      js:
-        library_one.js: {}
-    pattern_name.library_two:
-      css:
-         component:
-           library_one.css: {}
-      js:
-        library_two.js: {}
diff --git a/tests/src/fixtures/pattern_element_libraries.yml b/tests/src/fixtures/pattern_element_libraries.yml
deleted file mode 100644
index a679757a..00000000
--- a/tests/src/fixtures/pattern_element_libraries.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-- actual:
-    id: pattern_name
-    libraries:
-      - drupal/library_one
-      - drupal/library_two
-      - library_one:
-          css:
-            component:
-              library_one.css: {}
-            theme:
-              library_one.css: {}
-          js:
-            library_one.js: {}
-      - library_two:
-          css:
-            component:
-              library_one.css: {}
-          js:
-            library_two.js: {}
-  expected:
-    - drupal/library_one
-    - drupal/library_two
-    - ui_patterns/pattern_name.library_one
-    - ui_patterns/pattern_name.library_two
diff --git a/tests/src/fixtures/preview_markup.yml b/tests/src/fixtures/preview_markup.yml
deleted file mode 100644
index 68892d70..00000000
--- a/tests/src/fixtures/preview_markup.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-- actual:
-    type: pattern
-    id: image
-    fields:
-      image:
-        theme: image
-        uri: http://lorempixel.com/400/200/nature/2
-  expected:
-    '#type': pattern
-    '#id': image
-    '#fields':
-      image:
-        '#theme': image
-        '#uri': http://lorempixel.com/400/200/nature/2
diff --git a/tests/src/fixtures/preview_process.yml b/tests/src/fixtures/preview_process.yml
deleted file mode 100644
index 675fb040..00000000
--- a/tests/src/fixtures/preview_process.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-- render:
-    '#type': pattern_preview
-    '#id': foo
-  expected: preview
-- render:
-    '#type': pattern
-    '#id': foo
-  expected: empty
diff --git a/tests/src/fixtures/validation.yml b/tests/src/fixtures/validation.yml
deleted file mode 100644
index 79700e9e..00000000
--- a/tests/src/fixtures/validation.yml
+++ /dev/null
@@ -1,67 +0,0 @@
-- pattern:
-    id: 'not valid Name'
-  messages:
-    - "id: This value is not valid."
-    - "label: This value should not be null."
-    - "base path: This value should not be null."
-    - "file name: This value should not be null."
-    - "provider: This value should not be null."
-    - "theme hook: This value should not be null."
-- pattern:
-    id: 'id'
-  messages:
-    - "id: This value is not valid."
-    - "label: This value should not be null."
-    - "base path: This value should not be null."
-    - "file name: This value should not be null."
-    - "provider: This value should not be null."
-    - "theme hook: This value should not be null."
-- pattern:
-    id: pattern_id
-    label: label
-    description: description
-    fields:
-      fass:
-        name: 'not valid name'
-  messages:
-    - "base path: This value should not be null."
-    - "file name: This value should not be null."
-    - "provider: This value should not be null."
-    - "fields.0.name: This value is not valid."
-    - "fields.0.label: This value should not be null."
-    - "theme hook: This value should not be null."
-- pattern:
-    id: pattern_id
-    label: label
-    description: description
-    fields:
-      type:
-        type: text
-        label: type
-        description: description
-        preview: preview
-  messages:
-    - "base path: This value should not be null."
-    - "file name: This value should not be null."
-    - "provider: This value should not be null."
-    - "fields.0.name: This value should not be null."
-    - "theme hook: This value should not be null."
-- pattern:
-    id: pattern_id
-    label: label
-    description: description
-    base path: /path/to/pattern
-    file name: pattern_id.ui_patterns.yml
-    theme hook: pattern_pattern_id
-    provider: module
-    fields:
-      -
-        name: camelName
-        label: Label
-        type: text
-        description: description
-        preview: preview
-      -
-        name: name
-        label: Label
-  messages: []
diff --git a/ui_patterns.api.php b/ui_patterns.api.php
deleted file mode 100644
index 96b837fa..00000000
--- a/ui_patterns.api.php
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php
-
-/**
- * @file
- * API file.
- */
-
-use Drupal\ui_patterns\Element\PatternContext;
-
-/**
- * Alter UI Patterns definitions.
- *
- * @param \Drupal\ui_patterns\Definition\PatternDefinition[] $definitions
- *   Pattern definitions.
- *
- * @see \Drupal\ui_patterns\UiPatternsManager
- */
-function hook_ui_patterns_info_alter(array &$definitions) {
-  $definitions['my_pattern']->setLabel('My new label');
-}
-
-/**
- * Alter UI Patterns Source definitions.
- *
- * @see \Drupal\ui_patterns\UiPatternsSourceManager
- */
-function hook_ui_patterns_ui_patterns_source_info_alter(&$definitions) {
-  $definitions['my_field_source']['tags'][] = 'new_tag';
-}
-
-/**
- * Provide hook theme suggestions for patterns.
- *
- * @see ui_patterns_theme_suggestions_alter()
- */
-function hook_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) {
-  if ($context->isOfType('views_row')) {
-    $hook = $variables['theme_hook_original'];
-    $view_name = $context->getProperty('view_name');
-    $display = $context->getProperty('display');
-
-    $suggestions[] = $hook . '__views_row__' . $view_name;
-    $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $display;
-  }
-}
-
-/**
- * Provide hook theme suggestions for patterns destination wrapper.
- *
- * A pattern render element having '#multiple_sources' set to TRUE can render
- * multiple sources on the same destination field. Sources will be rendered
- * using the 'patterns_destination' theme function which will use the
- * 'patterns-destination.html.twig' template file.
- *
- * Developers can take over rendering of the template above by providing proper
- * suggestions, this is useful in case you wish to provide separators or other
- * wrapping elements.
- *
- * @see ui_patterns_theme_suggestions_alter()
- * @see \Drupal\ui_patterns\Element\Pattern::processMultipleSources()
- */
-function hook_ui_patterns_destination_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) {
-  if ($context->isOfType('views_row')) {
-    $hook = $variables['theme_hook_original'];
-    $view_name = $context->getProperty('view_name');
-    $display = $context->getProperty('display');
-    $pattern = $context->getProperty('pattern');
-    $field = $context->getProperty('field');
-
-    $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $pattern . '__' . $field;
-    $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $display . '__' . $pattern . '__' . $field;
-  }
-}
-
-/**
- * Alter pattern settings form under "Manage display".
- *
- * @param array $form
- *   Pattern settings fieldset.
- * @param array $configuration
- *   Pattern configuration.
- */
-function hook_ui_patterns_display_settings_form_alter(array &$form, array $configuration) {
-  $form['element'] = ['#type' => 'input'];
-}
diff --git a/ui_patterns.info.yml b/ui_patterns.info.yml
index fdd0b2e2..00543fbf 100644
--- a/ui_patterns.info.yml
+++ b/ui_patterns.info.yml
@@ -1,6 +1,7 @@
-name: UI Patterns
+name: "UI Patterns"
 type: module
-description: UI patterns
-core: 8.x
-core_version_requirement: ^8 || ^9
-package: User interface
+description: "Define and expose self-contained UI Components as Drupal plugins and use them seamlessly in Drupal development and site-building."
+core_version_requirement: ^10
+package: "User interface"
+dependencies:
+  - drupal:sdc
diff --git a/ui_patterns.module b/ui_patterns.module
deleted file mode 100644
index d4d61cd7..00000000
--- a/ui_patterns.module
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains ui_patterns.module.
- */
-
-use Drupal\ui_patterns\UiPatterns;
-use Drupal\ui_patterns\Element\PatternContext;
-
-/**
- * Implements hook_theme().
- */
-function ui_patterns_theme() {
-  /** @var \Drupal\ui_patterns\Plugin\PatternBase $pattern */
-  $items = [
-    'patterns_destination' => [
-      'variables' => ['sources' => NULL, 'context' => NULL],
-    ],
-    'patterns_use_wrapper' => [
-      'variables' => ['use' => NULL],
-    ],
-  ];
-
-  foreach (UiPatterns::getManager()->getPatterns() as $pattern) {
-    $items += $pattern->getThemeImplementation();
-  }
-  return $items;
-}
-
-/**
- * Implements hook_library_info_build().
- */
-function ui_patterns_library_info_build() {
-  /** @var \Drupal\ui_patterns\Plugin\PatternBase $pattern */
-
-  $definitions = [];
-  foreach (UiPatterns::getManager()->getPatterns() as $pattern) {
-    $definitions += $pattern->getLibraryDefinitions();
-  }
-  return $definitions;
-}
-
-/**
- * Implements hook_theme_suggestions_HOOK_alter().
- */
-function ui_patterns_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
-  if (UiPatterns::getManager()->isPatternHook($hook)) {
-    \Drupal::moduleHandler()->alter('ui_patterns_suggestions', $suggestions, $variables, $variables['context']);
-    \Drupal::theme()->alter('ui_patterns_suggestions', $suggestions, $variables, $variables['context']);
-  }
-
-  if ($hook == 'patterns_destination') {
-    \Drupal::moduleHandler()->alter('ui_patterns_destination_suggestions', $suggestions, $variables, $variables['context']);
-    \Drupal::theme()->alter('ui_patterns_destination_suggestions', $suggestions, $variables, $variables['context']);
-  }
-}
-
-/**
- * Implements hook_ui_patterns_suggestions_alter().
- */
-function ui_patterns_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) {
-  // Add preview theme suggestion.
-  if ($context->isOfType('preview')) {
-    $suggestions[] = $variables['theme_hook_original'] . '__preview';
-  }
-
-  if (!empty($variables['variant'])) {
-    $suggestions[] = $variables['theme_hook_original'] . '__variant_' . $variables['variant'];
-
-    // Add variant preview theme suggestion.
-    if ($context->isOfType('preview')) {
-      $suggestions[] = $variables['theme_hook_original'] . '__variant_' . $variables['variant'] . '__preview';
-    }
-  }
-}
diff --git a/ui_patterns.services.yml b/ui_patterns.services.yml
index cb420c61..c3b3d1fc 100644
--- a/ui_patterns.services.yml
+++ b/ui_patterns.services.yml
@@ -1,11 +1,36 @@
 services:
-  plugin.manager.ui_patterns:
-    class: Drupal\ui_patterns\UiPatternsManager
-    arguments: ['@container.namespaces', '@module_handler', '@theme_handler', '@cache.discovery']
+  plugin.manager.ui_patterns_prop_type:
+    class: Drupal\ui_patterns\PropTypePluginManager
+    arguments: [ '@container.namespaces', '@cache.discovery', '@module_handler',  '@Drupal\sdc\Component\SchemaCompatibilityChecker' ]
   plugin.manager.ui_patterns_source:
-    class: Drupal\ui_patterns\UiPatternsSourceManager
+    class: Drupal\ui_patterns\SourcePluginManager
     parent: default_plugin_manager
   ui_patterns.twig.extension:
     class: Drupal\ui_patterns\Template\TwigExtension
     tags:
       - { name: twig.extension }
+  ui_patterns.plugin.manager.sdc:
+    class: Drupal\ui_patterns\Sdc\UiPatternsSdcPluginManager
+    decorates: plugin.manager.sdc
+    decoration_priority: 9
+    public: false
+    arguments:
+      - '@ui_patterns.plugin.manager.sdc.inner'
+      - '@plugin.manager.ui_patterns_prop_type'
+      - '@plugin.manager.ui_patterns_source'
+      - '@module_handler'
+      - '@theme_handler'
+      - '@cache.discovery'
+      - '@config.factory'
+      - '@theme.manager'
+      - '@Drupal\sdc\ComponentNegotiator'
+      - '@file_system'
+      - '@Drupal\sdc\Component\SchemaCompatibilityChecker'
+      - '@Drupal\sdc\Component\ComponentValidator'
+      - '%app.root%'
+  stream_wrapper.ui_patterns_prop_type:
+    class: Drupal\ui_patterns\StreamWrapper\UiPatternsPropTypeStreamWrapper
+    arguments:
+      - '@plugin.manager.ui_patterns_prop_type'
+    tags:
+      - { name: stream_wrapper, scheme: ui-patterns }