Skip to content

Commit 7c4f5f7

Browse files
committed
feature #2539 [Toolkit] Introduce the UX Toolkit ✨ (Halleck45, Kocal)
This PR was merged into the 2.x branch. Discussion ---------- [Toolkit] Introduce the UX Toolkit ✨ | 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-component 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-component Button --registry github.com/MyRemote/UiKit` The website [https://ux.symfony.com/](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](https://ui.shadcn.com/) 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. **EDIT 14th April**: Many many things were done in the PR and it can be hard to follow all the work here. Please see the following links (time asc) to see precisely what's been done: - #2539 (comment) - #2539 (comment) - #2539 (comment) - #2539 (comment) ---- ![127 0 0 1_8000_toolkit (1)](https://github.com/user-attachments/assets/018858b6-3f48-4fb5-bbb2-c643802db963) https://github.com/user-attachments/assets/605cccb5-32e5-4f2b-b251-00b318435881 Commits ------- ebbf193 [Toolkit] Improve InstallComponentCommand by asking/guessing which Kit to use, remove ux_toolkit.kit parameter, remove DependencyInjection configuration cf9496e [Toolkit] Do not use FileSystem::readFile(), as it has been implemented in Symfony 7.1 and we want to support at least Symfony 6.4 98b6005 [Toolkit] Update version handling in dependencies to use string format, improve command output messages, improve InstallComponentCommand (interactive ask), remove LintKitCommand d3b3fc0 [Toolkit] Add StimulusController support, extract logic to KitSynchronizer, rewrite installation system, create dedicated Installer/Pool system, make classes not readonly (PHP 8.1 comptability) dbbf94f [Toolkit] Refactor `ToolkitKit` enum to `ToolkitKitId`, leverage description/uxIcon/installation steps in `Kit` VO 43e95a3 [Toolkit] Remove tales-from-a-dev/twig-tailwind-extra dependency, to allow PHP 8.1 minimal support b907659 [Toolkit] Rework the documentation 38d0b04 [Toolkit] Rework all components for Shadcn kit, add more, remove some, add docs too 982a21e [Toolkit] Big refactoring about the registry & kits system, kits architecture, and value-objects 0536dd4 [Toolkit] Fix UXToolkitBundle name, refactor DependencyInjection/* in UXToolkitBundle, 474d59e [Toolkit] Open dependencies constraints 7abe7d9 [Toolkit] Remove "Grid" component 51d7883 [Toolkit] Fine-tune the documentation 744b328 [Toolkit] Updated documentation in order to preview components f5591ff [Toolkit] Adds first components 6f53d2b [Toolkit] Adds engine for parsing, downloading and manage components
2 parents 182f01f + ebbf193 commit 7c4f5f7

File tree

197 files changed

+7582
-192
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

197 files changed

+7582
-192
lines changed
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Toolkit Kits
2+
3+
on:
4+
push:
5+
paths:
6+
- 'src/Toolkit/kits/**'
7+
pull_request:
8+
paths:
9+
- 'src/Toolkit/kits/**'
10+
11+
jobs:
12+
kits-cs:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- uses: shivammathur/setup-php@v2
18+
with:
19+
php-version: 8.3
20+
21+
- name: Install composer packages
22+
uses: ramsey/composer-install@v3
23+
with:
24+
working-directory: src/Toolkit
25+
26+
- name: Check kits code style
27+
run: php vendor/bin/twig-cs-fixer check kits
28+
working-directory: src/Toolkit

src/Toolkit/.gitattributes

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/.git* export-ignore
2+
/.symfony.bundle.yaml export-ignore
3+
/phpunit.xml.dist export-ignore
4+
/doc export-ignore
5+
/tests export-ignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Please do not submit any Pull Requests here. They will be closed.
2+
---
3+
4+
Please submit your PR here instead:
5+
https://github.com/symfony/ux
6+
7+
This repository is what we call a "subtree split": a read-only subset of that main repository.
8+
We're looking forward to your PR there!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Close Pull Request
2+
3+
on:
4+
pull_request_target:
5+
types: [opened]
6+
7+
jobs:
8+
run:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: superbrothers/close-pull-request@v3
12+
with:
13+
comment: |
14+
Thanks for your Pull Request! We love contributions.
15+
16+
However, you should instead open your PR on the main repository:
17+
https://github.com/symfony/ux
18+
19+
This repository is what we call a "subtree split": a read-only subset of that main repository.
20+
We're looking forward to your PR there!

src/Toolkit/.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
vendor
2+
composer.lock
3+
.phpunit.result.cache
4+
var
5+
.twig-cs-fixer.cache
6+
tests/ui/output
7+
tests/ui/screens

src/Toolkit/.symfony.bundle.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
branches: ["2.x"]
2+
maintained_branches: ["2.x"]
3+
doc_dir: "doc"

src/Toolkit/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# CHANGELOG
2+
3+
## 2.25
4+
5+
- Package added

src/Toolkit/LICENSE

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2025-present Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

src/Toolkit/README.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Symfony UX Toolkit
2+
3+
**EXPERIMENTAL** This component is currently experimental and is
4+
likely to change, or even change drastically.
5+
6+
Symfony UX Toolkit provides a set of ready-to-use UI components for Symfony applications.
7+
8+
**This repository is a READ-ONLY sub-tree split**. See
9+
https://github.com/symfony/ux to create issues or submit pull requests.
10+
11+
## Resources
12+
13+
- [Documentation](https://symfony.com/bundles/ux-toolkit/current/index.html)
14+
- [Report issues](https://github.com/symfony/ux/issues) and
15+
[send Pull Requests](https://github.com/symfony/ux/pulls)
16+
in the [main Symfony UX repository](https://github.com/symfony/ux)

src/Toolkit/bin/ux-toolkit-kit-create

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
/*
5+
* This file is part of the Symfony package.
6+
*
7+
* (c) Fabien Potencier <[email protected]>
8+
*
9+
* For the full copyright and license information, please view the LICENSE
10+
* file that was distributed with this source code.
11+
*/
12+
13+
if ('cli' !== \PHP_SAPI) {
14+
throw new Exception('This script must be run from the command line.');
15+
}
16+
17+
use Symfony\Component\Console\Application;
18+
use Symfony\Component\Filesystem\Filesystem;
19+
use Symfony\UX\Toolkit\Command\CreateKitCommand;
20+
21+
function includeIfExists(string $file): bool
22+
{
23+
return file_exists($file) && include $file;
24+
}
25+
26+
if (
27+
!includeIfExists(__DIR__ . '/../../../autoload.php') &&
28+
!includeIfExists(__DIR__ . '/../vendor/autoload.php')
29+
) {
30+
fwrite(STDERR, 'Install dependencies using Composer.'.PHP_EOL);
31+
exit(1);
32+
}
33+
34+
if (!class_exists(Application::class)) {
35+
fwrite(STDERR, 'You need the "symfony/console" component in order to run the UX Toolkit kit linter.'.PHP_EOL);
36+
exit(1);
37+
}
38+
39+
$filesystem = new Filesystem();
40+
41+
(new Application())->add($command = new CreateKitCommand($filesystem))
42+
->getApplication()
43+
->setDefaultCommand($command->getName(), true)
44+
->run()
45+
;

src/Toolkit/bin/ux-toolkit-kit-debug

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
/*
5+
* This file is part of the Symfony package.
6+
*
7+
* (c) Fabien Potencier <[email protected]>
8+
*
9+
* For the full copyright and license information, please view the LICENSE
10+
* file that was distributed with this source code.
11+
*/
12+
13+
if ('cli' !== \PHP_SAPI) {
14+
throw new Exception('This script must be run from the command line.');
15+
}
16+
17+
use Symfony\Component\Console\Application;
18+
use Symfony\Component\Filesystem\Filesystem;
19+
use Symfony\Component\HttpClient\HttpClient;
20+
use Symfony\Contracts\Service\ServiceLocatorTrait;
21+
use Symfony\Contracts\Service\ServiceProviderInterface;
22+
use Symfony\UX\Toolkit\Command\DebugKitCommand;
23+
use Symfony\UX\Toolkit\Kit\KitSynchronizer;
24+
use Symfony\UX\Toolkit\Kit\KitFactory;
25+
use Symfony\UX\Toolkit\Registry\GitHubRegistry;
26+
use Symfony\UX\Toolkit\Registry\LocalRegistry;
27+
use Symfony\UX\Toolkit\Registry\RegistryFactory;
28+
use Symfony\UX\Toolkit\Registry\Type;
29+
30+
function includeIfExists(string $file): bool
31+
{
32+
return file_exists($file) && include $file;
33+
}
34+
35+
if (
36+
!includeIfExists(__DIR__ . '/../../../autoload.php') &&
37+
!includeIfExists(__DIR__ . '/../vendor/autoload.php')
38+
) {
39+
fwrite(STDERR, 'Install dependencies using Composer.'.PHP_EOL);
40+
exit(1);
41+
}
42+
43+
if (!class_exists(Application::class)) {
44+
fwrite(STDERR, 'You need the "symfony/console" component in order to run the UX Toolkit kit linter.'.PHP_EOL);
45+
exit(1);
46+
}
47+
48+
$filesystem = new Filesystem();
49+
$kitFactory = new KitFactory($filesystem, new KitSynchronizer($filesystem));
50+
51+
(new Application())->add($command = new DebugKitCommand($kitFactory))
52+
->getApplication()
53+
->setDefaultCommand($command->getName(), true)
54+
->run()
55+
;

src/Toolkit/composer.json

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{
2+
"name": "symfony/ux-toolkit",
3+
"type": "symfony-bundle",
4+
"description": "A tool to easily create a design system in your Symfony app with customizable, well-crafted Twig components",
5+
"keywords": [
6+
"symfony-ux",
7+
"twig",
8+
"components"
9+
],
10+
"homepage": "https://symfony.com",
11+
"license": "MIT",
12+
"authors": [
13+
{
14+
"name": "Symfony Community",
15+
"homepage": "https://symfony.com/contributors"
16+
},
17+
{
18+
"name": "Hugo Alliaume",
19+
"email": "[email protected]"
20+
},
21+
{
22+
"name": "Jean-François Lépine",
23+
"email": "[email protected]"
24+
},
25+
{
26+
"name": "Simon André",
27+
"email": "[email protected]"
28+
}
29+
],
30+
"require": {
31+
"php": ">=8.1",
32+
"twig/twig": "^3.0",
33+
"symfony/console": "^6.4|^7.0",
34+
"symfony/filesystem": "^6.4|^7.0",
35+
"symfony/framework-bundle": "^6.4|^7.0",
36+
"symfony/twig-bundle": "^6.4|^7.0",
37+
"symfony/ux-twig-component": "^2.23",
38+
"symfony/yaml": "^6.4|^7.0"
39+
},
40+
"require-dev": {
41+
"symfony/finder": "6.4|^7.0",
42+
"twig/extra-bundle": "^3.19|^4.0",
43+
"twig/html-extra": "^3.19",
44+
"zenstruck/console-test": "^1.7",
45+
"symfony/http-client": "6.4|^7.0",
46+
"symfony/stopwatch": "^6.4|^7.0",
47+
"symfony/phpunit-bridge": "^6.4|^7.0",
48+
"vincentlanglet/twig-cs-fixer": "^3.5"
49+
},
50+
"bin": [
51+
"bin/ux-toolkit-kit-create",
52+
"bin/ux-toolkit-kit-debug"
53+
],
54+
"autoload": {
55+
"psr-4": {
56+
"Symfony\\UX\\Toolkit\\": "src"
57+
},
58+
"exclude-from-classmap": []
59+
},
60+
"autoload-dev": {
61+
"psr-4": {
62+
"Symfony\\UX\\Toolkit\\Tests\\": "tests/"
63+
}
64+
},
65+
"conflict": {
66+
"symfony/ux-twig-component": "<2.21"
67+
},
68+
"extra": {
69+
"thanks": {
70+
"name": "symfony/ux",
71+
"url": "https://github.com/symfony/ux"
72+
}
73+
}
74+
}

src/Toolkit/config/services.php

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
13+
14+
use Symfony\UX\Toolkit\Command\DebugKitCommand;
15+
use Symfony\UX\Toolkit\Command\InstallComponentCommand;
16+
use Symfony\UX\Toolkit\Kit\KitFactory;
17+
use Symfony\UX\Toolkit\Kit\KitSynchronizer;
18+
use Symfony\UX\Toolkit\Registry\GitHubRegistry;
19+
use Symfony\UX\Toolkit\Registry\LocalRegistry;
20+
use Symfony\UX\Toolkit\Registry\RegistryFactory;
21+
use Symfony\UX\Toolkit\Registry\Type;
22+
23+
/*
24+
* @author Hugo Alliaume <[email protected]>
25+
*/
26+
return static function (ContainerConfigurator $container): void {
27+
$container->services()
28+
// Commands
29+
30+
->set('.ux_toolkit.command.debug_kit', DebugKitCommand::class)
31+
->args([
32+
service('.ux_toolkit.kit.kit_factory'),
33+
])
34+
->tag('console.command')
35+
36+
->set('.ux_toolkit.command.install', InstallComponentCommand::class)
37+
->args([
38+
service('.ux_toolkit.registry.registry_factory'),
39+
service('filesystem'),
40+
])
41+
->tag('console.command')
42+
43+
// Registry
44+
45+
->set('.ux_toolkit.registry.registry_factory', RegistryFactory::class)
46+
->args([
47+
service_locator([
48+
Type::Local->value => service('.ux_toolkit.registry.local'),
49+
Type::GitHub->value => service('.ux_toolkit.registry.github'),
50+
]),
51+
])
52+
53+
->set('.ux_toolkit.registry.local', LocalRegistry::class)
54+
->args([
55+
service('.ux_toolkit.kit.kit_factory'),
56+
service('filesystem'),
57+
])
58+
59+
->set('.ux_toolkit.registry.github', GitHubRegistry::class)
60+
->args([
61+
service('.ux_toolkit.kit.kit_factory'),
62+
service('filesystem'),
63+
service('http_client')->nullOnInvalid(),
64+
])
65+
66+
// Kit
67+
68+
->set('.ux_toolkit.kit.kit_factory', KitFactory::class)
69+
->args([
70+
service('filesystem'),
71+
service('.ux_toolkit.kit.kit_synchronizer'),
72+
])
73+
74+
->set('.ux_toolkit.kit.kit_synchronizer', KitSynchronizer::class)
75+
->args([
76+
service('filesystem'),
77+
])
78+
;
79+
};

0 commit comments

Comments
 (0)