承接 n5s/dtcg-tokens 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

n5s/dtcg-tokens

Composer 安装命令:

composer require n5s/dtcg-tokens

包简介

PHP-first runtime library for DTCG design tokens, with Twig and Symfony bridges

README 文档

README

Read, resolve, and render DTCG design tokens at runtime in PHP. It implements the Design Tokens Community Group specification and is PHP-first, with optional Twig and Symfony bridges.

QA codecov PHP ^8.4 License MIT

Install

composer require n5s/dtcg-tokens

Requires PHP 8.4 or newer.

Quick start (plain PHP)

use n5s\DtcgTokens\Tokens;

$tokens = Tokens::fromFile('tokens.json');

$tokens->get('color.primary');         // ColorValue (Stringable)
(string) $tokens->get('space.md');     // "16px"
$tokens->get('color.primary', 'dark'); // mode-aware lookup

Tokens is an immutable, iterable (IteratorAggregate) and countable collection keyed by dot-separated token path. Build it from one or many files, or from an already-decoded array:

Tokens::fromFile('tokens.json');                       // single file
Tokens::fromFiles(['base.json', 'overrides.json']);    // merged, later files win
Tokens::fromArray(['color' => ['$type' => 'color', /* ... */]]);

Other methods: has(string $path): bool, all(): array<string, TokenValueInterface>, and get(string $path, ?string $mode = null). Unknown paths throw a TokenException. Per-token metadata is available via metadata(string $path): ?TokenMetadata and allMetadata() (carrying $description and $deprecated).

Supported token types

The parser handles the following DTCG $type values:

Type Value object Notes
color ColorValue hex + any CSS Color 4 space, stored losslessly
dimension DimensionValue { value, unit }, unit px / rem / em
duration DimensionValue { value, unit }, unit ms / s
number NumberValue
fontFamily FontFamilyValue string or list of strings
fontWeight NumberValue keyword (bold, medium, …) mapped to numeric
cubicBezier CubicBezierValue array of 4 numbers
boolean BooleanValue
string StringValue
link LinkValue
strokeStyle StrokeStyleValue keyword or { dashArray, lineCap }
border BorderValue composite: color + width + style
shadow ShadowValue single or multi-layer, inset supported
gradient GradientValue list of { color, position } stops
transition TransitionValue duration + delay + timing function
typography TypographyValue composite font shorthand; extra props preserved

Color spaces

