Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Toolkit] Introduce the UX Toolkit ✨ #2539

Draft
wants to merge 10 commits into
base: 2.x
Choose a base branch
from

Conversation

Halleck45
Copy link

@Halleck45 Halleck45 commented Jan 31, 2025

Q A
Bug fix? no
New feature? yes
Issues
License MIT

Following numerous discussions with various people, I'm opening this PR as a draft for a potential ux-toolkit component. This is the result of a joint reflection with @Kocal and @smnandre .

I’m not speaking on their behalf, but summarizing my interpretation of our discussions. Of course, @Kocal and @smnandre, feel free to correct, adjust, or add anything as needed.

Why?

Saving time

Creating Twig components like Badge or Button seems to be a very common step. The same work appears to be done over and over again, with minor differences from one company to another.

Providing a "packaged" toolkit would allow these companies and developers to save time by starting from ready-made templates.

Simplifying the process

Today, a beginner coming to Symfony has a lot to learn. Onboarding on a Symfony project can feel overwhelming, so it makes sense to offer them the most pleasant experience possible—including making it as easy as possible to create a clean and visually appealing web page.

Improving quality and accessibility

By providing a toolkit, we can leverage an entire community to gradually enhance the quality of components, including their digital accessibility. Accessibility is a major challenge, and we can aim to provide components that help companies and developers better support it.

How?

By providing:

  • Boilerplate for basic, accessible components (button, badge, etc.)
  • A symfony console ux:toolkit:install Button command that will generate the corresponding Twig component in the user's project. If necessary, this command will also install any required dependencies (if a component relies on another one).

Components will be unique.

It will be possible for the community to distribute unofficial components: for example, the command could accept symfony console ux:toolkit:install github.com/MyRemote/UiKit:Button

The website https://ux.symfony.com/ will include:

  • Presentations of each component, with a demo and the code needed to use it.
  • Options for automatic installation (via the command) or manual installation (by copying and pasting Twig code).
  • A few demos showcasing different variants.

Philosophy

Since the web is constantly evolving, it would be difficult to commit to maintaining components that require frequent updates.

An approach similar to shadcn seems more suitable: components are generated locally on demand by the developer, who will handle updates if necessary.

This approach likely offers the greatest flexibility: the developer can customize their component as they see fit, add their own variants, and adapt it to their specific needs.

The list of components could be based on the OpenUI initiative, which already provides a solid selection.

The symfony/ux-toolkit package will not be responsible for installing the CSS required for the components to function. It remains the developer's responsibility to manage their assets.

Next steps

There’s still a lot of work to do, which @Kocal will outline shortly. But it would be great to get feedback on the approach.

There is a lot of work to do; if the project is approved and the chosen direction is satisfactory, it would be great to create high-quality components. Looking forward to your feedback!

UX is an amazing component - it just needs "that little extra something" to truly make a difference in the daily lives of Symfony developers. I hope this kind of feature can help achieve that.

Takslist

  • Initialize symfony/ux-toolkit boilerplate
  • Create configuration tree:
ux_toolkit:
    # Either `default` or `new-york`, like https://ui.shadcn.com/ themes
    theme: default

    # Similar to https://symfony.com/bundles/ux-twig-component/current/index.html#configuration
    prefix: null
  • Do not hard depend on https://github.com/tales-from-a-dev/twig-tailwind-extra, but dev dependency, and document that the user needs to install tales-from-a-dev/twig-tailwind-extra if it wants to fully use our components
  • Implement ux:toolkit:install command
    • Handle local components (i.e.: ux:toolkit:install Button)
    • Handle remote components (i.e.: ux:toolkit:install github.com/MyRemote/UiKit:Button)
    • The command must use the Registry system (see below)
    • Support dependencies across components
    • Check if component's files already exist before writing, and ask to the user its choice
    • We must take the user's prefix when installing components, e.g.: <twig:Badge> must be replaced by <twig:Foobar:Badge> if the user use the prefix Foobar
  • The Registry system
    • It's a bunch of .json containing the list of components, their name, type (component or example/variant), registry dependencies, source code, ... that will be used by the ux:toolkit:install command and the ux.symfony.com website:
