[Jellybean] Blend vars and nested blends

Token names are referenced using the supplied name, instead of presuming that we want the *-rgb variant. This can still be provided explicitly.

Nested blends are now also supported by resolving recursively.

Bug: b/278121949
Change-Id: I412d1133ac900e0b5a5b596088de81e304e74196
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4493675
Commit-Queue: Christos Froussios <cfroussios@chromium.org>
Reviewed-by: Zain Afzal <zafzal@google.com>
Cr-Commit-Position: refs/heads/main@{#1138116}
diff --git a/tools/style_variable_generator/css_generator.py b/tools/style_variable_generator/css_generator.py
index 6efae6f..779cd05 100644
--- a/tools/style_variable_generator/css_generator.py
+++ b/tools/style_variable_generator/css_generator.py
@@ -153,12 +153,28 @@
         if isinstance(c, ColorRGB):
             return '%d, %d, %d' % (c.r, c.g, c.b)
 
-        raise NotImplementedError()
+        raise NotImplementedError(f'Cannot reduce {c} to RBG')
 
-    def CSSBlendInputColor(self, c):
+    def CSSBlendInputColor(self, c, mode):
         '''Resolves a color for use in a color-mix call.'''
+        # TODO(b/278121949): Assert that the color is opaque.
+        if (isinstance(c, ColorVar)):
+            return 'var(%s)' % self.ToCSSVarName(c.var)
+        if (isinstance(c, ColorBlend)):
+            return self.ToBlendColor(c, mode)
         return 'rgb(%s)' % self.CSSColorRGB(c)
 
+    def ToBlendColor(self, color, mode):
+        '''Resolves a color blend. Allows for nested blends.'''
+        assert (isinstance(color, ColorBlend))
+        blendPercentage = float(
+            color.blendPercentage
+            or self.ExtractOpacity(color.blended_colors[0], mode))
+        return 'color-mix(in srgb, %s %s%%, %s)' % (
+            self.CSSBlendInputColor(color.blended_colors[0],
+                                    mode), blendPercentage,
+            self.CSSBlendInputColor(color.blended_colors[1], mode))
+
     def ExtractOpacity(self, c, mode):
         if isinstance(c, ColorVar):
             return self.ExtractOpacity(self.model.colors.Resolve(c.var, mode),
@@ -175,13 +191,7 @@
             return 'var(%s)' % self.ToCSSVarName(color.var)
 
         if isinstance(color, ColorBlend):
-            blendPercentage = float(
-                color.blendPercentage
-                or self.ExtractOpacity(color.blended_colors[0], mode))
-            return 'color-mix(in srgb, %s %s%%, %s)' % (
-                self.CSSBlendInputColor(
-                    color.blended_colors[0]), blendPercentage,
-                self.CSSBlendInputColor(color.blended_colors[1]))
+            return self.ToBlendColor(color, mode)
 
         if isinstance(color,
                       ((ColorRGB, ColorRGBVar))) and color.opacity.a != 1:
diff --git a/tools/style_variable_generator/tests/colors_sys_tokens_test.json5 b/tools/style_variable_generator/tests/colors_sys_tokens_test.json5
index 9ed93042..cdb82ba 100644
--- a/tools/style_variable_generator/tests/colors_sys_tokens_test.json5
+++ b/tools/style_variable_generator/tests/colors_sys_tokens_test.json5
@@ -30,7 +30,9 @@
       light: "$white",
       dark: "blend(rgba($white.rgb, 0.04), $cros.ref.primary50)",
     },
-    'blend-opac': "blend(rgb($white.rgb), 40%, $cros.ref.primary50)",
+    'blend-opac': "blend($white, 40%, $cros.ref.primary50)",
+    'blend-a-ref-blend': "blend($white, 40%, $cros.sys.blend-opac)",
+    'blend-a-blend': "blend($white, 40%, blend($cros.sys.blend-opac, 2%, $white))",
   },
   opacities: {
     'disabled-opacity': 0.38,
diff --git a/tools/style_variable_generator/tests/goldens/blend_colors_test_expected.css b/tools/style_variable_generator/tests/goldens/blend_colors_test_expected.css
index 5c4d1644..226c378e 100644
--- a/tools/style_variable_generator/tests/goldens/blend_colors_test_expected.css
+++ b/tools/style_variable_generator/tests/goldens/blend_colors_test_expected.css
@@ -20,9 +20,9 @@
   --cros-highlight-color-hover-rgb: 0, 0, 0;
   --cros-highlight-color-hover: rgba(var(--cros-highlight-color-hover-rgb), 0.2);
 
-  --cros-foo-color: color-mix(in srgb, rgb(var(--cros-highlight-color-hover-rgb)) 20.0%, rgb(var(--cros-bg-color-rgb)));
+  --cros-foo-color: color-mix(in srgb, var(--cros-highlight-color-hover) 20.0%, var(--cros-bg-color));
 
-  --cros-bar-color: color-mix(in srgb, rgb(var(--cros-highlight-color-hover-rgb)) 20.0%, rgb(var(--cros-bg-color-rgb)));
+  --cros-bar-color: color-mix(in srgb, var(--cros-highlight-color-hover) 20.0%, var(--cros-bg-color));
 
   --cros-disabled-opacity: 0.38;
 }