ColorValue accepts hex strings (#rrggbb and #rrggbbaa) and DTCG color objects. Colors are stored losslessly in their authored color space — the components (with null representing a CSS none / powerless channel) and alpha are kept verbatim, never squashed to sRGB at parse time.

The accepted color spaces match the CSS Color 4 / terrazzo set: srgb, srgb-linear, display-p3, a98-rgb, prophoto-rgb, rec2020, lab, lab-d65, lch, oklab, oklch, okhsv, hsl, hwb, xyz, xyz-d50, xyz-d65. An unknown space throws.

fontWeight keywords (thin, light, regular, medium, semi-bold, bold, black, …) are mapped to their numeric equivalents; an unknown keyword, or a numeric weight outside 11000, throws a TokenException.

CSS serialization (toCss() / (string))

Casting to string emits faithful CSS Color 4 — no gamut conversion:

Color space(s) Output
srgb, hex rgb(R G B) / rgb(R G B / A)
hsl hsl(H S% L%)
hwb hwb(H W% B%)
lab, lab-d65 lab(L a b)
lch lch(L C H)
oklab oklab(L a b)
oklch oklch(L C H)
display-p3, a98-rgb, prophoto-rgb, rec2020, srgb-linear, xyz, xyz-d50, xyz-d65 color(<space> c1 c2 c3)
okhsv hex fallback (no CSS function)

A null component renders as none; alpha below 1 is appended as / A inside the function. okhsv has no CSS function, so toCss() emits the author-provided hex fallback if present, otherwise it throws.

Conversion to sRGB (toHex() / toRgb())

These reduce a color to 8-bit sRGB:

  • srgb, hsl, oklch (and hex): computed directly.
  • Any other space: uses the author-provided hex fallback if present, else throws — wide-gamut values are never silently approximated.

For example, display-p3 [1 0 0] serializes to color(display-p3 1 0 0); toHex() throws unless the token also carries a "hex" fallback (e.g. "#fd0000"), in which case that value is returned verbatim.

Value objects

Every token resolves to an immutable value object implementing TokenValueInterface extends \Stringable. Casting to string produces a CSS-ready representation, so value objects can be dropped straight into templates or stylesheets.

Color tokens additionally expose toHex() and toRgb(?float $alpha = null):

$color = $tokens->get('color.primary');

(string) $color;        // "rgb(255 0 0)"  — default string form is rgb()
$color->toHex();        // "#ff0000"
$color->toRgb(0.5);     // "rgb(255 0 0 / 0.5)"

Theming / modes

Mode-specific values are declared under $extensions.mode.<name>:

{
  "color": {
    "$type": "color",
    "fg": {
      "$value": "#ffffff",
      "$extensions": { "mode": { "dark": "#000000" } }
    }
  }
}

Resolve a mode by passing it to get(), or get a mode-bound value object via forMode():

$tokens->get('color.fg');          // base   -> rgb(255 255 255)
$tokens->get('color.fg', 'dark');  // dark   -> rgb(0 0 0)

$tokens->get('color.fg')->forMode('dark');   // same as above
$tokens->get('color.fg')->forMode('nope');   // falls back to the base value

An unknown mode falls back to the base value rather than throwing.

Modes work for every standard DTCG token type — color, dimension, number, fontFamily, fontWeight, duration, cubicBezier, strokeStyle, border, transition, shadow, gradient, and typography. (The non-spec boolean, string, and link extras are not mode-aware and always return their base value.)

Aliases and modes

Aliases are resolved per mode: an alias resolves to its target's value in the same mode, falling back to the target's base when the target does not declare that mode. A token that aliases a themed token but declares no modes of its own hoists the target's modes:

{
  "color": {
    "$type": "color",
    "blue":   { "$value": "#0000ff", "$extensions": { "mode": { "dark": "#000088" } } },
    "accent": { "$value": "{color.blue}" }
  }
}
$tokens->get('color.accent');         // -> rgb(0 0 255)   (blue, base)
$tokens->get('color.accent', 'dark'); // -> rgb(0 0 136)   (blue, dark) — hoisted through the alias

Hoisting also works through composites (e.g. a border whose color aliases a themed color becomes mode-aware on that channel) and transitively along alias chains. When a token declares its own modes, those take precedence and no extra modes are hoisted. This mirrors Terrazzo's mode resolution.

Twig

The TokenExtension exposes a token() function plus hex and rgb filters.

In a Symfony app it is registered automatically (see Symfony). For plain Twig, wire it as an attribute extension with a runtime loader:

use n5s\DtcgTokens\Tokens;
use n5s\DtcgTokens\Twig\TokenExtension;
use Twig\Environment;
use Twig\Extension\AttributeExtension;
use Twig\RuntimeLoader\FactoryRuntimeLoader;

$tokens = Tokens::fromFile('tokens.json');

$twig = new Environment($loader);
$twig->addExtension(new AttributeExtension(TokenExtension::class));
$twig->addRuntimeLoader(new FactoryRuntimeLoader([
    TokenExtension::class => static fn (): TokenExtension => new TokenExtension($tokens),
]));

Then in templates:

{{ token('color.primary')|hex }}      {# #ff0000 #}
{{ token('color.primary')|rgb }}      {# rgb(255 0 0) #}
{{ token('color.primary')|rgb(0.5) }} {# rgb(255 0 0 / 0.5) #}
{{ token('space.md') }}               {# 16px — Stringable, no filter needed #}
{{ token('color.fg', 'dark')|hex }}   {# mode-aware lookup #}

The hex and rgb filters only accept color tokens and throw a LogicException otherwise.

Symfony

Enable the bundle:

// config/bundles.php
return [
    // ...
    n5s\DtcgTokens\Bridge\Symfony\DtcgTokensBundle::class => ['all' => true],
];

Configure it under the dtcg_tokens key:

# config/packages/dtcg_tokens.yaml
dtcg_tokens:
    files:
        - '%kernel.project_dir%/assets/tokens/tokens.json'
    cache: cache.app   # optional PSR-6 pool service id

files is required (at least one entry); multiple files are merged in order. cache is optional — point it at any PSR-6 cache pool service to cache the parsed token tree.

Then inject n5s\DtcgTokens\Tokens into any service or controller:

use n5s\DtcgTokens\Tokens;

final class ThemeController
{
    public function __construct(private Tokens $tokens) {}
}

The Twig token() function and hex / rgb filters are registered automatically when Twig is installed.

CSS export

CssExporter renders a Tokens collection as CSS custom properties:

use n5s\DtcgTokens\Export\CssExporter;

echo new CssExporter()->export($tokens);
:root {
  --color-primary: rgb(255 0 0);
  --space-md: 16px;
}

The constructor takes an optional prefix and selector:

new CssExporter(prefix: 'ds', selector: '.theme-dark')->export($tokens);
// .theme-dark { --ds-color-primary: rgb(0 0 0); ... }

Token paths are slugged by replacing . and spaces with -; values use each value object's string form.

Caching

CachedTokenFactory wraps a loader and an optional PSR-6 pool. It parses tokens once, caches the result, and — in debug mode — invalidates the cache when any source file's mtime changes:

use n5s\DtcgTokens\Cache\CachedTokenFactory;
use n5s\DtcgTokens\Loader\JsonFileLoader;

$factory = new CachedTokenFactory(
    loader: JsonFileLoader::fromPaths(['tokens.json']),
    cache: $psr6Pool,   // any Psr\Cache\CacheItemPoolInterface, or null
    debug: $isDebug,
);

$tokens = $factory->create();

Without a cache pool it simply parses on first create() and reuses the result in-process. The Symfony bundle wires this factory for you.

Development

composer qa   # PHPStan (max), ECS, Rector, PHPUnit

Built and tested against PHP 8.4. PHPStan runs at max level with strict rules; ECS and Rector enforce style and modernization.

Limitations

This is a runtime library. It deliberately leaves some things out:

Not included Notes
Build-time codegen / plugins Runtime only — for a full token toolchain with codegen and plugins, see Terrazzo
Resolver-document format
Alias-graph metadata Aliases are resolved in place (including per mode); the reference graph itself is not exposed as data
Gamut-conversion math Colors serialize faithfully to CSS Color 4; reducing a wide-gamut space to sRGB (toHex() / toRgb()) needs an sRGB-reducible space (srgb, hsl, oklch) or an author-provided hex fallback

Credits

Feature scope was informed by Terrazzo, the state-of-the-art JavaScript reference for DTCG tooling.

License

MIT.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 2
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-16

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固