➜  Toolkit git:(ux_toolkit) tree registry 
registry
└── default
    ├── components
    │   ├── Alert.json
    │   ├── Badge.json
    │   ├── Button.json
    │   ├── Card.json
    │   ├── Navbar.json
    │   ├── Table
    │   │   └── Row.json
    │   └── Table.json
    └── examples
        ├── Badge.json
        ├── BadgeOutline.json
        └── Button.json

# default/components/Button.json
{
    "name": "Button",
    "theme": "default",
    "type": "component",
    "code": "<button {{ attributes.without('class') }}\n    class=\"{{ (' ' ~ attributes.render('class'))|tailwind_merge }}\"\n>\n    {% block content %}Button{% endblock %}\n<\/button>\n",
    "dependencies": []
}

# default/components/Button.json
{
    "name": "Table",
    "theme": "default",
    "type": "component",
    "code": "{# ux:with{Row, Button} #}\n<table {{ attributes.without('class') }}\n    class=\"{{ (' ' ~ attributes.render('class'))|tailwind_merge }}\"\n>\n    {% block content %}{% endblock %}\n<\/table>\n",
    "dependencies": [
        "Row"
    ]
}
  • For the Registry
    • Architecture Registry and RegistryItem
    • Provide a PHP script that build this Registry
    • The Registry must be able to read and handle components
    • The Registry must be able to read and handle examples
  • Implement components:
    • List, based on https://ui.shadcn.com/docs(and https://open-ui.org/)
      • Button
      • Badge
      • Avatar
      • ... to complete
    • Components must use html_cva, to provide a good development and customization experience for UX maintainers and users
    • Components must stay customizable from the outside, by passing the class property, by using the following boilerplate: <div class="{{ style.apply({}, attributes.render('class'))|tailwind_merge }}" {{ attributes.defaults({}).without('class') }}>
    • Add many examples/variations of components
  • Add a new page on ux.symfony.com
    • Some documentation about the Toolkit's philosophy, ...
    • Document about tales-from-a-dev/twig-tailwind-extra non hard-requirement, and what it can provides to users
    • Document how users can create their own toolkit (put symfony/ux-toolkit in dev dependency, run ./vendor/symfony/ux-toolkit/bin/generate-registry.php, ...)
    • Based on the Registry system
    • One page per component
      • Show automatic or manual installation steps
      • Show a demo and source code of the component
      • Show examples/variants

@carsonbot carsonbot added Feature New Feature Status: Needs Review Needs to be reviewed labels Jan 31, 2025
@Kocal Kocal changed the title Initialized the Toolkit component [Toolkit] Introduce the UX Toolkit ✨ Jan 31, 2025
@smnandre
Copy link
Member

Let's get to work! 👏

Copy link
Member

@smnandre smnandre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First comments on "form" only, just to warn early about some details

@Kocal Kocal added Status: Needs Work Additional work is needed and removed Status: Needs Review Needs to be reviewed labels Jan 31, 2025
@carsonbot carsonbot added Status: Needs Review Needs to be reviewed and removed Status: Needs Work Additional work is needed labels Feb 1, 2025
@seb-jean
Copy link
Contributor

seb-jean commented Feb 2, 2025

Wow, that's a awesome PR! 👏
I like it!

@ker0x
Copy link
Contributor

ker0x commented Feb 5, 2025

I really like the component idea, but is there any particular reason to store components source code in a json file rather than going through stubs files?

I'm afraid it's going to be hard to maintain because you'll have to remember to explicitly include line breaks, indentation and proper escaping!

Wouldn't it be better to have the code directly in {Component].html.twig files, so as to benefit from the autocompletion features of the IDE/text editor and reference it in the json?

Unless json are automatically generated 🤔?

@Halleck45
Copy link
Author

Unless json are automatically generated 🤔?

