Skip to content

Accept CSS numbers without a leading zero in rgb/rgba/hsl/hsla#187

Merged
grosser merged 1 commit into
premailer:masterfrom
fukayatsu:fix-rgba-leading-dot-alpha
May 7, 2026
Merged

Accept CSS numbers without a leading zero in rgb/rgba/hsl/hsla#187
grosser merged 1 commit into
premailer:masterfrom
fukayatsu:fix-rgba-leading-dot-alpha

Conversation

@fukayatsu
Copy link
Copy Markdown
Contributor

RE_COLOUR_NUMERIC / RE_COLOUR_NUMERIC_ALPHA use \d+(\.\d+)? for each
numeric component, which requires at least one digit before the decimal
point. CSS Values & Units allows <number> to omit the integer part
([0-9]* '.' [0-9]+), so values like rgba(0,0,0,.1) are not recognised
as colours.

The user-visible effect is that shorthand expansion silently drops the
colour. For example:

parser = CssParser::Parser.new
parser.add_block!(".a { border: 1px solid rgba(0,0,0,.1); }")
parser.each_rule_set { |rs, _| rs.expand_shorthand! }
# => border-top-width / -style are kept, border-top-color is gone.

The same shorthand with rgba(0,0,0,0.1) works correctly. background:
is affected the same way (background-color is dropped).

This bites Premailer users in the wild because Dart Sass's compressed
output strips leading zeros, so .1 is what the email inliner actually
receives.

This PR replaces \d+(\.\d+)? with (?:\d*\.)?\d+ in both numeric
colour regexps. The new pattern accepts 1, 1.5, and .5 while still
rejecting bare 1. (also invalid per spec).

Tests:

  • Positive cases added to RE_COLOUR (.1-style alpha for rgba/hsla).
  • Shorthand-expansion regression tests for both border: and
    background: covering rgba(0,0,0,.1).

Pre-Merge Checklist

  • CHANGELOG.md updated with short summary

The numeric colour regexps used `\d+(\.\d+)?` which requires at least
one digit before the decimal point, so values like `rgba(0,0,0,.1)`
were not recognised as colours. Per CSS Values & Units, `<number>`
permits the integer part to be omitted (`[0-9]* '.' [0-9]+`).

The practical impact is that shorthand expansion silently dropped the
colour:

  border: 1px solid rgba(0,0,0,.1)
    -> border-top-width / -style are kept, border-top-color is gone.

  background: url(x.png) rgba(0,0,0,.1)
    -> background-image is kept, background-color is gone.

This bites users of Dart Sass + Premailer because Dart Sass's
`compressed` output strips leading zeros, so `.1` is what the
inliner actually receives.

Switching the integer-and-optional-fraction pattern to
`(?:\d*\.)?\d+` accepts `1`, `1.5`, and `.5` while still
rejecting a bare `1.` (also invalid per spec).

Tests cover both the regex itself (positive cases for `.1`-style
values) and the shorthand-expansion regression for `border:` and
`background:`.
@grosser grosser merged commit 920a01b into premailer:master May 7, 2026
5 checks passed
@grosser
Copy link
Copy Markdown
Contributor

grosser commented May 7, 2026

thx! 2.2.0

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.

2 participants