@@ -35,8 +35,8 @@
   --cros-highlight-color-hover-rgb: 255, 255, 255;
   --cros-highlight-color-hover: rgba(var(--cros-highlight-color-hover-rgb), 0.4);
 
-  --cros-foo-color: color-mix(in srgb, rgb(var(--cros-highlight-color-hover-rgb)) 40.0%, rgb(var(--cros-bg-color-rgb)));
+  --cros-foo-color: color-mix(in srgb, var(--cros-highlight-color-hover) 40.0%, var(--cros-bg-color));
 
-  --cros-bar-color: color-mix(in srgb, rgb(var(--cros-highlight-color-hover-rgb)) 40.0%, rgb(var(--cros-bg-color-rgb)));
+  --cros-bar-color: color-mix(in srgb, var(--cros-highlight-color-hover) 40.0%, var(--cros-bg-color));
 }
 }
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_custom_dark_toggle_expected.css b/tools/style_variable_generator/tests/goldens/colors_test_custom_dark_toggle_expected.css
index b8f80406..8f7bdc58b 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_custom_dark_toggle_expected.css
+++ b/tools/style_variable_generator/tests/goldens/colors_test_custom_dark_toggle_expected.css
@@ -39,7 +39,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, var(--google-grey-900));
 
   --cros-reference-opacity: 1;
 }
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_dark_only_expected.css b/tools/style_variable_generator/tests/goldens/colors_test_dark_only_expected.css
index b8bbd83e..9f47103 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_dark_only_expected.css
+++ b/tools/style_variable_generator/tests/goldens/colors_test_dark_only_expected.css
@@ -20,7 +20,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, var(--google-grey-900));
 
   --cros-disabled-opacity: 0.38;
 
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_expected.css b/tools/style_variable_generator/tests/goldens/colors_test_expected.css
index 967f7b01..038e676 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_expected.css
+++ b/tools/style_variable_generator/tests/goldens/colors_test_expected.css
@@ -40,7 +40,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, var(--google-grey-900));
 
   --cros-reference-opacity: 1;
 }
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_include_style_sheet_expected.ts b/tools/style_variable_generator/tests/goldens/colors_test_include_style_sheet_expected.ts
index 6daeaa3..9874c5e3 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_include_style_sheet_expected.ts
+++ b/tools/style_variable_generator/tests/goldens/colors_test_include_style_sheet_expected.ts
@@ -53,7 +53,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, var(--google-grey-900));
 
   --cros-reference-opacity: 1;
 ` : '';
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_typography_and_untyped_css_expected.ts b/tools/style_variable_generator/tests/goldens/colors_test_typography_and_untyped_css_expected.ts
index 5a450c7..c6895074 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_typography_and_untyped_css_expected.ts
+++ b/tools/style_variable_generator/tests/goldens/colors_test_typography_and_untyped_css_expected.ts
@@ -55,7 +55,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, var(--google-grey-900));
 
   --cros-reference-opacity: 1;
 ` : '';
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_typography_expected.ts b/tools/style_variable_generator/tests/goldens/colors_test_typography_expected.ts
index 8e864721..c828dfb 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_typography_expected.ts
+++ b/tools/style_variable_generator/tests/goldens/colors_test_typography_expected.ts
@@ -54,7 +54,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, var(--google-grey-900));
 
   --cros-reference-opacity: 1;
 ` : '';
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_untyped_css_expected.ts b/tools/style_variable_generator/tests/goldens/colors_test_untyped_css_expected.ts
index b37ed48..8cf64f1 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_untyped_css_expected.ts
+++ b/tools/style_variable_generator/tests/goldens/colors_test_untyped_css_expected.ts
@@ -54,7 +54,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, var(--google-grey-900));
 
   --cros-reference-opacity: 1;
 ` : '';
diff --git a/tools/style_variable_generator/tests/goldens/colors_tokens_test_color_mappings.cc.generated b/tools/style_variable_generator/tests/goldens/colors_tokens_test_color_mappings.cc.generated
index 9f2214f..14bc827 100644
--- a/tools/style_variable_generator/tests/goldens/colors_tokens_test_color_mappings.cc.generated
+++ b/tools/style_variable_generator/tests/goldens/colors_tokens_test_color_mappings.cc.generated
@@ -46,6 +46,16 @@
   } else {
     mixer[kCrosSysBlendOpac] = ui::GetResultingPaintColor({SkColorSetRGB(0xFF, 0xFF, 0xFF)}, {kCrosRefPrimary50});
   }