@ker0x yes, all JSON files are automatically generated from the html.twig files :) .

Relying on JSON files may evolve, but for now, it is the simplest and most sustainable solution we've found. It allows for easy discoverability of components, and generating the files is very straightforward. That being said, if we find a way to eliminate the need for them entirely, this approach may change.

@ker0x
Copy link
Contributor

ker0x commented Feb 5, 2025

@Halleck45 Thanks for the clarification!

I hadn't seen the .html.twig files, so I thought that the code for each component had to be written manually in the json files, hence my concerns!

But if they're automatically generated, then I don't see any problem in using this format 👍

@nfacciolo
Copy link

Are you planning to make a component accessible like this: <twig:Action:Button></twig:Action:Button> ?

Do you need any help? I can offer some time to contribute to this awesome PR.

Thanks for your work!

@javiereguiluz
Copy link
Member

This looks very interesting. Thanks!

A quick question: how can third-party bundles (e.g. SonataAdmin or EasyAdmin) install and use these UI components to build their interfaces?

@Halleck45
Copy link
Author

Are you planning to make a component accessible like this: <twig:Action:Button></twig:Action:Button> ?

Do you need any help? I can offer some time to contribute to this awesome PR.

Thanks for your work!

@nfacciolo For now, yes (or rather twig:Button simply). In the future, the syntax might eventually evolve to on its own, but that's something to discuss, think about, etc., in another PR.

Yes, help is very welcome, that would be great! We need to adjust quite a few things, but there are tons of components left to build and a lot of documentation to write. The easiest way is probably to connect on the Symfony Slack.

A quick question: how can third-party bundles (e.g. SonataAdmin or EasyAdmin) install and use these UI components to build their interfaces?

@javiereguiluz Hi! I'm not sure I fully understand the question. As it stands, the components will be freely available for everyone to use and will be 100% customizable. Any Symfony project will be able to integrate them.

Was that the intent of your question?

@javiereguiluz
Copy link
Member

@Halleck45 let me explain what I mean.

You said that using elements from this UI toolkit will be as easy as:

(1) Run this command symfony console ux:toolkit:install Button
(2) If needed, tweak, change the downloaded component to make it yours

OK. Now, I'm developing a third-party bundle (e.g. EasyAdmin) that has a lot of UI features and I want to use this button from UX toolkit.

So, I run the command symfony console ux:toolkit:install Button ... and I see an error, because I cannot run any commands because this is not a full Symfony application.


In those cases, can third-party bundles just copy+paste some files manually from the UX repository to use those components or does this need more configuration + wiring? Thanks

@Kocal
Copy link
Member

Kocal commented Feb 9, 2025

Hey @javiereguiluz, indeed the command symfony console ux:toolkit:install is reserved to Symfony applications.

But we will also provide a manual installation (copy/paste) for each components, that will be visible on ux.symfony.com.

@sblondeau
Copy link
Contributor

Hi @Halleck45
Great work ! This is very interesting :-) One question, it will be tightly coupled to Tailwind if I understand correctly?
How could we use another css library or our own CSS classes ? We should have to adapt all the CSS classes in the html_cva ?
Thanks for clarification :-)

@Kocal
Copy link
Member

Kocal commented Feb 14, 2025

Yes it wil be tightly coupled to Tailwind (since we re-use examples from Shadcn). Thanks to the "pull" system we are implementing (like Shadcn does), the code you download will be yours and so open to fully customization. If you want to drop Tailwind for something else, you can, and yes CVA will make things easier on this point.

Also, surely in the future, we may propose other "themes" that can be based on another CSS framework, like Bootstrap for example.

@tibobaldwin
Copy link

tibobaldwin commented Feb 16, 2025

I'm curious to see some components like dialog or dropdown 😊.

@daFish
Copy link
Contributor

daFish commented Mar 5, 2025

Thanks @Halleck45 for this change. I tested some of the components (simple copy & paste) and saw that there needs to be additional configuration for Tailwind. There are currently no defined theme variables for things like destructive. Do you plan to add documentation for this?

