When a W3C design tokens file gives a colour as components, the import writes the wrong colour in two cases. An srgb-linear colour comes out too dark, and any alpha is dropped, so a translucent colour becomes opaque.
Where, at development fd992ea:
lib/Service/DesignTokensMapper.php:57 accepts both srgb and srgb-linear as colour spaces.
lib/Service/DesignTokensMapper.php:611-633 serializeColorObject() sends both spaces to the same converter when there is no hex.
lib/Service/DesignTokensMapper.php:644-652 componentsToHex() scales each component by 255 without the sRGB transfer function, and writes three channels only.
|
$colorSpace = strtolower((string)($value['colorSpace'] ?? '')); |
|
if (in_array($colorSpace, self::SRGB_COLOR_SPACES, true) === false) { |
|
return ['ok' => false, 'reason' => 'unsupported-color-space', 'detail' => ($value['colorSpace'] ?? 'unknown')]; |
|
} |
|
|
|
if (isset($value['hex']) === true && is_string($value['hex']) === true) { |
|
$hex = $value['hex']; |
|
if (str_starts_with($hex, '#') === false) { |
|
$hex = '#' . $hex; |
|
} |
|
|
|
return ['ok' => true, 'value' => $hex]; |
|
} |
|
|
|
if (isset($value['components']) === true |
|
&& is_array($value['components']) === true |
|
&& count($value['components']) >= 3 |
|
) { |
|
return ['ok' => true, 'value' => $this->componentsToHex(components: array_values($value['components']))]; |
|
} |
|
|
|
return ['ok' => false, 'reason' => 'unsupported-value-shape']; |
|
}//end serializeColorObject() |
|
|
|
/** |
|
* Serialize three 0–1 sRGB float components to a `#rrggbb` hex literal. |
|
* |
|
* @param array<int, mixed> $components The `[r, g, b]` components (0–1 range, clamped). |
|
* |
|
* @return string The `#rrggbb` hex literal. |
|
* |
|
* @spec openspec/specs/custom-token-sets/spec.md |
|
*/ |
|
private function componentsToHex(array $components): string { |
|
$hex = '#'; |
|
foreach ([0, 1, 2] as $index) { |
|
$channel = max(0.0, min(1.0, (float)$components[$index])); |
|
$hex .= str_pad(dechex((int)round($channel * 255)), 2, '0', STR_PAD_LEFT); |
|
} |
|
|
|
return $hex; |
|
}//end componentsToHex() |
Linear light values must go through the sRGB transfer function before scaling. Without it, a linear 0.5 grey becomes #808080 instead of about #bcbcbc. The colour object's alpha is never read. Neither case is reported, so the imported set looks correct and renders differently.
Specified fix: openspec/changes/authoring-dtcg-export (requirement "W3C Design Tokens JSON Import", task 2.2).
Found by the OpenSpec pass on 27 Sep 2026 and re-read at fd992ea on 28 Sep.
Live check: import a token file with {"colorSpace": "srgb-linear", "components": [0.5, 0.5, 0.5], "alpha": 0.5} and read the stored value #808080.
When a W3C design tokens file gives a colour as
components, the import writes the wrong colour in two cases. Ansrgb-linearcolour comes out too dark, and anyalphais dropped, so a translucent colour becomes opaque.Where, at development fd992ea:
lib/Service/DesignTokensMapper.php:57accepts bothsrgbandsrgb-linearas colour spaces.lib/Service/DesignTokensMapper.php:611-633serializeColorObject()sends both spaces to the same converter when there is nohex.lib/Service/DesignTokensMapper.php:644-652componentsToHex()scales each component by 255 without the sRGB transfer function, and writes three channels only.thematiq/lib/Service/DesignTokensMapper.php
Lines 611 to 652 in fd992ea
Linear light values must go through the sRGB transfer function before scaling. Without it, a linear 0.5 grey becomes #808080 instead of about #bcbcbc. The colour object's
alphais never read. Neither case is reported, so the imported set looks correct and renders differently.Specified fix:
openspec/changes/authoring-dtcg-export(requirement "W3C Design Tokens JSON Import", task 2.2).Found by the OpenSpec pass on 27 Sep 2026 and re-read at fd992ea on 28 Sep.
Live check: import a token file with
{"colorSpace": "srgb-linear", "components": [0.5, 0.5, 0.5], "alpha": 0.5}and read the stored value#808080.