KSES: Add support for rgb(a) color#10923
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
3e19b4e to
797ab0e
Compare
Co-authored-by: Weston Ruter <westonruter@gmail.com>
Co-authored-by: Weston Ruter <westonruter@gmail.com>
| array( | ||
| 'css' => 'color: rgb(255, 50%, 0)', | ||
| 'expected' => 'color: rgb(255, 50%, 0)', | ||
| ), |
There was a problem hiding this comment.
Thanks for noticing, I have corrected the invalid values in 129c006
There was a problem hiding this comment.
I don't think it's fixed? The problem is the 50%. Apparently CSS doesn't like mixing percentages and non-percentages. The above is still marked as invalid CSS by the CSS Validator.
There was a problem hiding this comment.
This format is the Modern Syntax defined in CSS Color Module Level 4.
https://www.w3.org/TR/css-color-4/#rgb-functions
It should be supported by all browsers.
https://codepen.io/editor/Tetsuaki-Hamno/pen/019df1b7-20f5-77a8-b2f1-27811658e5bc
There was a problem hiding this comment.
Yeah, specifically the legacy format uses the commas and which doesn't allow mixing percentage and non-percentage values:
The modern syntax without the commas allows mixing, and 129c006 looks right.
Drop the `^`/`$` anchors on the rgb()/rgba() match in `safecss_filter_attr()` and switch to `preg_match_all`, mirroring how `url()` is handled. This lets shorthand values like `border: 1px solid rgb(0, 0, 0)` and `background: rgb(0, 0, 0) center` pass sanitization, while malformed colors still fail because the leftover `(` is rejected downstream. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`rgb(255, 50%, 0)` mixes numbers and percentages within the legacy comma-separated syntax, which is invalid per CSS Color Level 4 — only the modern space-separated syntax permits mixing. Switch the case to `rgb(255 50% 0)`. Also bring `rgba(100, 200, 300, 0.8)` into the 0–255 range (`250`) so the fixtures reflect realistic, valid colors rather than relying on browser clamping. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Weston Ruter <westonruter@gmail.com>
Co-authored-by: Weston Ruter <westonruter@gmail.com>
CSS Color Level 4 introduced the `none` keyword to represent missing color components, but it is only valid in the modern (space-separated) syntax — the legacy comma-separated form does not accept it. Update the regex to permit `none` in space-separated `rgb()`/`rgba()` while still rejecting it in the comma-separated form, and factor out the shared component term to avoid duplication. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… comment. Fixes a `Squiz.WhiteSpace.SuperfluousWhitespace.EndLine` PHPCS violation introduced by an earlier comment edit on this branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| if ( $is_custom_var ) { | ||
| $css_value = trim( $parts[1] ); | ||
| $url_attr = str_starts_with( $css_value, 'url(' ); | ||
| $gradient_attr = str_contains( $css_value, '-gradient(' ); |
There was a problem hiding this comment.
Claude pointed out that rgb() isn't working for CSS variables. It suggests adding something like this:
| $gradient_attr = str_contains( $css_value, '-gradient(' ); | |
| $color_attr = (bool) preg_match( '/\brgba?\(/i', $css_value ); |
| 'color', | ||
|
|
||
| 'border', | ||
| 'border-color', | ||
| 'border-right', | ||
| 'border-right-color', | ||
| 'border-bottom', | ||
| 'border-bottom-color', | ||
| 'border-left', | ||
| 'border-left-color', | ||
| 'border-top', | ||
| 'border-top-color', | ||
|
|
||
| 'background', | ||
| 'background-color', |
There was a problem hiding this comment.
Another to add to the list: box-shadow. With some sorting:
| 'color', | |
| 'border', | |
| 'border-color', | |
| 'border-right', | |
| 'border-right-color', | |
| 'border-bottom', | |
| 'border-bottom-color', | |
| 'border-left', | |
| 'border-left-color', | |
| 'border-top', | |
| 'border-top-color', | |
| 'background', | |
| 'background-color', | |
| 'background', | |
| 'background-color', | |
| 'box-shadow', | |
| 'border', | |
| 'border-color', | |
| 'border-right', | |
| 'border-right-color', | |
| 'border-bottom', | |
| 'border-bottom-color', | |
| 'border-left', | |
| 'border-left-color', | |
| 'border-top', | |
| 'border-top-color', | |
| 'color', |
| // Simplified: matches the sequence `rgb(*)` or `rgba(*)`. | ||
| $term = '(?:[+-]?\d*\.?\d+)%?'; | ||
| $comma_syntax = '/rgba?\(\s*' . join( '\s*,\s*', array_fill( 0, 3, $term ) ) . '\s*(?:,\s*' . $term . '\s*)?\)/i'; | ||
| // The `none` keyword is only allowed in modern (space-separated) syntax. | ||
| $term = '(?:(?:[+-]?\d*\.?\d+)%?|none)'; | ||
| $space_syntax = '/rgba?\(\s*' . join( '\s+', array_fill( 0, 3, $term ) ) . '\s*(?:\/\s*' . $term . '\s*)?\)/i'; |
There was a problem hiding this comment.
Claude pointed out confusing reuse of $term variable, and that implode() is used more than join():
| // Simplified: matches the sequence `rgb(*)` or `rgba(*)`. | |
| $term = '(?:[+-]?\d*\.?\d+)%?'; | |
| $comma_syntax = '/rgba?\(\s*' . join( '\s*,\s*', array_fill( 0, 3, $term ) ) . '\s*(?:,\s*' . $term . '\s*)?\)/i'; | |
| // The `none` keyword is only allowed in modern (space-separated) syntax. | |
| $term = '(?:(?:[+-]?\d*\.?\d+)%?|none)'; | |
| $space_syntax = '/rgba?\(\s*' . join( '\s+', array_fill( 0, 3, $term ) ) . '\s*(?:\/\s*' . $term . '\s*)?\)/i'; | |
| // Simplified: matches the sequence `rgb(*)` or `rgba(*)`. | |
| $number_term = '(?:[+-]?\d*\.?\d+)%?'; | |
| $comma_syntax = '/rgba?\(\s*' . implode( '\s*,\s*', array_fill( 0, 3, $number_term1 ) ) . '\s*(?:,\s*' . $number_term . '\s*)?\)/i'; | |
| // The `none` keyword is only allowed in modern (space-separated) syntax. | |
| $number_or_none_term = '(?:(?:[+-]?\d*\.?\d+)%?|none)'; | |
| $space_syntax = '/rgba?\(\s*' . implode( '\s+', array_fill( 0, 3, $number_or_none_term ) ) . '\s*(?:\/\s*' . $number_or_none_term . '\s*)?\)/i'; |
Without this, assigning an `rgb()` or `rgba()` value to a CSS variable (e.g. `--my-color: rgb(255, 0, 0)`) was rejected because `$color_attr` was never set in the custom-property branch, leaving the open paren in the test string and tripping the disallowed-character check. Detect `rgb(`/`rgba(` in custom property values so the existing strict validation runs and structurally invalid colors continue to be rejected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Weston Ruter <westonruter@gmail.com>
The `box-shadow` property accepts a color component, but it was missing from `$css_color_data_types` so values like `box-shadow: 0 0 5px rgb(0, 0, 0)` were stripped while equivalent hex/named colors were allowed. Add `box-shadow` to the list (with the existing entries grouped/reordered for readability) so RGB(A) colors are preserved alongside the structural validation that already rejects malformed values. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lter_attr`. The single `$term` variable was reassigned with two different meanings — first a numeric term, then a numeric-or-`none` term — making the intent of each regex harder to follow. Rename to `$number_term` and `$number_or_none_term` so the legacy vs. modern syntax distinction is explicit at the point of use. Also switch the surrounding `join()` calls to `implode()` to match the prevailing convention in WordPress core PHP. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>



Note
This PR is the same as #3097. I submitted this new PR because I accidentally closed #3097 and it can no longer be reopened.
Trac ticket: https://core.trac.wordpress.org/ticket/56391
Related gutenberg issue: WordPress/gutenberg#39402
Related gutenberg PR: WordPress/gutenberg#39488
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.