Skip to content

fix(theme): make the Theme Generator's colors real - #9

Merged
puikinsh merged 1 commit into
masterfrom
fix/theme-generator-colors
Aug 11, 2026
Merged

fix(theme): make the Theme Generator's colors real#9
puikinsh merged 1 commit into
masterfrom
fix/theme-generator-colors

Conversation

@puikinsh

Copy link
Copy Markdown
Member

Description

Supersedes #8 by @ruanpepe, which correctly spotted that the Theme Generator's color pickers were broken. This lands that fix plus the underlying reason the page never worked.

The rendering bug (#8)

The page passed value="#343a40" to <x-adminlte-input-color>, which has no value prop — so the color fell through to $attributes->merge() and landed after the component's own value attribute:

<input type="color" name="sidebar_color" value="#0d6efd" class="form-control form-control-color" value="#343a40">

Browsers keep the first of two duplicate attributes, so all four swatches rendered #0d6efd regardless. Now uses the documented default prop, seeded from config.

The deeper bug

The config snippet the page told you to paste did nothing. sidebar_color, primary_color, navbar_color and footer_color were never read by the package, and color_mode was not a config key at all.

Rather than delete the feature, this makes the keys real. Support\ThemeColors injects them into the layout <head> as CSS custom-property overrides, so the chrome can be recolored without compiling SCSS. All four default to null and emit nothing in that case, so existing installs are untouched.

primary_color needs more than --bs-primary, because Bootstrap compiles .btn-primary from Sass literals rather than from the variable. Hover/active shades are recomputed with Bootstrap's own shade-color() weights, and button text follows color-contrast() — which is deliberately not "pick the higher contrast ratio": Bootstrap's own $primary (#0d6efd) contrasts 4.50:1 against white and 4.67:1 against black, so choosing the maximum would render every stock primary button with black text instead of Bootstrap's white. Tests pin the shades against Bootstrap's compiled output (#0b5ed7, #0a58ca, #0a53be).

The block is rendered unescaped, so values are matched against a strict #rgb/#rrggbb pattern and anything else is dropped. A data provider covers rgb(), var(), named colors and both CSS- and markup-injection attempts.

Also

  • The generator previews every change live on the page, seeds each control from the running config, and emits only keys that exist.
  • The color-mode select is relabelled preview-only: at runtime the mode comes from the topbar toggle and the visitor's system preference, so a config key there would be a second lie.
  • Color inputs use the component's label prop, so labels are actually associated with their inputs.
  • Applies to the main, auth and error layouts.

Checklist

  • composer check passes locally (Pint + PHPStan + PHPUnit)
  • Added/updated tests for the change
  • Updated docs in docs/ if behaviour or APIs changed (they are served in-app at /docs)
  • Added a CHANGELOG.md entry under [Unreleased]
  • New translatable strings added to all 9 locale files in resources/lang/ — n/a, no new strings

🤖 Generated with Claude Code

The Theme Generator demo page was broken in two independent ways.

Every color picker rendered Bootstrap blue. The page passed
`value="#343a40"` to `<x-adminlte-input-color>`, which has no `value`
prop — so the color fell through to `$attributes->merge()` and landed
*after* the component's own `value` attribute. Browsers keep the first
of two duplicate attributes, so all four swatches showed `#0d6efd` no
matter what the page asked for. They now use the documented `default`
prop, seeded from config.

And the config snippet it told you to paste did nothing: four of its
five keys were never read by the package, and `color_mode` was not a
config key at all. Rather than delete the feature, make the keys real —
`primary_color`, `sidebar_color`, `navbar_color` and `footer_color` are
now injected into the layout head as CSS custom-property overrides by
`Support\ThemeColors`, so the chrome can be recolored without compiling
SCSS. All four default to null and emit nothing at all in that case.

`primary_color` needs more than `--bs-primary` because Bootstrap
compiles `.btn-primary` from Sass literals, so the hover/active shades
are recomputed with Bootstrap's own `shade-color()` weights and the
button text follows `color-contrast()`. That last one is not
"pick the higher ratio": Bootstrap's own $primary contrasts 4.50:1 on
white and 4.67:1 on black, so choosing the maximum would render every
stock primary button with black text. Tests pin the shades against
Bootstrap's compiled output.

The style block is rendered unescaped, so colors are matched against a
strict `#rgb`/`#rrggbb` pattern and anything else is dropped.

The generator now previews every change live, seeds each control from
the running config, emits only keys that exist, and labels the
color-mode select as preview-only — the runtime mode comes from the
topbar toggle and the visitor's system preference, not from config.

Co-authored-by: Ruan Pépe <ruanpepe@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@puikinsh
puikinsh merged commit 5bff3f5 into master Aug 11, 2026
3 checks passed
@puikinsh
puikinsh deleted the fix/theme-generator-colors branch August 11, 2026 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant