diff --git a/ux.symfony.com/assets/images/ux_packages/stimulus-1200x675.png b/ux.symfony.com/assets/images/ux_packages/stimulus-1200x675.png
new file mode 100644
index 00000000000..4d7f5a688b4
Binary files /dev/null and b/ux.symfony.com/assets/images/ux_packages/stimulus-1200x675.png differ
diff --git a/ux.symfony.com/assets/images/ux_packages/stimulus.svg b/ux.symfony.com/assets/images/ux_packages/stimulus.svg
new file mode 100644
index 00000000000..b951c16f759
--- /dev/null
+++ b/ux.symfony.com/assets/images/ux_packages/stimulus.svg
@@ -0,0 +1,5 @@
+
diff --git a/ux.symfony.com/assets/styles/app.scss b/ux.symfony.com/assets/styles/app.scss
index c38c96d208d..0b17c833edf 100644
--- a/ux.symfony.com/assets/styles/app.scss
+++ b/ux.symfony.com/assets/styles/app.scss
@@ -132,6 +132,7 @@ $utilities: map-remove(
@import "components/DemoContainer";
@import "components/DemoCard";
@import "components/DocsLink";
+@import "components/FeatureBox";
@import "components/FileTree";
@import "components/Icon";
@import "components/IconGrid";
diff --git a/ux.symfony.com/assets/styles/components/_FeatureBox.scss b/ux.symfony.com/assets/styles/components/_FeatureBox.scss
new file mode 100644
index 00000000000..82cf3b632aa
--- /dev/null
+++ b/ux.symfony.com/assets/styles/components/_FeatureBox.scss
@@ -0,0 +1,49 @@
+.FeatureBox {
+
+ --bg-color: var(--bs-body-bg);
+ [data-bs-theme="dark"] {
+ --bg-color: #1b1e21;
+ }
+
+ background: var(--bg-color);
+ border: 1px solid var(--bs-secondary-bg-subtle);
+ border-radius: var(--border-radius);
+ display: grid;
+ padding: 1rem;
+ place-content: center;
+ gap: .75rem;
+}
+
+.FeatureBox_icon {
+ height: 2.4rem;
+ display: grid;
+ place-content: center;
+ svg {
+ height: 100%;
+ width: 100%;
+ }
+ path, circle {
+ fill: var(--bs-body-bg);
+ fill-opacity: .1;
+ stroke: currentColor;
+ stroke-width: 1;
+ transition: 400ms;
+ }
+}
+.FeatureBox:hover {
+ path, circle {
+ fill-opacity: .15;
+ stroke-width: 1.25;
+ }
+}
+
+.FeatureBox_title {
+ opacity: .85;
+ font-weight: 400;
+ font-size: 1rem;
+ font-family: var(--font-family-text);
+ color: var(--bs-body-color);
+ text-decoration: none;
+ line-height: 1;
+ text-align: center;
+}
diff --git a/ux.symfony.com/assets/styles/sections/_hero.scss b/ux.symfony.com/assets/styles/sections/_hero.scss
index fbf30e7a289..30434b17bc8 100644
--- a/ux.symfony.com/assets/styles/sections/_hero.scss
+++ b/ux.symfony.com/assets/styles/sections/_hero.scss
@@ -7,9 +7,16 @@
}
.hero-sub-text {
- width: 40%;
+ width: 50%;
text-wrap: balance;
}
+.hero-sub-text a {
+ border-bottom: 1.5px solid #ffff;
+ text-decoration: none;
+}
+.hero-sub-text a:hover {
+ color: inherit;
+}
@media (max-width: 1114px) {
.hero-sub-text {
diff --git a/ux.symfony.com/config/packages/ux_icons.yaml b/ux.symfony.com/config/packages/ux_icons.yaml
index 61326164e87..43ecf5c476d 100644
--- a/ux.symfony.com/config/packages/ux_icons.yaml
+++ b/ux.symfony.com/config/packages/ux_icons.yaml
@@ -1,3 +1,10 @@
ux_icons:
default_icon_attributes:
class: 'Icon'
+
+ icon_sets:
+
+ # FeatureBox icons
+ feature:
+ alias: 'lucide'
+ # icon_attributes:
diff --git a/ux.symfony.com/src/Controller/UxPackage/StimulusController.php b/ux.symfony.com/src/Controller/UxPackage/StimulusController.php
new file mode 100644
index 00000000000..6719020f49c
--- /dev/null
+++ b/ux.symfony.com/src/Controller/UxPackage/StimulusController.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace App\Controller\UxPackage;
+
+use App\Service\UxPackageRepository;
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Attribute\Route;
+
+class StimulusController extends AbstractController
+{
+ public function __construct(
+ private readonly UxPackageRepository $packageRepository,
+ ) {
+ }
+
+ #[Route('/stimulus', name: 'app_stimulus')]
+ public function __invoke(): Response
+ {
+ $package = $this->packageRepository->find('stimulus');
+
+ return $this->render('ux_packages/stimulus.html.twig', [
+ 'package' => $package,
+ ]);
+ }
+}
diff --git a/ux.symfony.com/src/Model/UxPackage.php b/ux.symfony.com/src/Model/UxPackage.php
index 824983b45bd..bba81f52e5c 100644
--- a/ux.symfony.com/src/Model/UxPackage.php
+++ b/ux.symfony.com/src/Model/UxPackage.php
@@ -26,8 +26,9 @@ public function __construct(
private string $gradient,
private string $tagLine,
private string $description,
- private string $createString,
+ private ?string $createString = null,
private ?string $imageFileName = null,
+ private ?string $composerName = null,
) {
}
@@ -73,7 +74,7 @@ public function getDescription(): string
public function getComposerName(): string
{
- return 'symfony/ux-'.$this->getName();
+ return $this->composerName ?? 'symfony/ux-'.$this->getName();
}
public function getComposerRequireCommand(): string
@@ -117,12 +118,21 @@ public function getScreencastLinkText(): ?string
return $this->screencastLinkText;
}
+ public function setOfficialDocsUrl(string $officialDocsUrl): self
+ {
+ $this->officialDocsUrl = $officialDocsUrl;
+
+ return $this;
+ }
+
+ private string $officialDocsUrl;
+
public function getOfficialDocsUrl(): string
{
- return \sprintf('https://symfony.com/bundles/ux-%s/current/index.html', $this->name);
+ return $this->officialDocsUrl ??= \sprintf('https://symfony.com/bundles/ux-%s/current/index.html', $this->name);
}
- public function getCreateString(): string
+ public function getCreateString(): ?string
{
return $this->createString;
}
diff --git a/ux.symfony.com/src/Service/UxPackageRepository.php b/ux.symfony.com/src/Service/UxPackageRepository.php
index a0a2eb251a9..0810e0e8def 100644
--- a/ux.symfony.com/src/Service/UxPackageRepository.php
+++ b/ux.symfony.com/src/Service/UxPackageRepository.php
@@ -80,6 +80,21 @@ public function findAll(?string $query = null): array
->setDocsLink('https://turbo.hotwired.dev/handbook/introduction', 'Documentation specifically for the Turbo JavaScript library.')
->setScreencastLink('https://symfonycasts.com/screencast/turbo', 'Go deep into all 3 parts of Turbo.'),
+ (new UxPackage(
+ 'stimulus',
+ 'Stimulus',
+ 'app_stimulus',
+ '#2EB17B',
+ 'linear-gradient(to bottom right, #3D9A89 5%, #2EB17B 80%)',
+ 'Central Bridge of Symfony UX',
+ 'Integration with Stimulus for HTML-powered controllers',
+ null,
+ 'stimulus.svg',
+ 'symfony/stimulus-bundle',
+ ))
+ ->setOfficialDocsUrl('https://symfony.com/bundles/StimulusBundle')
+ ->setScreencastLink('https://symfonycasts.com/screencast/stimulus', 'More than 40 videos to master Stimulus.'),
+
new UxPackage(
'autocomplete',
'Autocomplete',
@@ -100,7 +115,7 @@ public function findAll(?string $query = null): array
'Symfony Translations in JavaScript',
"Use Symfony's translations in JavaScript",
'I need to translate strings in JavaScript',
- 'translator.svg'
+ 'translator.svg',
),
(new UxPackage(
@@ -135,7 +150,7 @@ public function findAll(?string $query = null): array
'linear-gradient(95deg, #35B67C -5%, #8CE3BC 105%)',
'Render Vue components from Twig',
'Quickly render `
{{ eyebrowText|raw }}
+{{ eyebrowText|raw }}
-- {% block sub_content %}{% endblock %} + {% block sub_content %}{{ subtitle|default }}{% endblock %}
stimulus_controller
stimulus_target
stimulus_action