Skip to content

Commit

Permalink
fix #1682: always use the shortest css alpha value
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Oct 15, 2021
1 parent 8b5d6e9 commit 85f85f2
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 5 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,22 @@
# Changelog

## Unreleased

* Minify CSS alpha values correctly ([#1682](https://github.com/evanw/esbuild/issues/1682))

When esbuild uses the `rgba()` syntax for a color instead of the 8-character hex code (e.g. when `target` is set to Chrome 61 or earlier), the 0-to-255 integer alpha value must be printed as a floating-point fraction between 0 and 1. The fraction was only printed to three decimal places since that is the minimal number of decimal places required for all 256 different alpha values to be uniquely determined. However, using three decimal places does not necessarily result in the shortest result. For example, `128 / 255` is `0.5019607843137255` which is printed as `".502"` using three decimal places, but `".5"` is equivalent because `round(0.5 * 255) == 128`, so printing `".5"` would be better. With this release, esbuild will always use the minimal numeric representation for the alpha value:

```css
/* Original code */
a { color: #FF800080 }

/* Old output (with --minify --target=chrome61) */
a{color:rgba(255,128,0,.502)}

/* New output (with --minify --target=chrome61) */
a{color:rgba(255,128,0,.5)}
```

## 0.13.6

* Emit decorators for `declare` class fields ([#1675](https://github.com/evanw/esbuild/issues/1675))
Expand Down
28 changes: 23 additions & 5 deletions internal/css_parser/css_decls_color.go
Expand Up @@ -633,11 +633,10 @@ func (p *parser) mangleColor(token css_ast.Token, hex uint32) css_ast.Token {
token.Kind = css_lexer.TFunction
token.Text = "rgba"
commaToken := p.commaToken()
alpha := floatToString(float64(hexA(hex)) / 255)
if p.options.MangleSyntax {
if text, ok := mangleNumber(alpha); ok {
alpha = text
}
index := hexA(hex) * 4
alpha := alphaFractionTable[index : index+4]
if space := strings.IndexByte(alpha, ' '); space != -1 {
alpha = alpha[:space]
}
token.Children = &[]css_ast.Token{
{Kind: css_lexer.TNumber, Text: strconv.Itoa(hexR(hex))}, commaToken,
Expand All @@ -649,3 +648,22 @@ func (p *parser) mangleColor(token css_ast.Token, hex uint32) css_ast.Token {

return token
}

// Every four characters in this table is the fraction for that index
const alphaFractionTable string = "" +
"0 .004.008.01 .016.02 .024.027.03 .035.04 .043.047.05 .055.06 " +
".063.067.07 .075.08 .082.086.09 .094.098.1 .106.11 .114.118.12 " +
".125.13 .133.137.14 .145.15 .153.157.16 .165.17 .173.176.18 .184" +
".19 .192.196.2 .204.208.21 .216.22 .224.227.23 .235.24 .243.247" +
".25 .255.26 .263.267.27 .275.28 .282.286.29 .294.298.3 .306.31 " +
".314.318.32 .325.33 .333.337.34 .345.35 .353.357.36 .365.37 .373" +
".376.38 .384.39 .392.396.4 .404.408.41 .416.42 .424.427.43 .435" +
".44 .443.447.45 .455.46 .463.467.47 .475.48 .482.486.49 .494.498" +
".5 .506.51 .514.518.52 .525.53 .533.537.54 .545.55 .553.557.56 " +
".565.57 .573.576.58 .584.59 .592.596.6 .604.608.61 .616.62 .624" +
".627.63 .635.64 .643.647.65 .655.66 .663.667.67 .675.68 .682.686" +
".69 .694.698.7 .706.71 .714.718.72 .725.73 .733.737.74 .745.75 " +
".753.757.76 .765.77 .773.776.78 .784.79 .792.796.8 .804.808.81 " +
".816.82 .824.827.83 .835.84 .843.847.85 .855.86 .863.867.87 .875" +
".88 .882.886.89 .894.898.9 .906.91 .914.918.92 .925.93 .933.937" +
".94 .945.95 .953.957.96 .965.97 .973.976.98 .984.99 .992.9961 "
29 changes: 29 additions & 0 deletions internal/css_parser/css_parser_test.go
@@ -1,6 +1,7 @@
package css_parser

import (
"fmt"
"testing"

"github.com/evanw/esbuild/internal/compat"
Expand Down Expand Up @@ -1316,3 +1317,31 @@ func TestTransform(t *testing.T) {
expectPrintedMangle(t, "a { transform: perspective(0px) }", "a {\n transform: perspective(0);\n}\n")
expectPrintedMangle(t, "a { transform: perspective(1px) }", "a {\n transform: perspective(1px);\n}\n")
}

func TestMangleAlpha(t *testing.T) {
alphas := []string{
"0", ".004", ".008", ".01", ".016", ".02", ".024", ".027", ".03", ".035", ".04", ".043", ".047", ".05", ".055", ".06",
".063", ".067", ".07", ".075", ".08", ".082", ".086", ".09", ".094", ".098", ".1", ".106", ".11", ".114", ".118", ".12",
".125", ".13", ".133", ".137", ".14", ".145", ".15", ".153", ".157", ".16", ".165", ".17", ".173", ".176", ".18", ".184",
".19", ".192", ".196", ".2", ".204", ".208", ".21", ".216", ".22", ".224", ".227", ".23", ".235", ".24", ".243", ".247",
".25", ".255", ".26", ".263", ".267", ".27", ".275", ".28", ".282", ".286", ".29", ".294", ".298", ".3", ".306", ".31",
".314", ".318", ".32", ".325", ".33", ".333", ".337", ".34", ".345", ".35", ".353", ".357", ".36", ".365", ".37", ".373",
".376", ".38", ".384", ".39", ".392", ".396", ".4", ".404", ".408", ".41", ".416", ".42", ".424", ".427", ".43", ".435",
".44", ".443", ".447", ".45", ".455", ".46", ".463", ".467", ".47", ".475", ".48", ".482", ".486", ".49", ".494", ".498",
".5", ".506", ".51", ".514", ".518", ".52", ".525", ".53", ".533", ".537", ".54", ".545", ".55", ".553", ".557", ".56",
".565", ".57", ".573", ".576", ".58", ".584", ".59", ".592", ".596", ".6", ".604", ".608", ".61", ".616", ".62", ".624",
".627", ".63", ".635", ".64", ".643", ".647", ".65", ".655", ".66", ".663", ".667", ".67", ".675", ".68", ".682", ".686",
".69", ".694", ".698", ".7", ".706", ".71", ".714", ".718", ".72", ".725", ".73", ".733", ".737", ".74", ".745", ".75",
".753", ".757", ".76", ".765", ".77", ".773", ".776", ".78", ".784", ".79", ".792", ".796", ".8", ".804", ".808", ".81",
".816", ".82", ".824", ".827", ".83", ".835", ".84", ".843", ".847", ".85", ".855", ".86", ".863", ".867", ".87", ".875",
".88", ".882", ".886", ".89", ".894", ".898", ".9", ".906", ".91", ".914", ".918", ".92", ".925", ".93", ".933", ".937",
".94", ".945", ".95", ".953", ".957", ".96", ".965", ".97", ".973", ".976", ".98", ".984", ".99", ".992", ".996",
}

for i, alpha := range alphas {
expectPrintedLowerMangle(t, fmt.Sprintf("a { color: #%08X }", i), "a {\n color: rgba(0, 0, 0, "+alpha+");\n}\n")
}

// An alpha value of 100% does not use "rgba(...)"
expectPrintedLowerMangle(t, "a { color: #000000FF }", "a {\n color: #000;\n}\n")
}

0 comments on commit 85f85f2

Please sign in to comment.