+  if (dark_mode) {
+    mixer[kCrosSysBlendARefBlend] = ui::GetResultingPaintColor({SkColorSetRGB(0xFF, 0xFF, 0xFF)}, {kCrosSysBlendOpac});
+  } else {
+    mixer[kCrosSysBlendARefBlend] = ui::GetResultingPaintColor({SkColorSetRGB(0xFF, 0xFF, 0xFF)}, {kCrosSysBlendOpac});
+  }
+  if (dark_mode) {
+    mixer[kCrosSysBlendABlend] = ui::GetResultingPaintColor({SkColorSetRGB(0xFF, 0xFF, 0xFF)}, ui::GetResultingPaintColor({kCrosSysBlendOpac}, {SkColorSetRGB(0xFF, 0xFF, 0xFF)}));
+  } else {
+    mixer[kCrosSysBlendABlend] = ui::GetResultingPaintColor({SkColorSetRGB(0xFF, 0xFF, 0xFF)}, ui::GetResultingPaintColor({kCrosSysBlendOpac}, {SkColorSetRGB(0xFF, 0xFF, 0xFF)}));
+  }
 }
 
 
@@ -65,6 +75,10 @@
       return "--cros-sys-on_primary_container";
     case kCrosSysBlendOpac:
       return "--cros-sys-blend_opac";
+    case kCrosSysBlendARefBlend:
+      return "--cros-sys-blend_a_ref_blend";
+    case kCrosSysBlendABlend:
+      return "--cros-sys-blend_a_blend";
   }
   NOTREACHED();
   return "";
diff --git a/tools/style_variable_generator/tests/goldens/colors_tokens_test_color_mappings.h.generated b/tools/style_variable_generator/tests/goldens/colors_tokens_test_color_mappings.h.generated
index 8c390d2..6a8c3341 100644
--- a/tools/style_variable_generator/tests/goldens/colors_tokens_test_color_mappings.h.generated
+++ b/tools/style_variable_generator/tests/goldens/colors_tokens_test_color_mappings.h.generated
@@ -33,6 +33,8 @@
   kCrosSysPrimaryContainer,
   kCrosSysOnPrimaryContainer,
   kCrosSysBlendOpac,
+  kCrosSysBlendARefBlend,
+  kCrosSysBlendABlend,
   kCrosSysColorsEnd,
 };
 
diff --git a/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.css b/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.css
index fe914f7..8f92877 100644
--- a/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.css
+++ b/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.css
@@ -26,7 +26,11 @@
   --cros-sys-on_primary_container-rgb: 255, 255, 255;
   --cros-sys-on_primary_container: rgb(var(--cros-sys-on_primary_container-rgb));
 
-  --cros-sys-blend_opac: color-mix(in srgb, rgb(255, 255, 255) 40.0%, rgb(var(--cros-ref-primary50-rgb)));
+  --cros-sys-blend_opac: color-mix(in srgb, rgb(255, 255, 255) 40.0%, var(--cros-ref-primary50));
+
+  --cros-sys-blend_a_ref_blend: color-mix(in srgb, rgb(255, 255, 255) 40.0%, var(--cros-sys-blend_opac));
+
+  --cros-sys-blend_a_blend: color-mix(in srgb, rgb(255, 255, 255) 40.0%, color-mix(in srgb, var(--cros-sys-blend_opac) 2.0%, rgb(255, 255, 255)));
 
   --cros-sys-disabled_opacity: 0.38;
 
@@ -48,9 +52,13 @@
   --cros-sys-primary_container-rgb: var(--cros-ref-primary50-rgb);
   --cros-sys-primary_container: rgba(var(--cros-sys-primary_container-rgb), var(--cros-sys-disabled_opacity));
 
-  --cros-sys-on_primary_container: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--cros-ref-primary50-rgb)));
+  --cros-sys-on_primary_container: color-mix(in srgb, rgb(255, 255, 255) 4.0%, var(--cros-ref-primary50));
 
-  --cros-sys-blend_opac: color-mix(in srgb, rgb(255, 255, 255) 40.0%, rgb(var(--cros-ref-primary50-rgb)));
+  --cros-sys-blend_opac: color-mix(in srgb, rgb(255, 255, 255) 40.0%, var(--cros-ref-primary50));
+
+  --cros-sys-blend_a_ref_blend: color-mix(in srgb, rgb(255, 255, 255) 40.0%, var(--cros-sys-blend_opac));
+
+  --cros-sys-blend_a_blend: color-mix(in srgb, rgb(255, 255, 255) 40.0%, color-mix(in srgb, var(--cros-sys-blend_opac) 2.0%, rgb(255, 255, 255)));
 
   --cros-sys-reference_opacity: 1;
 }
