Rework darkest color in base16, shadows, and add hoverPopover styleTree

Keith Simmons created

Change summary

styles/src/styleTree/chatPanel.ts    |   7 
styles/src/styleTree/components.ts   |  13 +
styles/src/styleTree/editor.ts       |   2 
styles/src/styleTree/hoverPopover.ts |  20 +++
styles/src/styleTree/picker.ts       |   4 
styles/src/styleTree/workspace.ts    |   4 
styles/src/themes/common/base16.ts   | 173 ++++++++++++++---------------
styles/src/themes/common/theme.ts    |   4 
8 files changed, 128 insertions(+), 99 deletions(-)

Detailed changes

styles/src/styleTree/chatPanel.ts 🔗

@@ -4,9 +4,10 @@ import {
   backgroundColor,
   border,
   player,
-  shadow,
+  modalShadow,
   text,
-  TextColor
+  TextColor,
+  popoverShadow
 } from "./components";
 
 export default function chatPanel(theme: Theme) {
@@ -69,7 +70,7 @@ export default function chatPanel(theme: Theme) {
         cornerRadius: 6,
         padding: 4,
         border: border(theme, "primary"),
-        shadow: shadow(theme),
+        shadow: popoverShadow(theme),
       },
     },
     signInPrompt: text(theme, "sans", "secondary", { underline: true }),

styles/src/styleTree/components.ts 🔗

@@ -1,4 +1,5 @@
 import chroma from "chroma-js";
+import { isIPv4 } from "net";
 import Theme, { BackgroundColorSet } from "../themes/common/theme";
 import { fontFamilies, fontSizes, FontWeight } from "../tokens";
 import { Color } from "../utils/color";
@@ -84,10 +85,18 @@ export function backgroundColor(
   return theme.backgroundColor[name][state || "base"].value;
 }
 