@Halleck45
Copy link
Author

Thanks @Halleck45 for this change. I tested some of the components (simple copy & paste) and saw that there needs to be additional configuration for Tailwind. There are currently no defined theme variables for things like destructive. Do you plan to add documentation for this?

Hi ! We are planning two things:

  • Documentation first (I pushed an update to the documentation with information on this).
  • Eventually, removing external packages so that everything is managed by UX.

@Kocal
Copy link
Member

Kocal commented Mar 13, 2025

Hey! I just started a code review, let's begin with "visual" review first:

  1. On Packages page, there is no icon for the Toolkit yet, I believe this is on purpose? :
    Capture d’écran 2025-03-13 à 09 28 26
  2. The "copy" button color is wrong
  3. Badge examples do not work
    Capture d’écran 2025-03-13 à 09 29 48

Let's see the code next

Copy link
Member

@Kocal Kocal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your work, here are my final review comments. After, I believe we would be ready to merge the PR and continue to iterate on Toolkit.

Thanks!

@carsonbot carsonbot added Status: Needs Work Additional work is needed and removed Status: Needs Review Needs to be reviewed labels Mar 13, 2025
@weaverryan
Copy link
Member

Ok, I'll give it another look and see how it feels once you've had a chance to make some changes - just let me know!

@Halleck45
Copy link
Author

I think @Halleck45 is pretty busy with his new work and many other things, and I don't have so much news about him :/

Sorry about that, I've finally got a bit of breathing room again.

Wow, the discussions really moved forward all of a sudden! Thank you! It’s great to see that this PR sparked such a rich discussion — it means the time spent was worthwhile 🙏

I hope I didn’t miss any messages. To bounce back on what was said:

after a quick code review, it looks like "theme" and "package" terms are used for the same thing

Yes, that’s more of a leftover than a deliberate decision — simplifying makes sense. However, I do think it’s important to keep the possibility for a theme (or template, whatever we call it) to come from somewhere other than Symfony, and to be downloadable from multiple sources.

@weaverryan nope, I've just rebased the PR on 2.x branch, now I'm starting to address the review comments.

Thanks @Kocal ! Really sorry for the delay — I’m available this evening if you want to chat.

symfony console ux:toolkit:publish Button

The idea of "publishing" a component is really interesting — it does seem like a good solution for the user.

@smnandre
Copy link
Member

So guys IF you need anything from me i'll have a look at minor things tomorrow

we pwe provide a command to "publish" a component. This copies the component into the user's app and the user now owns itrovide a command to "publish" a component. This copies the component into the user's app and the user now owns it

Not sure if the word 'publish" comes from this comment of something you guys already played with, but i ver y strongly advocates we use words like install / publish / update / for anything but what they are generally used for.

ux:kot:publish would publish on the website, on packagist ok.... to make it ... public :)


AS i echooed a small discussion we had around Vienna: if you dont' want to lose time on it, no problem let's change the name and be done 😆

But these thing are not something we can (and should) so let's deal with problems before we make them :)

@Kocal Kocal marked this pull request as draft March 28, 2025 07:35
@1ed
Copy link
Contributor

1ed commented Mar 29, 2025

Maybe interesting here https://x.com/shadcn/status/1905688710894481498?s=46

@Kocal
Copy link
Member

Kocal commented Mar 29, 2025

Maybe interesting here x.com/shadcn/status/1905688710894481498?s=46

flat JSON file

Funny, two days ago I started rewriting our registry system for a “flat” version, what a coincidence :D

About tokens, CSS vars, Tailwind layers (...), IMO it is too early to think about it. Let's keep focusing on UI components and later introduce "snippets" (like what @kbond and @weaverryan brainstormed)

@weaverryan
Copy link
Member

💯 one step at a time so that we can actually take those steps

@Kocal
Copy link
Member

Kocal commented Mar 30, 2025

