Conversation
This was referenced Sep 27, 2026
Member
Author
|
Some updates:
|
| Q | A | -------------- | --- | Bug fix? | no | New feature? | yes | Deprecations? | no | Documentation? | yes | Issues | | License | MIT This PR adds a new package, `symfony/ux-css`. It gives Twig a `css()` function in the spirit of Panda CSS (https://panda-css.com/): you pass a hash of style properties and get back atomic class names built from design tokens. The hash is checked when Twig compiles the template, so a misspelled property or an unknown token shows up as a Twig syntax error pointing to the template line, not as a silent CSS bug. PHP writes one CSS file (`var/ux_css/styles.css`). There is no Node.js involved. AssetMapper serves the file directly, and Webpack Encore or Symfony Reprise can import it like any other stylesheet. Class names and generated CSS match what Panda CSS produces (a pinned version) for the supported syntax, checked against test cases recorded from Panda's own test suite. ```html+twig <div class="{{ css({ p: 'md', color: 'fg', _hover: { color: 'primary' }, md: { p: 'lg' } }) }}"> ``` This renders `class="p_md c_fg hover:c_primary md:p_lg"`. This is complementary to symfony#3932 (Simon's "[DesignTokens] Add the Design Tokens component"), not competing with it. symfony#3932 reads DTCG token files and exposes their values as CSS variables and PHP values. This PR is a styling API that consumes tokens. A later step could let `ux-css` read its tokens from `ux-design-tokens` instead of its own config. I open this as a draft so both approaches can be compared side by side. Most of the diff, about 64,000 of the 114,000 lines, is test fixtures recorded from Panda CSS: reviewers can skip `src/Css/tests/Fixtures/Panda/`. The package suite passes (5445 tests, 224 skipped because they cover Panda syntax we don't support), and PHP-CS-Fixer, Twig-CS-Fixer and DOCtor-RST all pass too.
UX CSS now reads every design token from symfony/ux-design-tokens (symfony#3932): DTCG token files, resolved when the container is built, with every Resolver context included. A token's name in css() comes from its path, with a fixed prefix per category, for example color.* for colors and dimension.spacing.* for spacing. Generated rules use UX Design Tokens' own CSS variables, like var(--dt-color-primary), so values, dark mode and per-request themes come from there. _dark reuses the same selectors as its stylesheet. The tokens, semantic_tokens, default_tokens and breakpoints options of ux_css are removed; breakpoints now come from breakpoint.* tokens. CI stays red until symfony#3932 is merged, since the package isn't in the repository yet.
Member
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds a new package,
symfony/ux-css. It gives Twig acss()function in the spirit of Panda CSS (https://panda-css.com/): you pass a hash of style properties and get back atomic class names built from design tokens. The hash is checked when Twig compiles the template, so a misspelled property or an unknown token shows up as a Twig syntax error pointing to the template line, not as a silent CSS bug.PHP writes one CSS file (
var/ux_css/styles.css). There is no Node.js involved. AssetMapper serves the file directly, and Webpack Encore or Symfony Reprise can import it like any other stylesheet. Class names and generated CSS match what Panda CSS produces (a pinned version) for the supported syntax, checked against test cases recorded from Panda's own test suite.This renders
class="p_md c_fg hover:c_primary md:p_lg".This PR complements #3932 (Simon's "[DesignTokens] Add the Design Tokens component") and does not compete with it. DesignTokens is where tokens are defined, CSS is a styling layer that uses them.
I wrote the first commit on September 24 and 25, before #3932 was opened, with its own token config in Panda's format. The second commit replaces that: CSS now reads every token from DesignTokens' DTCG files, and the generated rules use DesignTokens'
--dt-*CSS variables. That means this PR now depends on #3932, and CI stays red until it's merged. I'm keeping this as a draft so both approaches can be compared side by side.--dt-*), typed token objects in PHP, exports to CSS, JavaScript, DTCG, Tailwind and DESIGN.md--dt-*variables, no token variable declared herevar(--dt-...)in your own CSS, andux_token()where CSS variables cannot go (emails, PDF, SVG)class="{{ css({ ... }) }}"_hover,md,_dark, custom conditions):_darkreuses DesignTokens' dark selectors, breakpoints come frombreakpoint.*tokenslint:design-tokens; an unknown token path fails the rendercss()call, with suggestionsMost of the diff, about 64,000 of the 114,000 lines, is test fixtures recorded from Panda CSS: reviewers can skip
src/Css/tests/Fixtures/Panda/.Note
css({})auto-completion requires a modification on the Symfony Plugin for PHPStorm (EDIT: Haehnchen/idea-php-symfony2-plugin#2889), and Symfony Language Tools for VSCode (EDIT: symfony/language-tools#108)Current limitations
For the moment, it's not possible to generate a uber array-shaped PHPDoc type, because of PHPStan and PHPStorm limitations, given Opus 5.5.
It means it's not possible to have support for (
|represents the cursor) :{ base: '', ...states}as value, for examples:{ color: { |,color: { base : '|, and{ _hover: { |[blue.500/20], which equalsNB colors variants * 101(100because opacity can go from 0 to 100), which means making the array-shaped PHPDoc type much biger