diff --git a/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.json b/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.json
index f55caa45..d826a7f 100644
--- a/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.json
+++ b/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.json
@@ -45,6 +45,22 @@
                 "light": "blend(rgba(255, 255, 255, 1), 40, cros.ref.primary50)",
                 "dark": "blend(rgba(255, 255, 255, 1), 40, cros.ref.primary50)"
             }
+        },
+        {
+            "token_name": "cros.sys.blend-a-ref-blend",
+            "css_variable": "--cros-sys-blend_a_ref_blend",
+            "mode_values": {
+                "light": "blend(rgba(255, 255, 255, 1), 40, cros.sys.blend-opac)",
+                "dark": "blend(rgba(255, 255, 255, 1), 40, cros.sys.blend-opac)"
+            }
+        },
+        {
+            "token_name": "cros.sys.blend-a-blend",
+            "css_variable": "--cros-sys-blend_a_blend",
+            "mode_values": {
+                "light": "blend(rgba(255, 255, 255, 1), 40, blend(cros.sys.blend-opac, 2, rgba(255, 255, 255, 1)))",
+                "dark": "blend(rgba(255, 255, 255, 1), 40, blend(cros.sys.blend-opac, 2, rgba(255, 255, 255, 1)))"
+            }
         }
     ]
 }
diff --git a/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.protojson b/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.protojson
index f82d0e6..2f8e0a1 100644
--- a/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.protojson
+++ b/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.protojson
@@ -43,5 +43,19 @@
       light_value: 0xFFFFFFFF,
       dark_value: 0xFFFFFFFF
     }
+  },
+  {
+    key: "cros.sys.blend-a-ref-blend",
+    value: {
+      light_value: 0xFFFFFFFF,
+      dark_value: 0xFFFFFFFF
+    }
+  },
+  {
+    key: "cros.sys.blend-a-blend",
+    value: {
+      light_value: 0xFFFFFFFF,
+      dark_value: 0xFFFFFFFF
+    }
   }
 ]
diff --git a/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.ts b/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.ts
index f1d901e..379385b3 100644
--- a/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.ts
+++ b/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.ts
@@ -15,5 +15,7 @@
 export const CROS_SYS_PRIMARY_CONTAINER = css`var(--cros-sys-primary_container)`;
 export const CROS_SYS_ON_PRIMARY_CONTAINER = css`var(--cros-sys-on_primary_container)`;
 export const CROS_SYS_BLEND_OPAC = css`var(--cros-sys-blend_opac)`;
+export const CROS_SYS_BLEND_A_REF_BLEND = css`var(--cros-sys-blend_a_ref_blend)`;
+export const CROS_SYS_BLEND_A_BLEND = css`var(--cros-sys-blend_a_blend)`;
 export const CROS_SYS_DISABLED_OPACITY = css`var(--cros-sys-disabled_opacity)`;
 export const CROS_SYS_REFERENCE_OPACITY = css`var(--cros-sys-reference_opacity)`;
diff --git a/tools/style_variable_generator/tests/goldens/suppress_sources_comment_test_expected.css b/tools/style_variable_generator/tests/goldens/suppress_sources_comment_test_expected.css
index 02901c9..fb90c52f 100644
--- a/tools/style_variable_generator/tests/goldens/suppress_sources_comment_test_expected.css
+++ b/tools/style_variable_generator/tests/goldens/suppress_sources_comment_test_expected.css
@@ -35,7 +35,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, var(--google-grey-900));
 
   --cros-reference-opacity: 1;
 }
diff --git a/ui/chromeos/styles/cros_sys_colors.json5 b/ui/chromeos/styles/cros_sys_colors.json5
index fb257d7..e44b15fe 100644
--- a/ui/chromeos/styles/cros_sys_colors.json5
+++ b/ui/chromeos/styles/cros_sys_colors.json5
@@ -201,11 +201,7 @@
     'base-elevated': {
       light: '$cros.ref.neutralvariant100',
       /* In dark mode we layer primary80 @ 11% ontop of neutral80 @ 2% ontop of neutral 10. */
-      /* TODO(b/278121949): This should be:
-      dark: 'blend(rgba($cros.ref.primary80.rgb, .11),
-                   blend(rgba($cros.ref.neutral80.rgb, .02),
-                         $cros.ref.neutral10))' */
-      dark: 'blend(rgba($cros.ref.primary80.rgb, .11), $black)',
+      dark: 'blend($cros.ref.primary80, 11%, blend($cros.ref.neutral80, 2%, $cros.ref.neutral10))',
       generate_per_mode: true,
     },