Quick updates on what I've commited these 3 last days, but not pushed yet because I didn't finish:

  • ✅ The documentation docs/index.rst was improved, it's still missing the "Create your own theme (kit)" section
  • ✅ The component Grid and its children have been removed
  • ✅ The UX Toolkit can now be used with symfony/*:^6.4 packages
  • ✅ The UXToolkitBundle name was fixed (s/Ux/UX/), and I have merged Dependency/* to the UXToolkitBundle (by extending the AbstractBundle, like we did for UX Map)
  • ✅ The theme default has been renamed to shadcn
  • ✅ The directory templates has been renamed to themes
  • ✅ The initial system of Registry, ComponentRepository, etc... was entirely reworked:
    • No more registry/ compiled folder, just a small manifest.json containing 3/4 metadata, and Twig files
    • No more RegistryItem, now distinct DTOs like Component and Example Doc have been introduced, as well with Dependency (PhpPackageDependency and ComponentDependency) and File (only twig for now, but it can be extended in the future, ex. for Stimulus Controllers "recipes" cc @kbond 🙂)
.
└── kits/
    └── shadcn/
	    └── templates/
	    │   ├── components/
	    │   │   ├── Table/
	    │   │   │   └── Row.html.twig
	    │   │   └── Table.html.twig
        ├── docs/    
        │   └── components/
        │       ├── Alert.twig
        │       └── Table.twig
        └── manifest.json
  • ✅ The examples were reworked too, it has been moved from components/examples to docs/components, and now contains one "story" file (similar to Storybook) per components, it is inspired by @kbond's POC Example:
{% block meta %}
title: Avatar
description: The Avatar component displays a user's profile picture or a fallback representation when no image is available.
{% endblock %}

{% block examples %}
## Avatar with Image

\```twig
<twig:Avatar>
    <twig:Avatar:Image src="https://github.com/symfony.png" alt="@symfony" />
</twig:Avatar>
\```

## Avatar with Fallback

\```twig
<twig:Avatar>
    <twig:Avatar:Fallback>JF</twig:Avatar:Fallback>
</twig:Avatar>
\```
{% endblock %} 
  • ✅ Added composer bin ux-toolkit-theme-create, ux-toolkit-theme-lint and ux-toolkit-theme-debug, so it can be used by users working on their own kit
  • ✅ The component installation command was reworked too
  • ✅ I still need to adapt existing tests and add more for new files and behaviors
  • ⏳ I still need to adapt the ux.symfony.com website, due to all previous changes

Also, after some discussions with Simon this afternoon:

  • ✅ The term theme will change to kit. It makes really more sense, the theme term suggest that user can switch from a theme to another, like a theme in your code editor, which is not the behavior we want
  • Introduce Component::isChild() or something equivalent, in order to describe that Table:Row is not same kind of component than "high-level" component like Table or Button Not needed in fact, checking on $component->doc will be enough for ux.symfony.com
  • ✅ Hide commands ux:toolkit:debug-theme and ux:toolkit:lint-theme in Symfony apps
  • ✅ In introduced Value objects, add more check about data (e.g. a theme name, a component name, ...)

@Kocal Kocal force-pushed the ux_toolkit branch 3 times, most recently from b065d90 to 295ad70 Compare April 1, 2025 07:05
@weaverryan
Copy link
Member

Great stuff @Kocal! When it's ready for a review, please let me know!

@Kocal
Copy link
Member

Kocal commented Apr 2, 2025

@weaverryan sure!
I still have the UX Toolkit integration on ux.symfony.com to adapt and rework (find a clean solution to render those components without conflicting with existing ones), and also rework the PR description (should not be very long).

If you still want to review right now, please ignore the ux.symfony.com directory 🙏🏻
Here some info:

  • everything described from [Toolkit] Introduce the UX Toolkit ✨ #2539 (comment)
  • The Registry is either a LocalRegistry (format: shadcn) or GitHubRegistry (format: github.com/User/Repo@Kit)
  • A Kit is a manifest.json with a bunch of Component and documentation files
  • A Component consist of a name, a list of File, an optional Doc object (with a title, description, and a list of tags), and a list of Dependency
  • A Dependency can be a PhpPackageDependency (with a name and a version), or a ComponentDependency (with a name)
  • To initialize a Kit from a kit name, you must use the RegistryFactory::getForKit() method, which will return the right Registry instance, and then call Registry::getKit() with the kit name.
  • We provide 3 composer bin commands, for people who want to develop their own kits (in their own GitHub repository):
    • php bin/console ux-toolkit-kit-create to create a kit
    • php bin/console ux-toolkit-kit-debug to debug a kit
    • php bin/console ux-toolkit-kit-lint to lint a kit
  • We provide 2 Symfony commands, available in Symfony apps:
    • ux:toolkit:install-kit to install a whole kit (and its dependencies) in the current Symfony app. I'm not sure about keeping this command
    • ux:toolkit:install-component to install a component (and its dependencies) in the current Symfony app
  • The install command is smart enough to suggest you PHP dependencies to install, if not already installed, when you install a kit or a component.
  • The install command is also smart enough to not install a component twice if it's already installed (it asks you to confirm the action), it also suggest similar components name if you made a typo.

Typically, this is what I should explain in the PR description

@kbond
Copy link
Member

kbond commented Apr 2, 2025

A Dependency can be a PhpPackageDependency (with a name and a version), or a ComponentDependency (with a name)

Nice, so we can easily add a JsPackageDependency later

@Kocal
Copy link
Member

Kocal commented Apr 2, 2025

Exactly!

@Kocal Kocal force-pushed the ux_toolkit branch 2 times, most recently from b0504a8 to c7e6644 Compare April 6, 2025 18:42
@Kocal
Copy link
Member

Kocal commented Apr 6, 2025

A few updates:

  • ✅ The examples system has been reworked and looks more and more like Stories from Storybook, e.g. examples/components/Alert.md:
# Alert

The Alert component is used to display important messages or notifications to users, with support for different styles and variants.

## Examples

### Basic Alert

\```twig
<twig:Alert>
    This is an alert message.
</twig:Alert>
\```

### Alert with Custom Class

\```twig
<twig:Alert class="bg-red-100 text-red-800">
    This is a custom styled alert.
</twig:Alert>
\``` 
  • ⏳ The website is now able to render the examples, and convert "```twig" to highlighted code block + preview (with an iframe for styles isolation, secured by a signature) (that's still ugly, but step by step :D) :
Enregistrement.de.l.ecran.2025-04-06.a.20.38.32.mov

Next! Implement more and more components from Shadcn and enhance examples.

@kbond
Copy link
Member

kbond commented Apr 7, 2025

The website is now able to render the examples, and convert "```twig" to highlighted code block + preview (with an iframe for styles isolation, secured by a signature)

Super clever, I love it!

…itecture, and value-objects

- The registry system and JSON serialization were fully reworked, no more useless compilation to JSON
- Rename "default" theme to "shadcn"
- The "theme" term is now known as "kit"
useless JSON serialization
- Abuse ValueObjects usage for File, Example, Dependency, ...
 - Add binary for vendors
// Decrease headings level (e.g.: "# Alert" to "## Alert", "## Examples" to "### Examples", etc...)
$markdownContent = $markdownContent->replaceMatches('/^(#{1,5})\s+(.+)$/m', '#$1 $2');

// Wrap each "```twig" code block with a HTML tab system containing the code and the component preview
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible there could be twig code that we don't want converted to this tab system?

Maybe this should be twig+example?

Copy link
Member

@Kocal Kocal Apr 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! I improved the component's doc rendering these two last days and it's already supported with "```twig {"preview":true}" (JSON is more easier to parse), but I didn't push yet :)

It will be especially useful for an "Usage" section where you only want to display the Twig code without preview (done in "Examples" section)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also: I wonder if some of this code could be simpler as a common mark extension?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this is exactly what I did :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature New Feature Status: Needs Review Needs to be reviewed
Projects
None yet
Development

Successfully merging this pull request may close these issues.