idci/color-scheme-bundle
A Symfony bundle for managing color schemes in your application. Define named palettes, organize colors centrally, and reuse them across templates and configuration so themes stay consistent and easy to update. Useful for theming and design-system work.
Treat this repository as a read-only spec, not a dependency. The single most important first step is confirming that in composer.json: the require block will pin symfony/symfony at a 2.x range (EOL since November 2018), which is mutually unsatisfiable with any maintained Laravel release. Do not run composer require idci/color-scheme-bundle — it will cascade your dependency tree into an unresolvable state.
Minimal path to first value, in order:
git clone <repo> /tmp/color-scheme-ref && cd /tmp/color-scheme-ref
cat composer.json # what it depends on; is there a wrapped core lib?
find . -type f -name '*.php' | head -50
ls -R Resources/config # services.xml + doctrine mappings
DependencyInjection/*Extension.php file contains the Configuration tree — that is the authoritative list of what dimensions a "color scheme" has in this package (name, palette entries, defaults, maybe an entity class). This is the schema you actually want to port.getScheme(name), getColors(), getComplementary(), getAnalogous(), getTriadic(). That method list is your interface contract.idci/color-scheme package. If that core library is framework-agnostic and still on Packagist, that is installable in Laravel; the bundle is just Symfony glue.First use case to target: a tenant-branded color scheme resolved per request. Concretely — a config/color-scheme.php with named palettes, a ColorSchemeManager singleton, a ResolveColorScheme middleware, and a @colorScheme Blade directive that emits CSS custom properties into your layout. That is ~200 lines and covers the same ground as the bundle's entire feature set. Budget 3–5 engineer-days including tests.
If you genuinely maintain a frozen Symfony 2.8 app, the steps are different: register the bundle in AppKernel::registerBundles(), add idci_color_scheme: { ... } to config.yml, and inject the manager service. But that is the only scenario where the code runs as shipped.
Pattern 1 — Config-defined palettes (the portable core). The bundle models schemes as named sets in configuration. Re-express that directly as a Laravel config file, which gets you config:cache support for free:
// config/color-scheme.php
return [
'default' => 'neutral',
'schemes' => [
'neutral' => ['primary' => '#1a1a1a', 'accent' => '#0066cc', 'surface' => '#ffffff'],
'dark' => ['primary' => '#f5f5f5', 'accent' => '#66b3ff', 'surface' => '#111111'],
],
];
Pattern 2 — Resolution precedence chain. This is where the bundle's design is worth copying: it separates scheme definition from scheme resolution. Implement an explicit cascade so behavior is never ambiguous:
// explicit override → user preference → tenant default → app default
public function resolve(Request $request): array
{
$name = $request->query('scheme')
?? optional($request->user())->color_scheme
?? optional($request->user()?->tenant)->default_color_scheme
?? config('color-scheme.default');
return config("color-scheme.schemes.{$name}", config('color-scheme.schemes.'.config('color-scheme.default')));
}
Bind it once, resolve once per request via middleware, share via View::share or a view composer so Blade doesn't re-resolve.
Pattern 3 — Persistence. The bundle associates schemes with your entities via Doctrine mappings under Resources/config/doctrine. The Laravel equivalent is a single additive column and no pivot table:
Schema::table('users', fn (Blueprint $t) => $t->string('color_scheme')->nullable()->index());
Store the scheme name, never serialized colors — otherwise config changes require a data migration.
Pattern 4 — Presentation layer. The bundle's Twig extension becomes a Blade component that emits design tokens rather than inline styles, so the scheme is consumable by Tailwind/CSS:
{{-- resources/views/components/color-scheme.blade.php --}}
<style>
:root {
@foreach ($scheme as $token => $hex)
--color-{{ $token }}: {{ $hex }};
@endforeach
}
</style>
Pattern 5 — Extension point parity. The bundle's extensibility comes from overriding its manager service in the container. In Laravel the analogue is a contract plus contextual binding:
$this->app->singleton(ColorSchemeManager::class, fn ($app) => new ColorSchemeManager($app['config']));
// and for tests:
$this->app->instance(ColorSchemeManager::class, new FakeColorSchemeManager());
Symfony-native usage, for reference only: services are declared in Resources/config/services.xml and injected by id; the Twig extension is registered with a twig.extension tag; scheme entities use Doctrine annotations in Model/. Compiler passes and XML service definitions have no Laravel analogue — do not reproduce that structure in a provider. Over-engineering the provider to mirror Symfony's container model is the most common way this port goes wrong.
ServiceProvider, no auto-discovery, no config/ publish tag, no Blade directives. Any "integration" is a clean-room port. Estimate accordingly and set the expectation up front so nobody budgets for a shortcut.declare(strict_types=1), no return types, array() literals, possibly Twig_Extension and other pre-namespace or early-namespace class references. Do not paste files and let the linter "fix" them — reimplement from the schema summary and avoid license contamination from unverified copied code.TreeBuilder will fill unspecified keys with defaults and never warn. When extracting the schema, read ->defaultValue() calls explicitly — that's where implicit contract lives (e.g. what "default scheme" means when the DB column is null). Reproduce those defaults deliberately in config/color-scheme.php.getComplementary() / getAnalogous() derive colors algorithmically from an HSL rotation. They do not check contrast. If WCAG 2.1 AA is a requirement, the algorithmic helpers are unusable as-is — you must constrain selection to pre-validated palettes and add a contrast assertion in tests (a unit-testable contrastRatio() helper is cheap insurance against white-on-white regressions).[] renders an unstyled page that looks broken but throws nothing. Always terminate the resolution chain with an explicit default and assert non-empty in a feature test.Resources/config/doctrine rather than annotations, and the entity directory may not be PSR-4. Don't assume src/; don't trust autoload paths. In the port, prefer Eloquent's conventional migration over replicating a mapping layer.(tenant, user_preference) — not by user id — to keep cardinality bounded and avoid an unbounded cache key space.php bin/console debug:container idci_color_scheme and debug:config are your tools; the container dump shows whether the manager service is public. In the Laravel port, php artisan tinker plus config('color-scheme.schemes') after php artisan config:clear — remember that config:cache will freeze your palettes and make runtime overrides invisible.composer require against a modern Laravel app.How can I help you explore Laravel packages today?