-export function shadow(theme: Theme) {
+export function modalShadow(theme: Theme) {
   return {
     blur: 16,
-    color: chroma("black").alpha(theme.shadowAlpha.value).hex(),
+    color: theme.shadow.value,
     offset: [0, 2],
   };
 }
+
+export function popoverShadow(theme: Theme) {
+  return {
+    blur: 4,
+    color: theme.shadow.value,
+    offset: [1, 2],
+  };
+}

styles/src/styleTree/editor.ts 🔗

@@ -4,6 +4,7 @@ import {
   border,
   iconColor,
   player,
+  popoverShadow,
   text,
   TextColor
 } from "./components";
@@ -80,6 +81,7 @@ export default function editor(theme: Theme) {
       cornerRadius: 8,
       padding: 4,
       border: border(theme, "secondary"),
+      shadow: popoverShadow(theme),
       item: autocompleteItem,
       hoveredItem: {
         ...autocompleteItem,

styles/src/styleTree/hoverPopover.ts 🔗

@@ -0,0 +1,20 @@
+import Theme from "../themes/common/theme";
+import { backgroundColor, border, popoverShadow } from "./components";
+
+export default function HoverPopover(theme: Theme) {
+  return {
+    background: backgroundColor(theme, 500),
+    cornerRadius: 8,
+    padding: {
+      left: 8,
+      right: 8,
+      top: 4,
+      bottom: 4
+    },
+    shadow: popoverShadow(theme),
+    border: border(theme, "primary"),
+    margin: {
+      left: -14,
+    },
+  }
+}

styles/src/styleTree/picker.ts 🔗

@@ -1,5 +1,5 @@
 import Theme from "../themes/common/theme";
-import { backgroundColor, border, player, shadow, text } from "./components";
+import { backgroundColor, border, player, modalShadow, text } from "./components";
 
 export default function picker(theme: Theme) {
   return {
@@ -48,6 +48,6 @@ export default function picker(theme: Theme) {
         top: 7,
       },
     },
-    shadow: shadow(theme),
+    shadow: modalShadow(theme),
   };
 }

styles/src/styleTree/workspace.ts 🔗

@@ -1,6 +1,6 @@
 import Theme from "../themes/common/theme";
 import { withOpacity } from "../utils/color";
-import { backgroundColor, border, iconColor, shadow, text } from "./components";
+import { backgroundColor, border, iconColor, modalShadow, text } from "./components";
 import statusBar from "./statusBar";
 
 export function workspaceBackground(theme: Theme) {
@@ -164,7 +164,7 @@ export default function workspace(theme: Theme) {
       cornerRadius: 6,
       padding: 12,
       border: border(theme, "primary"),
-      shadow: shadow(theme),
+      shadow: modalShadow(theme),
     },
     notifications: {
       width: 380,

styles/src/themes/common/base16.ts 🔗

@@ -14,7 +14,6 @@ export function createTheme(
   name: string,
   isLight: boolean,
   ramps: { [rampName: string]: Scale },
-  blend?: number
 ): Theme {
   if (isLight) {
     for (var rampName in ramps) {
@@ -25,100 +24,99 @@ export function createTheme(
     ramps.neutral = ramps.neutral.domain([0, 7]);
   }
 
-  if (blend === undefined) {
-    blend = isLight ? 0.12 : 0.24;
-  }
+  let blend = isLight ? 0.12 : 0.24;
 
-  function rampColor(ramp: Scale, index: number): ColorToken {
+  function sample(ramp: Scale, index: number): ColorToken {
     return color(ramp(index).hex());
   }
+  const darkest = color(ramps.neutral(isLight ? 7 : 0).hex());
 
   const backgroundColor = {
     // Title bar
     100: {
-      base: rampColor(ramps.neutral, 1.25),
-      hovered: rampColor(ramps.neutral, 1.5),
-      active: rampColor(ramps.neutral, 1.75),
+      base: sample(ramps.neutral, 1.25),
+      hovered: sample(ramps.neutral, 1.5),
+      active: sample(ramps.neutral, 1.75),
     },
     // Midground (panels, etc)
     300: {
-      base: rampColor(ramps.neutral, 1),
-      hovered: rampColor(ramps.neutral, 1.25),
-      active: rampColor(ramps.neutral, 1.5),
+      base: sample(ramps.neutral, 1),
+      hovered: sample(ramps.neutral, 1.25),
+      active: sample(ramps.neutral, 1.5),
     },
     // Editor
     500: {
-      base: rampColor(ramps.neutral, 0),
-      hovered: rampColor(ramps.neutral, 0.25),
-      active: rampColor(ramps.neutral, 0.5),
+      base: sample(ramps.neutral, 0),
+      hovered: sample(ramps.neutral, 0.25),
+      active: sample(ramps.neutral, 0.5),
     },
     on300: {
-      base: rampColor(ramps.neutral, 0),
-      hovered: rampColor(ramps.neutral, 0.25),
-      active: rampColor(ramps.neutral, 0.5),
+      base: sample(ramps.neutral, 0),
+      hovered: sample(ramps.neutral, 0.25),
+      active: sample(ramps.neutral, 0.5),
     },
     on500: {
-      base: rampColor(ramps.neutral, 1.25),
-      hovered: rampColor(ramps.neutral, 1.5),
-      active: rampColor(ramps.neutral, 1.75),
+      base: sample(ramps.neutral, 1.25),
+      hovered: sample(ramps.neutral, 1.5),
+      active: sample(ramps.neutral, 1.75),
     },
     ok: {
-      base: withOpacity(rampColor(ramps.green, 0.5), 0.15),
-      hovered: withOpacity(rampColor(ramps.green, 0.5), 0.2),
-      active: withOpacity(rampColor(ramps.green, 0.5), 0.25),
+      base: withOpacity(sample(ramps.green, 0.5), 0.15),
+      hovered: withOpacity(sample(ramps.green, 0.5), 0.2),
+      active: withOpacity(sample(ramps.green, 0.5), 0.25),
     },
     error: {
-      base: withOpacity(rampColor(ramps.red, 0.5), 0.15),
-      hovered: withOpacity(rampColor(ramps.red, 0.5), 0.2),
-      active: withOpacity(rampColor(ramps.red, 0.5), 0.25),
+      base: withOpacity(sample(ramps.red, 0.5), 0.15),
+      hovered: withOpacity(sample(ramps.red, 0.5), 0.2),
+      active: withOpacity(sample(ramps.red, 0.5), 0.25),
     },
     warning: {
-      base: withOpacity(rampColor(ramps.yellow, 0.5), 0.15),
-      hovered: withOpacity(rampColor(ramps.yellow, 0.5), 0.2),
-      active: withOpacity(rampColor(ramps.yellow, 0.5), 0.25),
+      base: withOpacity(sample(ramps.yellow, 0.5), 0.15),
+      hovered: withOpacity(sample(ramps.yellow, 0.5), 0.2),
+      active: withOpacity(sample(ramps.yellow, 0.5), 0.25),
     },
     info: {
-      base: withOpacity(rampColor(ramps.blue, 0.5), 0.15),
-      hovered: withOpacity(rampColor(ramps.blue, 0.5), 0.2),
-      active: withOpacity(rampColor(ramps.blue, 0.5), 0.25),
+      base: withOpacity(sample(ramps.blue, 0.5), 0.15),
+      hovered: withOpacity(sample(ramps.blue, 0.5), 0.2),
+      active: withOpacity(sample(ramps.blue, 0.5), 0.25),
     },
   };
 
   const borderColor = {
-    primary: rampColor(ramps.neutral, isLight ? 1.5 : 0),
-    secondary: rampColor(ramps.neutral, isLight ? 1.25 : 1),
-    muted: rampColor(ramps.neutral, isLight ? 1 : 3),
-    active: rampColor(ramps.neutral, isLight ? 4 : 3),
-    onMedia: withOpacity(rampColor(ramps.neutral, 0), 0.1),
-    ok: withOpacity(rampColor(ramps.green, 0.5), 0.15),
-    error: withOpacity(rampColor(ramps.red, 0.5), 0.15),
-    warning: withOpacity(rampColor(ramps.yellow, 0.5), 0.15),
-    info: withOpacity(rampColor(ramps.blue, 0.5), 0.15),
+    primary: sample(ramps.neutral, isLight ? 1.5 : 0),
+    secondary: sample(ramps.neutral, isLight ? 1.25 : 1),
+    muted: sample(ramps.neutral, isLight ? 1 : 3),
+    active: sample(ramps.neutral, isLight ? 4 : 3),
+    onMedia: withOpacity(darkest, 0.1),
+    ok: withOpacity(sample(ramps.green, 0.5), 0.15),
+    error: withOpacity(sample(ramps.red, 0.5), 0.15),
+    warning: withOpacity(sample(ramps.yellow, 0.5), 0.15),
+    info: withOpacity(sample(ramps.blue, 0.5), 0.15),
   };
 
   const textColor = {
-    primary: rampColor(ramps.neutral, 6),
-    secondary: rampColor(ramps.neutral, 5),
-    muted: rampColor(ramps.neutral, 5),
-    placeholder: rampColor(ramps.neutral, 4),
-    active: rampColor(ramps.neutral, 7),
-    feature: rampColor(ramps.blue, 0.5),
-    ok: rampColor(ramps.green, 0.5),
-    error: rampColor(ramps.red, 0.5),
-    warning: rampColor(ramps.yellow, 0.5),
-    info: rampColor(ramps.blue, 0.5),
-    onMedia: rampColor(ramps.neutral, isLight ? 0 : 7),
+    primary: sample(ramps.neutral, 6),
+    secondary: sample(ramps.neutral, 5),
+    muted: sample(ramps.neutral, 5),
+    placeholder: sample(ramps.neutral, 4),
+    active: sample(ramps.neutral, 7),
+    feature: sample(ramps.blue, 0.5),
+    ok: sample(ramps.green, 0.5),
+    error: sample(ramps.red, 0.5),
+    warning: sample(ramps.yellow, 0.5),
+    info: sample(ramps.blue, 0.5),
+    onMedia: darkest,
   };
 
   const player = {
-    1: buildPlayer(rampColor(ramps.blue, 0.5)),
-    2: buildPlayer(rampColor(ramps.green, 0.5)),
-    3: buildPlayer(rampColor(ramps.magenta, 0.5)),
-    4: buildPlayer(rampColor(ramps.orange, 0.5)),
-    5: buildPlayer(rampColor(ramps.violet, 0.5)),
-    6: buildPlayer(rampColor(ramps.cyan, 0.5)),
-    7: buildPlayer(rampColor(ramps.red, 0.5)),
-    8: buildPlayer(rampColor(ramps.yellow, 0.5)),
+    1: buildPlayer(sample(ramps.blue, 0.5)),
+    2: buildPlayer(sample(ramps.green, 0.5)),
+    3: buildPlayer(sample(ramps.magenta, 0.5)),
+    4: buildPlayer(sample(ramps.orange, 0.5)),
+    5: buildPlayer(sample(ramps.violet, 0.5)),
+    6: buildPlayer(sample(ramps.cyan, 0.5)),
+    7: buildPlayer(sample(ramps.red, 0.5)),
+    8: buildPlayer(sample(ramps.yellow, 0.5)),
   };
 
   const editor = {
@@ -126,16 +124,16 @@ export function createTheme(
     indent_guide: borderColor.muted,
     indent_guide_active: borderColor.secondary,
     line: {
-      active: rampColor(ramps.neutral, 1),
-      highlighted: rampColor(ramps.neutral, 1.25), // TODO: Where is this used?
+      active: sample(ramps.neutral, 1),
+      highlighted: sample(ramps.neutral, 1.25), // TODO: Where is this used?
     },
     highlight: {
       selection: player[1].selectionColor,
-      occurrence: withOpacity(rampColor(ramps.neutral, 3.5), blend),
-      activeOccurrence: withOpacity(rampColor(ramps.neutral, 3.5), blend * 2), // TODO: Not hooked up - https://github.com/zed-industries/zed/issues/751
+      occurrence: withOpacity(sample(ramps.neutral, 3.5), blend),
+      activeOccurrence: withOpacity(sample(ramps.neutral, 3.5), blend * 2), // TODO: Not hooked up - https://github.com/zed-industries/zed/issues/751
       matchingBracket: backgroundColor[500].active, // TODO: Not hooked up
-      match: rampColor(ramps.violet, 0.15),
-      activeMatch: withOpacity(rampColor(ramps.violet, 0.4), blend * 2), // TODO: Not hooked up - https://github.com/zed-industries/zed/issues/751
+      match: sample(ramps.violet, 0.15),
+      activeMatch: withOpacity(sample(ramps.violet, 0.4), blend * 2), // TODO: Not hooked up - https://github.com/zed-industries/zed/issues/751
       related: backgroundColor[500].hovered,
     },
     gutter: {
@@ -146,59 +144,59 @@ export function createTheme(
 
   const syntax: Syntax = {
     primary: {
-      color: rampColor(ramps.neutral, 7),
+      color: sample(ramps.neutral, 7),
       weight: fontWeights.normal,
     },
     comment: {
-      color: rampColor(ramps.neutral, 5),
+      color: sample(ramps.neutral, 5),
       weight: fontWeights.normal,
     },
     punctuation: {
-      color: rampColor(ramps.neutral, 6),
+      color: sample(ramps.neutral, 6),
       weight: fontWeights.normal,
     },
     constant: {
-      color: rampColor(ramps.neutral, 4),
+      color: sample(ramps.neutral, 4),
       weight: fontWeights.normal,
     },
     keyword: {
-      color: rampColor(ramps.blue, 0.5),
+      color: sample(ramps.blue, 0.5),
       weight: fontWeights.normal,
     },
     function: {
-      color: rampColor(ramps.yellow, 0.5),
+      color: sample(ramps.yellow, 0.5),
       weight: fontWeights.normal,
     },
     type: {
-      color: rampColor(ramps.cyan, 0.5),
+      color: sample(ramps.cyan, 0.5),
       weight: fontWeights.normal,
     },
     variant: {
-      color: rampColor(ramps.blue, 0.5),
+      color: sample(ramps.blue, 0.5),
       weight: fontWeights.normal,
     },
     property: {
-      color: rampColor(ramps.blue, 0.5),
+      color: sample(ramps.blue, 0.5),
       weight: fontWeights.normal,
     },
     enum: {
-      color: rampColor(ramps.orange, 0.5),
+      color: sample(ramps.orange, 0.5),
       weight: fontWeights.normal,
     },
     operator: {
-      color: rampColor(ramps.orange, 0.5),
+      color: sample(ramps.orange, 0.5),
       weight: fontWeights.normal,
     },
     string: {
-      color: rampColor(ramps.orange, 0.5),
+      color: sample(ramps.orange, 0.5),
       weight: fontWeights.normal,
     },
     number: {
-      color: rampColor(ramps.green, 0.5),
+      color: sample(ramps.green, 0.5),
       weight: fontWeights.normal,
     },
     boolean: {
-      color: rampColor(ramps.green, 0.5),
+      color: sample(ramps.green, 0.5),
       weight: fontWeights.normal,
     },
     predictive: {
@@ -206,7 +204,7 @@ export function createTheme(
       weight: fontWeights.normal,
     },
     title: {
-      color: rampColor(ramps.yellow, 0.5),
+      color: sample(ramps.yellow, 0.5),
       weight: fontWeights.bold,
     },
     emphasis: {
@@ -218,21 +216,20 @@ export function createTheme(
       weight: fontWeights.bold,
     },
     linkUri: {
-      color: rampColor(ramps.green, 0.5),
+      color: sample(ramps.green, 0.5),
       weight: fontWeights.normal,
       underline: true,
     },
     linkText: {
-      color: rampColor(ramps.orange, 0.5),
+      color: sample(ramps.orange, 0.5),
       weight: fontWeights.normal,
       italic: true,
     },
   };
 
-  const shadowAlpha: NumberToken = {
-    value: blend,
-    type: "number",
-  };
+  const shadow = withOpacity(
+    color(ramps.neutral(isLight ? 7 : 0).darken().hex()),
+    blend);
 
   return {
     name,
@@ -243,6 +240,6 @@ export function createTheme(
     editor,
     syntax,
     player,
-    shadowAlpha,
+    shadow,
   };
 }

styles/src/themes/common/theme.ts 🔗

@@ -153,6 +153,6 @@ export default interface Theme {
     6: Player;
     7: Player;
     8: Player;
-  };
-  shadowAlpha: NumberToken;
+  },
+  shadow: ColorToken;
 }