CSS Gradient Generator — PHP source
Build linear, radial, and conic CSS gradients with multiple color stops and positions. Live preview and copy-ready CSS.
This is the PHP implementation — the same logic the interactive tool runs, in a shareable, citable form.
<?php
/**
* Pure CSS-gradient builder — PHP polyglot showcase port.
*
* Language: PHP (standard library only, no Composer dependencies)
* Origin: CosmoDev polyglot showcase — port of the css-gradient-generator tool
* Ported from: src/lib/cssGradient.ts (the canonical TypeScript implementation)
*
* Purpose: Build linear / radial / conic CSS gradient strings from a small
* config array. Deterministic and side-effect free — never throws.
*
* Display source — part of CosmoDev's polyglot tool pages.
*/
declare(strict_types=1);
/**
* Named CSS colors this tool accepts. The full CSS spec defines ~148 names,
* but we accept only the common, unambiguous set so output stays predictable.
*/
const NAMED_COLORS = [
'transparent', 'black', 'white', 'red', 'green', 'blue', 'yellow',
'orange', 'purple', 'pink', 'gray', 'grey', 'brown', 'cyan', 'magenta',
'none', 'currentcolor',
];
// Regexes mirror the TypeScript source exactly: 3- or 6-digit hex, 8-digit
// alpha hex, and the rgb()/rgba() / hsl()/hsla() functional forms.
const HEX_3_OR_6 = '/^#[0-9a-f]{3}([0-9a-f]{3})?$/';
const HEX_8 = '/^#[0-9a-f]{8}$/';
const RGB_FUNC = '/^rgba?\([^)]+\)$/';
const HSL_FUNC = '/^hsla?\([^)]+\)$/';
/**
* Validate a CSS color string.
*
* Returns ['ok' => bool, 'error' => string|null] — error is null when ok.
* Accepts named colors, #RGB / #RRGGBB / #RRGGBBAA hex, and rgb()/rgba()/
* hsl()/hsla() functional forms. The input is trimmed and lowercased first.
*/
function parseColor(string $color): array {
$c = strtolower(trim($color));
if ($c === '') {
return ['ok' => false, 'error' => 'empty color'];
}
if (in_array($c, NAMED_COLORS, true)) {
return ['ok' => true, 'error' => null];
}
if (preg_match(HEX_3_OR_6, $c) || preg_match(HEX_8, $c)) {
return ['ok' => true, 'error' => null];
}
if (preg_match(RGB_FUNC, $c) || preg_match(HSL_FUNC, $c)) {
return ['ok' => true, 'error' => null];
}
return ['ok' => false, 'error' => "invalid color: {$color}"];
}
/**
* Coerce a possibly-invalid color to a safe value. Invalid colors become solid
* black so the gradient always renders. Original casing is preserved — we only
* ever trim, mirroring the TypeScript source.
*/
function normalizeColor(string $color): string {
return parseColor($color)['ok'] ? trim($color) : '#000000';
}
/**
* Round a position the way JavaScript's Math.round does (half rounds up) and
* format as an integer string. PHP's default round() (PHP_ROUND_HALF_UP)
* agrees with Math.round for non-negative values — the gradient-position
* domain (0..100). Cast through int so whole floats render without ".0".
*/
function roundPosition(float $x): string {
return (string) (int) round($x);
}
/**
* Build a complete CSS gradient string from a config array:
* ['type' => 'linear'|'radial'|'conic', 'angle' => float,
* 'stops' => [['color' => string, 'position' => float], ...],
* 'radialShape' => 'circle'|'ellipse'|null]
*
* Stops are sorted ascending by position (stable on PHP 8.0+, matching modern
* JavaScript's stable Array.sort). Fewer than 2 stops collapse to a black →
* white default so the output is always renderable. `radialShape` uses PHP's
* `??`, which — like TS — only falls back on null/unset; an explicit empty
* string passes through.
*
* @param array{type: string, angle: int|float, stops: array, radialShape?: string|null} $config
*/
function buildGradient(array $config): string {
// Copy stops so we never mutate the caller's array, then stable-sort by
// position ascending.
$stops = array_values($config['stops'] ?? []);
usort($stops, static fn (array $a, array $b): int => $a['position'] <=> $b['position']);
if (count($stops) < 2) {
$stops = [
['color' => '#000000', 'position' => 0],
['color' => '#ffffff', 'position' => 100],
];
}
$parts = array_map(
static fn (array $s): string =>
normalizeColor($s['color']) . ' ' . roundPosition($s['position']) . '%',
$stops
);
$stopsStr = implode(', ', $parts);
switch ($config['type']) {
case 'linear':
return "linear-gradient({$config['angle']}deg, {$stopsStr})";
case 'radial':
$shape = $config['radialShape'] ?? 'circle';
return "radial-gradient({$shape}, {$stopsStr})";
case 'conic':
return "conic-gradient(from {$config['angle']}deg, {$stopsStr})";
default:
// Unknown type — mirrors the TypeScript, which returns undefined.
return '';
}
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →