Skip to content

Editors and static analysis

Edit this page

The type of the css() hash

In debug mode, the bundle writes config/reference_css.php: the PHPDoc types of the hash that css() takes, built from your tokens, conditions and breakpoints. It is rewritten only when its content changes, like config/reference.php. Commit it, so the types are there on every machine.

The file declares a CssStyles array shape on the Symfony\UX\Css\CssReference class, and the values of your tokens in its TOKENS constant:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// config/reference_css.php
namespace Symfony\UX\Css;

/**
 * @psalm-type ColorsToken = 'blue.500'|'primary'|string
 * @psalm-type CssStyles = array{
 *     color?: ColorsToken|array<ColorsToken|array<array-key, mixed>>,
 *     p?: SpacingToken|array<SpacingToken|array<array-key, mixed>>,
 *     _hover?: CssStylesNested,
 *     md?: CssStylesNested,
 *     ...
 * }
 * @psalm-type CssStylesNested = array{
 *     color?: ColorsToken|array<ColorsToken|array<array-key, mixed>>,
 *     p?: SpacingToken|array<SpacingToken|array<array-key, mixed>>,
 *     _hover?: array<string, mixed>,
 *     md?: array<string, mixed>,
 *     ...
 * }
 */
final class CssReference
{
    public const TOKENS = [
        'colors' => [
            'blue.500' => '#3b82f6',
            'primary' => '#3b82f6',
        ],
        'spacing' => [
            'md' => '1rem',
            '-md' => '-1rem',
        ],
    ];
}

Each property lists the tokens or keywords it accepts, and each condition and breakpoint is a key. The hash under a condition or a breakpoint is typed one level deep, with CssStylesNested. Below that it is not typed: PHPStan refuses a type that refers to itself, and each nested level slows its analysis down.

string stays in every union. Values written in a template are checked when Twig compiles it, and values only known at runtime are checked in debug mode: see Catching mistakes.

TOKENS maps each token to its value, by category, so that editors can show the value of a token: the color of a color token, the length of a spacing token. A semantic token gets the final value of the token it refers to: primary refers to {colors.blue.500}, so it gets #3b82f6. A conditional token gets its base value. A negative token gets the negative value: -md gets -1rem.

In your editor

The css() function declares its parameter with CssStyles. Editors that read PHPDoc array shapes can use it:

  • PhpStorm completes the keys of an array shape in PHP code;
  • in Twig templates, the Symfony plugin for PhpStorm completes the keys and values of a css() hash since version 2026.2.314;
  • Symfony Language Tools does the same since version 0.24.0, when it can run its dynamic analysis of the project.

Static analysis

A css() call written in a template is compiled into a string of classes, so PHPStan, or TwigStan on compiled templates, has nothing left to check there: Twig itself reports the mistakes, in lint:twig for example. Values only known at runtime are checked by css() in debug mode. See Catching mistakes.

PHP code that calls the css() runtime directly is the one place where PHPStan uses the types. Since config/ is not autoloaded, add the file to its scanFiles:

1
2
3
4
# phpstan.neon
parameters:
    scanFiles:
        - config/reference_css.php
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version