Create common base16 theme constructor

Nate Butler , Keith Simmons , and Max Brunsfeld created

Co-Authored-By: Keith Simmons <keith@the-simmons.net>
Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>

Change summary

assets/themes/cave-dark.json           |  12 
assets/themes/cave-light.json          |  16 
assets/themes/solarized-dark.json      |  12 
assets/themes/solarized-light.json     |  16 
assets/themes/sulphurpool-dark.json    |  12 
assets/themes/sulphurpool-light.json   |  16 
styles/src/buildThemes.ts              |   9 
styles/src/buildTokens.ts              |   4 
styles/src/themes/base16.ts            | 242 +++++++++++++++++++++++++
styles/src/themes/cave-dark.ts         |   3 
styles/src/themes/cave-light.ts        |   3 
styles/src/themes/cave.ts              | 262 +--------------------------
styles/src/themes/solarized-dark.ts    |   3 
styles/src/themes/solarized-light.ts   |   3 
styles/src/themes/solarized.ts         | 262 +--------------------------
styles/src/themes/sulphurpool-dark.ts  |   3 
styles/src/themes/sulphurpool-light.ts |   3 
styles/src/themes/sulphurpool.ts       | 262 +--------------------------
18 files changed, 339 insertions(+), 804 deletions(-)

Detailed changes

assets/themes/cave-dark.json 🔗

@@ -42,7 +42,7 @@
         "weight": "bold",
         "size": 14
       },
-      "background": "#655f6d"
+      "background": "#5852607a"
     },
     "border": {
       "color": "#19171c",
@@ -837,7 +837,7 @@
     },
     "hovered_entry": {
       "height": 22,
-      "background": "#655f6d",
+      "background": "#58526052",
       "icon_color": "#8b8792",
       "icon_size": 8,
       "icon_spacing": 8,
@@ -860,7 +860,7 @@
     },
     "hovered_selected_entry": {
       "height": 22,
-      "background": "#655f6d",
+      "background": "#58526052",
       "icon_color": "#8b8792",
       "icon_size": 8,
       "icon_spacing": 8,
@@ -945,7 +945,7 @@
             "right": 8
           }
         },
-        "background": "#655f6d",
+        "background": "#58526052",
         "corner_radius": 6
       },
       "active_item": {
@@ -980,7 +980,7 @@
             "right": 8
           }
         },
-        "background": "#655f6d",
+        "background": "#58526052",
         "corner_radius": 6
       },
       "menu": {
@@ -1170,7 +1170,7 @@
       "padding": {
         "left": 8
       },
-      "background": "#655f6d",
+      "background": "#58526052",
       "corner_radius": 6
     },
     "unshared_project": {

assets/themes/cave-light.json 🔗

@@ -42,7 +42,7 @@
         "weight": "bold",
         "size": 14
       },
-      "background": "#7e7887"
+      "background": "#8b87922e"
     },
     "border": {
       "color": "#efecf4",
@@ -91,7 +91,7 @@
     },
     "shadow": {
       "blur": 16,
-      "color": "#00000052",
+      "color": "#0000001f",
       "offset": [
         0,
         2
@@ -837,7 +837,7 @@
     },
     "hovered_entry": {
       "height": 22,
-      "background": "#7e7887",
+      "background": "#8b87921f",
       "icon_color": "#585260",
       "icon_size": 8,
       "icon_spacing": 8,
@@ -860,7 +860,7 @@
     },
     "hovered_selected_entry": {
       "height": 22,
-      "background": "#7e7887",
+      "background": "#8b87921f",
       "icon_color": "#585260",
       "icon_size": 8,
       "icon_spacing": 8,
@@ -945,7 +945,7 @@
             "right": 8
           }
         },
-        "background": "#7e7887",
+        "background": "#8b87921f",
         "corner_radius": 6
       },
       "active_item": {
@@ -980,7 +980,7 @@
             "right": 8
           }
         },
-        "background": "#7e7887",
+        "background": "#8b87921f",
         "corner_radius": 6
       },
       "menu": {
@@ -993,7 +993,7 @@
         },
         "shadow": {
           "blur": 16,
-          "color": "#00000052",
+          "color": "#0000001f",
           "offset": [
             0,
             2
@@ -1170,7 +1170,7 @@
       "padding": {
         "left": 8
       },
-      "background": "#7e7887",
+      "background": "#8b87921f",
       "corner_radius": 6
     },
     "unshared_project": {

assets/themes/solarized-dark.json 🔗

@@ -42,7 +42,7 @@
         "weight": "bold",
         "size": 14
       },
-      "background": "#657b83"
+      "background": "#586e757a"
     },
     "border": {
       "color": "#002b36",
@@ -837,7 +837,7 @@
     },
     "hovered_entry": {
       "height": 22,
-      "background": "#657b83",
+      "background": "#586e7552",
       "icon_color": "#93a1a1",
       "icon_size": 8,
       "icon_spacing": 8,
@@ -860,7 +860,7 @@
     },
     "hovered_selected_entry": {
       "height": 22,
-      "background": "#657b83",
+      "background": "#586e7552",
       "icon_color": "#93a1a1",
       "icon_size": 8,
       "icon_spacing": 8,
@@ -945,7 +945,7 @@
             "right": 8
           }
         },
-        "background": "#657b83",
+        "background": "#586e7552",
         "corner_radius": 6
       },
       "active_item": {
@@ -980,7 +980,7 @@
             "right": 8
           }
         },
-        "background": "#657b83",
+        "background": "#586e7552",
         "corner_radius": 6
       },
       "menu": {
@@ -1170,7 +1170,7 @@
       "padding": {
         "left": 8
       },
-      "background": "#657b83",
+      "background": "#586e7552",
       "corner_radius": 6
     },
     "unshared_project": {

assets/themes/solarized-light.json 🔗

@@ -42,7 +42,7 @@
         "weight": "bold",
         "size": 14
       },
-      "background": "#839496"
+      "background": "#93a1a12e"
     },
     "border": {
       "color": "#fdf6e3",
@@ -91,7 +91,7 @@
     },
     "shadow": {
       "blur": 16,
-      "color": "#00000052",
+      "color": "#0000001f",
       "offset": [
         0,
         2
@@ -837,7 +837,7 @@
     },
     "hovered_entry": {
       "height": 22,
-      "background": "#839496",
+      "background": "#93a1a11f",
       "icon_color": "#586e75",
       "icon_size": 8,
       "icon_spacing": 8,
@@ -860,7 +860,7 @@
     },
     "hovered_selected_entry": {
       "height": 22,
-      "background": "#839496",
+      "background": "#93a1a11f",
       "icon_color": "#586e75",
       "icon_size": 8,
       "icon_spacing": 8,
@@ -945,7 +945,7 @@
             "right": 8
           }
         },
-        "background": "#839496",
+        "background": "#93a1a11f",
         "corner_radius": 6
       },
       "active_item": {
@@ -980,7 +980,7 @@
             "right": 8
           }
         },
-        "background": "#839496",
+        "background": "#93a1a11f",
         "corner_radius": 6
       },
       "menu": {
@@ -993,7 +993,7 @@
         },
         "shadow": {
           "blur": 16,
-          "color": "#00000052",
+          "color": "#0000001f",
           "offset": [
             0,
             2
@@ -1170,7 +1170,7 @@
       "padding": {
         "left": 8
       },
-      "background": "#839496",
+      "background": "#93a1a11f",
       "corner_radius": 6
     },
     "unshared_project": {

assets/themes/sulphurpool-dark.json 🔗

@@ -42,7 +42,7 @@
         "weight": "bold",
         "size": 14
       },
-      "background": "#6b7394"
+      "background": "#5e66877a"
     },
     "border": {
       "color": "#202746",
@@ -837,7 +837,7 @@
     },
     "hovered_entry": {
       "height": 22,
-      "background": "#6b7394",
+      "background": "#5e668752",
       "icon_color": "#979db4",
       "icon_size": 8,
       "icon_spacing": 8,
@@ -860,7 +860,7 @@
     },
     "hovered_selected_entry": {
       "height": 22,
-      "background": "#6b7394",
+      "background": "#5e668752",
       "icon_color": "#979db4",
       "icon_size": 8,
       "icon_spacing": 8,
@@ -945,7 +945,7 @@
             "right": 8
           }
         },
-        "background": "#6b7394",
+        "background": "#5e668752",
         "corner_radius": 6
       },
       "active_item": {
@@ -980,7 +980,7 @@
             "right": 8
           }
         },
-        "background": "#6b7394",
+        "background": "#5e668752",
         "corner_radius": 6
       },
       "menu": {
@@ -1170,7 +1170,7 @@
       "padding": {
         "left": 8
       },
-      "background": "#6b7394",
+      "background": "#5e668752",
       "corner_radius": 6
     },
     "unshared_project": {

assets/themes/sulphurpool-light.json 🔗

@@ -42,7 +42,7 @@
         "weight": "bold",
         "size": 14
       },
-      "background": "#898ea4"
+      "background": "#979db42e"
     },
     "border": {
       "color": "#f5f7ff",
@@ -91,7 +91,7 @@
     },
     "shadow": {
       "blur": 16,
-      "color": "#00000052",
+      "color": "#0000001f",
       "offset": [
         0,
         2
@@ -837,7 +837,7 @@
     },
     "hovered_entry": {
       "height": 22,
-      "background": "#898ea4",
+      "background": "#979db41f",
       "icon_color": "#5e6687",
       "icon_size": 8,
       "icon_spacing": 8,
@@ -860,7 +860,7 @@
     },
     "hovered_selected_entry": {
       "height": 22,
-      "background": "#898ea4",
+      "background": "#979db41f",
       "icon_color": "#5e6687",
       "icon_size": 8,
       "icon_spacing": 8,
@@ -945,7 +945,7 @@
             "right": 8
           }
         },
-        "background": "#898ea4",
+        "background": "#979db41f",
         "corner_radius": 6
       },
       "active_item": {
@@ -980,7 +980,7 @@
             "right": 8
           }
         },
-        "background": "#898ea4",
+        "background": "#979db41f",
         "corner_radius": 6
       },
       "menu": {
@@ -993,7 +993,7 @@
         },
         "shadow": {
           "blur": 16,
-          "color": "#00000052",
+          "color": "#0000001f",
           "offset": [
             0,
             2
@@ -1170,7 +1170,7 @@
       "padding": {
         "left": 8
       },
-      "background": "#898ea4",
+      "background": "#979db41f",
       "corner_radius": 6
     },
     "unshared_project": {

styles/src/buildThemes.ts 🔗

@@ -1,14 +1,11 @@
 import * as fs from "fs";
 import * as path from "path";
 import app from "./styleTree/app";
-import caveDark from "./themes/cave-dark";
-import caveLight from "./themes/cave-light";
+import { dark as caveDark, light as caveLight } from "./themes/cave";
 import dark from "./themes/dark";
 import light from "./themes/light";
-import solarizedDark from "./themes/solarized-dark";
-import solarizedLight from "./themes/solarized-light";
-import sulphurpoolDark from "./themes/sulphurpool-dark";
-import sulphurpoolLight from "./themes/sulphurpool-light";
+import { dark as solarizedDark, light as solarizedLight } from "./themes/solarized";
+import { dark as sulphurpoolDark, light as sulphurpoolLight } from "./themes/sulphurpool";
 import snakeCase from "./utils/snakeCase";
 
 const themes = [

styles/src/buildTokens.ts 🔗

@@ -2,8 +2,6 @@ import * as fs from "fs";
 import * as path from "path";
 import dark from "./themes/dark";
 import light from "./themes/light";
-import solarizedDark from "./themes/solarized-dark";
-import solarizedLight from "./themes/solarized-light";
 import Theme from "./themes/theme";
 import { colors, fontFamilies, fontSizes, fontWeights } from "./tokens";
 
@@ -98,7 +96,7 @@ combinedTokens.core = coreTokens;
 
 // Add each theme to the combined tokens and write ${theme}.json.
 // We write `${theme}.json` as a separate file for the design team's convenience, but it isn't consumed by Figma Tokens directly.
-let themes = [dark, light, solarizedDark, solarizedLight];
+let themes = [dark, light];
 themes.forEach((theme) => {
   const themePath = `${distPath}/${theme.name}.json`
   fs.writeFileSync(themePath, JSON.stringify(themeTokens(theme), null, 2));

styles/src/themes/base16.ts 🔗

@@ -0,0 +1,242 @@
+import { ColorToken, fontWeights, NumberToken } from "../tokens";
+import { withOpacity } from "../utils/color";
+import Theme, { buildPlayer, Syntax } from "./theme";
+
+export interface Accents {
+  "red": ColorToken,
+  "orange": ColorToken,
+  "yellow": ColorToken,
+  "green": ColorToken,
+  "cyan": ColorToken,
+  "blue": ColorToken,
+  "violet": ColorToken,
+  "magenta": ColorToken,
+}
+
+export function createTheme(name: string, isLight: boolean, neutral: ColorToken[], accent: Accents): Theme {
+  if (isLight) {
+    neutral = [...neutral].reverse();
+  }
+  let blend = isLight ? 0.12 : 0.32;
+
+  const backgroundColor = {
+    100: {
+      base: neutral[1],
+      hovered: withOpacity(neutral[2], blend),
+      active: withOpacity(neutral[2], blend * 1.5),
+      focused: neutral[2],
+    },
+    300: {
+      base: neutral[1],
+      hovered: withOpacity(neutral[2], blend),
+      active: withOpacity(neutral[2], blend * 1.5),
+      focused: neutral[2],
+    },
+    500: {
+      base: neutral[0],
+      hovered: neutral[1],
+      active: neutral[1],
+      focused: neutral[1],
+    },
+    on300: {
+      base: neutral[0],
+      hovered: neutral[1],
+      active: neutral[1],
+      focused: neutral[1],
+    },
+    on500: {
+      base: neutral[1],
+      hovered: neutral[3],
+      active: neutral[3],
+      focused: neutral[3],
+    },
+    ok: {
+      base: accent.green,
+      hovered: accent.green,
+      active: accent.green,
+      focused: accent.green,
+    },
+    error: {
+      base: accent.red,
+      hovered: accent.red,
+      active: accent.red,
+      focused: accent.red,
+    },
+    warning: {
+      base: accent.yellow,
+      hovered: accent.yellow,
+      active: accent.yellow,
+      focused: accent.yellow,
+    },
+    info: {
+      base: accent.blue,
+      hovered: accent.blue,
+      active: accent.blue,
+      focused: accent.blue,
+    },
+  };
+
+  const borderColor = {
+    primary: neutral[0],
+    secondary: neutral[1],
+    muted: neutral[3],
+    focused: neutral[3],
+    active: neutral[3],
+    ok: accent.green,
+    error: accent.red,
+    warning: accent.yellow,
+    info: accent.blue,
+  };
+
+  const textColor = {
+    primary: neutral[6],
+    secondary: neutral[5],
+    muted: neutral[5],
+    placeholder: neutral[4],
+    active: neutral[7],
+    feature: accent.blue,
+    ok: accent.green,
+    error: accent.red,
+    warning: accent.yellow,
+    info: accent.blue,
+  };
+
+  const player = {
+    1: buildPlayer(accent.blue),
+    2: buildPlayer(accent.green),
+    3: buildPlayer(accent.magenta),
+    4: buildPlayer(accent.orange),
+    5: buildPlayer(accent.violet),
+    6: buildPlayer(accent.cyan),
+    7: buildPlayer(accent.red),
+    8: buildPlayer(accent.yellow),
+  };
+
+  const editor = {
+    background: backgroundColor[500].base,
+    indent_guide: borderColor.muted,
+    indent_guide_active: borderColor.secondary,
+    line: {
+      active: withOpacity(neutral[7], 0.07),
+      highlighted: withOpacity(neutral[7], 0.12),
+      inserted: backgroundColor.ok.active,
+      deleted: backgroundColor.error.active,
+      modified: backgroundColor.info.active,
+    },
+    highlight: {
+      selection: player[1].selectionColor,
+      occurrence: withOpacity(neutral[0], 0.12),
+      activeOccurrence: withOpacity(neutral[0], 0.16),
+      matchingBracket: backgroundColor[500].active,
+      match: withOpacity(accent.violet, 0.5),
+      activeMatch: withOpacity(accent.violet, 0.7),
+      related: backgroundColor[500].focused,
+    },
+    gutter: {
+      primary: textColor.placeholder,
+      active: textColor.active,
+    },
+  };
+
+  const syntax: Syntax = {
+    primary: {
+      color: neutral[7],
+      weight: fontWeights.normal,
+    },
+    comment: {
+      color: neutral[5],
+      weight: fontWeights.normal,
+    },
+    punctuation: {
+      color: neutral[5],
+      weight: fontWeights.normal,
+    },
+    constant: {
+      color: neutral[4],
+      weight: fontWeights.normal,
+    },
+    keyword: {
+      color: accent.blue,
+      weight: fontWeights.normal,
+    },
+    function: {
+      color: accent.yellow,
+      weight: fontWeights.normal,
+    },
+    type: {
+      color: accent.cyan,
+      weight: fontWeights.normal,
+    },
+    variant: {
+      color: accent.blue,
+      weight: fontWeights.normal,
+    },
+    property: {
+      color: accent.blue,
+      weight: fontWeights.normal,
+    },
+    enum: {
+      color: accent.orange,
+      weight: fontWeights.normal,
+    },
+    operator: {
+      color: accent.orange,
+      weight: fontWeights.normal,
+    },
+    string: {
+      color: accent.orange,
+      weight: fontWeights.normal,
+    },
+    number: {
+      color: accent.green,
+      weight: fontWeights.normal,
+    },
+    boolean: {
+      color: accent.green,
+      weight: fontWeights.normal,
+    },
+    predictive: {
+      color: textColor.muted,
+      weight: fontWeights.normal,
+    },
+    title: {
+      color: accent.yellow,
+      weight: fontWeights.bold,
+    },
+    emphasis: {
+      color: textColor.feature,
+      weight: fontWeights.normal,
+    },
+    "emphasis.strong": {
+      color: textColor.feature,
+      weight: fontWeights.bold,
+    },
+    linkUri: {
+      color: accent.green,
+      weight: fontWeights.normal,
+      underline: true,
+    },
+    linkText: {
+      color: accent.orange,
+      weight: fontWeights.normal,
+      italic: true,
+    },
+  };
+
+  const shadowAlpha: NumberToken = {
+    value: blend,
+    type: "number",
+  };
+
+  return {
+    name,
+    backgroundColor,
+    borderColor,
+    textColor,
+    iconColor: textColor,
+    editor,
+    syntax,
+    player,
+    shadowAlpha,
+  };
+}

styles/src/themes/cave.ts 🔗

@@ -1,21 +1,18 @@
-import { color, fontWeights, NumberToken } from "../tokens";
-import { withOpacity } from "../utils/color";
-import Theme, { buildPlayer, Syntax } from "./theme";
-
-// Dark: 0 == Darkest, 3 == Lightest
-const dark = {
-  0: color("#19171c"),
-  1: color("#26232a"),
-  2: color("#585260"),
-  3: color("#655f6d"),
-};
-// Light: 0 == Lightest, 3 == Darkest
-const light = {
-  0: color("#efecf4"),
-  1: color("#e2dfe7"),
-  2: color("#8b8792"),
-  3: color("#7e7887"),
-};
+import { createTheme } from "./base16";
+import { color } from "../tokens";
+
+const name = "cave";
+
+const neutrals = [
+  color("#19171c"),
+  color("#26232a"),
+  color("#585260"),
+  color("#655f6d"),
+  color("#7e7887"),
+  color("#8b8792"),
+  color("#e2dfe7"),
+  color("#efecf4"),
+];
 
 const colors = {
   "red": color("#be4678"),
@@ -28,230 +25,5 @@ const colors = {
   "magenta": color("#bf40bf"),
 };
 
-export function cave(darkTheme: boolean): Theme {
-  let fg = darkTheme ? light : dark;
-  let bg = darkTheme ? dark : light;
-  let name = darkTheme ? "cave-dark" : "cave-light";
-
-  const backgroundColor = {
-    100: {
-      base: bg[1],
-      hovered: bg[3],
-      active: bg[3],
-      focused: bg[3],
-    },
-    300: {
-      base: bg[1],
-      hovered: bg[3],
-      active: bg[3],
-      focused: bg[3],
-    },
-    500: {
-      base: bg[0],
-      hovered: bg[1],
-      active: bg[1],
-      focused: bg[1],
-    },
-    on300: {
-      base: bg[0],
-      hovered: bg[1],
-      active: bg[1],
-      focused: bg[1],
-    },
-    on500: {
-      base: bg[1],
-      hovered: bg[3],
-      active: bg[3],
-      focused: bg[3],
-    },
-    ok: {
-      base: colors.green,
-      hovered: colors.green,
-      active: colors.green,
-      focused: colors.green,
-    },
-    error: {
-      base: colors.red,
-      hovered: colors.red,
-      active: colors.red,
-      focused: colors.red,
-    },
-    warning: {
-      base: colors.yellow,
-      hovered: colors.yellow,
-      active: colors.yellow,
-      focused: colors.yellow,
-    },
-    info: {
-      base: colors.blue,
-      hovered: colors.blue,
-      active: colors.blue,
-      focused: colors.blue,
-    },
-  };
-
-  const borderColor = {
-    primary: bg[0],
-    secondary: bg[1],
-    muted: bg[3],
-    focused: bg[3],
-    active: bg[3],
-    ok: colors.green,
-    error: colors.red,
-    warning: colors.yellow,
-    info: colors.blue,
-  };
-
-  const textColor = {
-    primary: fg[1],
-    secondary: fg[2],
-    muted: fg[2],
-    placeholder: fg[3],
-    active: fg[0],
-    //TODO: (design) define feature and it's correct value
-    feature: colors.blue,
-    ok: colors.green,
-    error: colors.red,
-    warning: colors.yellow,
-    info: colors.blue,
-  };
-
-  const player = {
-    1: buildPlayer(colors.blue),
-    2: buildPlayer(colors.green),
-    3: buildPlayer(colors.magenta),
-    4: buildPlayer(colors.orange),
-    5: buildPlayer(colors.violet),
-    6: buildPlayer(colors.cyan),
-    7: buildPlayer(colors.red),
-    8: buildPlayer(colors.yellow),
-  };
-
-  const editor = {
-    background: backgroundColor[500].base,
-    indent_guide: borderColor.muted,
-    indent_guide_active: borderColor.secondary,
-    line: {
-      active: withOpacity(fg[0], 0.07),
-      highlighted: withOpacity(fg[0], 0.12),
-      inserted: backgroundColor.ok.active,
-      deleted: backgroundColor.error.active,
-      modified: backgroundColor.info.active,
-    },
-    highlight: {
-      selection: player[1].selectionColor,
-      occurrence: withOpacity(bg[0], 0.12),
-      activeOccurrence: withOpacity(bg[0], 0.16), // TODO: This is not correctly hooked up to occurences on the rust side
-      matchingBracket: backgroundColor[500].active,
-      match: withOpacity(colors.violet, 0.5),
-      activeMatch: withOpacity(colors.violet, 0.7),
-      related: backgroundColor[500].focused,
-    },
-    gutter: {
-      primary: textColor.placeholder,
-      active: textColor.active,
-    },
-  };
-
-  const syntax: Syntax = {
-    primary: {
-      color: fg[0],
-      weight: fontWeights.normal,
-    },
-    comment: {
-      color: fg[2],
-      weight: fontWeights.normal,
-    },
-    punctuation: {
-      color: fg[2],
-      weight: fontWeights.normal,
-    },
-    constant: {
-      color: fg[3],
-      weight: fontWeights.normal,
-    },
-    keyword: {
-      color: colors.blue,
-      weight: fontWeights.normal,
-    },
-    function: {
-      color: colors.yellow,
-      weight: fontWeights.normal,
-    },
-    type: {
-      color: colors.cyan,
-      weight: fontWeights.normal,
-    },
-    variant: {
-      color: colors.blue,
-      weight: fontWeights.normal,
-    },
-    property: {
-      color: colors.blue,
-      weight: fontWeights.normal,
-    },
-    enum: {
-      color: colors.orange,
-      weight: fontWeights.normal,
-    },
-    operator: {
-      color: colors.orange,
-      weight: fontWeights.normal,
-    },
-    string: {
-      color: colors.orange,
-      weight: fontWeights.normal,
-    },
-    number: {
-      color: colors.green,
-      weight: fontWeights.normal,
-    },
-    boolean: {
-      color: colors.green,
-      weight: fontWeights.normal,
-    },
-    predictive: {
-      color: textColor.muted,
-      weight: fontWeights.normal,
-    },
-    title: {
-      color: colors.yellow,
-      weight: fontWeights.bold,
-    },
-    emphasis: {
-      color: textColor.feature,
-      weight: fontWeights.normal,
-    },
-    "emphasis.strong": {
-      color: textColor.feature,
-      weight: fontWeights.bold,
-    },
-    linkUri: {
-      color: colors.green,
-      weight: fontWeights.normal,
-      underline: true,
-    },
-    linkText: {
-      color: colors.orange,
-      weight: fontWeights.normal,
-      italic: true,
-    },
-  };
-
-  const shadowAlpha: NumberToken = {
-    value: 0.32,
-    type: "number",
-  };
-
-  return {
-    name,
-    backgroundColor,
-    borderColor,
-    textColor,
-    iconColor: textColor,
-    editor,
-    syntax,
-    player,
-    shadowAlpha,
-  };
-}
+export const dark = createTheme(`${name}-dark`, false, neutrals, colors);
+export const light = createTheme(`${name}-light`, true, neutrals, colors);

styles/src/themes/solarized.ts 🔗

@@ -1,21 +1,18 @@
-import { color, fontWeights, NumberToken } from "../tokens";
-import { withOpacity } from "../utils/color";
-import Theme, { buildPlayer, Syntax } from "./theme";
-
-// Dark: 0 == Darkest, 3 == Lightest
-const dark = {
-  0: color("#002b36"),
-  1: color("#073642"),
-  2: color("#586e75"),
-  3: color("#657b83"),
-};
-// Light: 0 == Lightest, 3 == Darkest
-const light = {
-  0: color("#fdf6e3"),
-  1: color("#eee8d5"),
-  2: color("#93a1a1"),
-  3: color("#839496"),
-};
+import { createTheme } from "./base16";
+import { color } from "../tokens";
+
+const name = "solarized";
+
+const neutrals = [
+  color("#002b36"),
+  color("#073642"),
+  color("#586e75"),
+  color("#657b83"),
+  color("#839496"),
+  color("#93a1a1"),
+  color("#eee8d5"),
+  color("#fdf6e3"),
+];
 
 const colors = {
   "red": color("#dc322f"),
@@ -28,230 +25,5 @@ const colors = {
   "magenta": color("#d33682"),
 };
 
-export function solarized(darkTheme: boolean): Theme {
-  let fg = darkTheme ? light : dark;
-  let bg = darkTheme ? dark : light;
-  let name = darkTheme ? "solarized-dark" : "solarized-light";
-
-  const backgroundColor = {
-    100: {
-      base: bg[1],
-      hovered: bg[3],
-      active: bg[3],
-      focused: bg[3],
-    },
-    300: {
-      base: bg[1],
-      hovered: bg[3],
-      active: bg[3],
-      focused: bg[3],
-    },
-    500: {
-      base: bg[0],
-      hovered: bg[1],
-      active: bg[1],
-      focused: bg[1],
-    },
-    on300: {
-      base: bg[0],
-      hovered: bg[1],
-      active: bg[1],
-      focused: bg[1],
-    },
-    on500: {
-      base: bg[1],
-      hovered: bg[3],
-      active: bg[3],
-      focused: bg[3],
-    },
-    ok: {
-      base: colors.green,
-      hovered: colors.green,
-      active: colors.green,
-      focused: colors.green,
-    },
-    error: {
-      base: colors.red,
-      hovered: colors.red,
-      active: colors.red,
-      focused: colors.red,
-    },
-    warning: {
-      base: colors.yellow,
-      hovered: colors.yellow,
-      active: colors.yellow,
-      focused: colors.yellow,
-    },
-    info: {
-      base: colors.blue,
-      hovered: colors.blue,
-      active: colors.blue,
-      focused: colors.blue,
-    },
-  };
-
-  const borderColor = {
-    primary: bg[0],
-    secondary: bg[1],
-    muted: bg[3],
-    focused: bg[3],
-    active: bg[3],
-    ok: colors.green,
-    error: colors.red,
-    warning: colors.yellow,
-    info: colors.blue,
-  };
-
-  const textColor = {
-    primary: fg[1],
-    secondary: fg[2],
-    muted: fg[2],
-    placeholder: fg[3],
-    active: fg[0],
-    //TODO: (design) define feature and it's correct value
-    feature: colors.blue,
-    ok: colors.green,
-    error: colors.red,
-    warning: colors.yellow,
-    info: colors.blue,
-  };
-
-  const player = {
-    1: buildPlayer(colors.blue),
-    2: buildPlayer(colors.green),
-    3: buildPlayer(colors.magenta),
-    4: buildPlayer(colors.orange),
-    5: buildPlayer(colors.violet),
-    6: buildPlayer(colors.cyan),
-    7: buildPlayer(colors.red),
-    8: buildPlayer(colors.yellow),
-  };
-
-  const editor = {
-    background: backgroundColor[500].base,
-    indent_guide: borderColor.muted,
-    indent_guide_active: borderColor.secondary,
-    line: {
-      active: withOpacity(fg[0], 0.07),
-      highlighted: withOpacity(fg[0], 0.12),
-      inserted: backgroundColor.ok.active,
-      deleted: backgroundColor.error.active,
-      modified: backgroundColor.info.active,
-    },
-    highlight: {
-      selection: player[1].selectionColor,
-      occurrence: withOpacity(bg[0], 0.12),
-      activeOccurrence: withOpacity(bg[0], 0.16), // TODO: This is not correctly hooked up to occurences on the rust side
-      matchingBracket: backgroundColor[500].active,
-      match: withOpacity(colors.violet, 0.5),
-      activeMatch: withOpacity(colors.violet, 0.7),
-      related: backgroundColor[500].focused,
-    },
-    gutter: {
-      primary: textColor.placeholder,
-      active: textColor.active,
-    },
-  };
-
-  const syntax: Syntax = {
-    primary: {
-      color: fg[0],
-      weight: fontWeights.normal,
-    },
-    comment: {
-      color: fg[2],
-      weight: fontWeights.normal,
-    },
-    punctuation: {
-      color: fg[2],
-      weight: fontWeights.normal,
-    },
-    constant: {
-      color: fg[3],
-      weight: fontWeights.normal,
-    },
-    keyword: {
-      color: colors.blue,
-      weight: fontWeights.normal,
-    },
-    function: {
-      color: colors.yellow,
-      weight: fontWeights.normal,
-    },
-    type: {
-      color: colors.cyan,
-      weight: fontWeights.normal,
-    },
-    variant: {
-      color: colors.blue,
-      weight: fontWeights.normal,
-    },
-    property: {
-      color: colors.blue,
-      weight: fontWeights.normal,
-    },
-    enum: {
-      color: colors.orange,
-      weight: fontWeights.normal,
-    },
-    operator: {
-      color: colors.orange,
-      weight: fontWeights.normal,
-    },
-    string: {
-      color: colors.orange,
-      weight: fontWeights.normal,
-    },
-    number: {
-      color: colors.green,
-      weight: fontWeights.normal,
-    },
-    boolean: {
-      color: colors.green,
-      weight: fontWeights.normal,
-    },
-    predictive: {
-      color: textColor.muted,
-      weight: fontWeights.normal,
-    },
-    title: {
-      color: colors.yellow,
-      weight: fontWeights.bold,
-    },
-    emphasis: {
-      color: textColor.feature,
-      weight: fontWeights.normal,
-    },
-    "emphasis.strong": {
-      color: textColor.feature,
-      weight: fontWeights.bold,
-    },
-    linkUri: {
-      color: colors.green,
-      weight: fontWeights.normal,
-      underline: true,
-    },
-    linkText: {
-      color: colors.orange,
-      weight: fontWeights.normal,
-      italic: true,
-    },
-  };
-
-  const shadowAlpha: NumberToken = {
-    value: 0.32,
-    type: "number",
-  };
-
-  return {
-    name,
-    backgroundColor,
-    borderColor,
-    textColor,
-    iconColor: textColor,
-    editor,
-    syntax,
-    player,
-    shadowAlpha,
-  };
-}
+export const dark = createTheme(`${name}-dark`, false, neutrals, colors);
+export const light = createTheme(`${name}-light`, true, neutrals, colors);

styles/src/themes/sulphurpool.ts 🔗

@@ -1,21 +1,18 @@
-import { color, fontWeights, NumberToken } from "../tokens";
-import { withOpacity } from "../utils/color";
-import Theme, { buildPlayer, Syntax } from "./theme";
-
-// Dark: 0 == Darkest, 3 == Lightest
-const dark = {
-  0: color("#202746"),
-  1: color("#293256"),
-  2: color("#5e6687"),
-  3: color("#6b7394"),
-};
-// Light: 0 == Lightest, 3 == Darkest
-const light = {
-  0: color("#f5f7ff"),
-  1: color("#dfe2f1"),
-  2: color("#979db4"),
-  3: color("#898ea4"),
-};
+import { createTheme } from "./base16";
+import { color } from "../tokens";
+
+const name = "sulphurpool";
+
+const neutrals = [
+  color("#202746"),
+  color("#293256"),
+  color("#5e6687"),
+  color("#6b7394"),
+  color("#898ea4"),
+  color("#979db4"),
+  color("#dfe2f1"),
+  color("#f5f7ff"),
+]
 
 const colors = {
   "red": color("#c94922"),
@@ -28,230 +25,5 @@ const colors = {
   "magenta": color("#9c637a"),
 };
 
-export function sulphurpool(darkTheme: boolean): Theme {
-  let fg = darkTheme ? light : dark;
-  let bg = darkTheme ? dark : light;
-  let name = darkTheme ? "sulphurpool-dark" : "sulphurpool-light";
-
-  const backgroundColor = {
-    100: {
-      base: bg[1],
-      hovered: bg[3],
-      active: bg[3],
-      focused: bg[3],
-    },
-    300: {
-      base: bg[1],
-      hovered: bg[3],
-      active: bg[3],
-      focused: bg[3],
-    },
-    500: {
-      base: bg[0],
-      hovered: bg[1],
-      active: bg[1],
-      focused: bg[1],
-    },
-    on300: {
-      base: bg[0],
-      hovered: bg[1],
-      active: bg[1],
-      focused: bg[1],
-    },
-    on500: {
-      base: bg[1],
-      hovered: bg[3],
-      active: bg[3],
-      focused: bg[3],
-    },
-    ok: {
-      base: colors.green,
-      hovered: colors.green,
-      active: colors.green,
-      focused: colors.green,
-    },
-    error: {
-      base: colors.red,
-      hovered: colors.red,
-      active: colors.red,
-      focused: colors.red,
-    },
-    warning: {
-      base: colors.yellow,
-      hovered: colors.yellow,
-      active: colors.yellow,
-      focused: colors.yellow,
-    },
-    info: {
-      base: colors.blue,
-      hovered: colors.blue,
-      active: colors.blue,
-      focused: colors.blue,
-    },
-  };
-
-  const borderColor = {
-    primary: bg[0],
-    secondary: bg[1],
-    muted: bg[3],
-    focused: bg[3],
-    active: bg[3],
-    ok: colors.green,
-    error: colors.red,
-    warning: colors.yellow,
-    info: colors.blue,
-  };
-
-  const textColor = {
-    primary: fg[1],
-    secondary: fg[2],
-    muted: fg[2],
-    placeholder: fg[3],
-    active: fg[0],
-    //TODO: (design) define feature and it's correct value
-    feature: colors.blue,
-    ok: colors.green,
-    error: colors.red,
-    warning: colors.yellow,
-    info: colors.blue,
-  };
-
-  const player = {
-    1: buildPlayer(colors.blue),
-    2: buildPlayer(colors.green),
-    3: buildPlayer(colors.magenta),
-    4: buildPlayer(colors.orange),
-    5: buildPlayer(colors.violet),
-    6: buildPlayer(colors.cyan),
-    7: buildPlayer(colors.red),
-    8: buildPlayer(colors.yellow),
-  };
-
-  const editor = {
-    background: backgroundColor[500].base,
-    indent_guide: borderColor.muted,
-    indent_guide_active: borderColor.secondary,
-    line: {
-      active: withOpacity(fg[0], 0.07),
-      highlighted: withOpacity(fg[0], 0.12),
-      inserted: backgroundColor.ok.active,
-      deleted: backgroundColor.error.active,
-      modified: backgroundColor.info.active,
-    },
-    highlight: {
-      selection: player[1].selectionColor,
-      occurrence: withOpacity(bg[0], 0.12),
-      activeOccurrence: withOpacity(bg[0], 0.16), // TODO: This is not correctly hooked up to occurences on the rust side
-      matchingBracket: backgroundColor[500].active,
-      match: withOpacity(colors.violet, 0.5),
-      activeMatch: withOpacity(colors.violet, 0.7),
-      related: backgroundColor[500].focused,
-    },
-    gutter: {
-      primary: textColor.placeholder,
-      active: textColor.active,
-    },
-  };
-
-  const syntax: Syntax = {
-    primary: {
-      color: fg[0],
-      weight: fontWeights.normal,
-    },
-    comment: {
-      color: fg[2],
-      weight: fontWeights.normal,
-    },
-    punctuation: {
-      color: fg[2],
-      weight: fontWeights.normal,
-    },
-    constant: {
-      color: fg[3],
-      weight: fontWeights.normal,
-    },
-    keyword: {
-      color: colors.blue,
-      weight: fontWeights.normal,
-    },
-    function: {
-      color: colors.yellow,
-      weight: fontWeights.normal,
-    },
-    type: {
-      color: colors.cyan,
-      weight: fontWeights.normal,
-    },
-    variant: {
-      color: colors.blue,
-      weight: fontWeights.normal,
-    },
-    property: {
-      color: colors.blue,
-      weight: fontWeights.normal,
-    },
-    enum: {
-      color: colors.orange,
-      weight: fontWeights.normal,
-    },
-    operator: {
-      color: colors.orange,
-      weight: fontWeights.normal,
-    },
-    string: {
-      color: colors.orange,
-      weight: fontWeights.normal,
-    },
-    number: {
-      color: colors.green,
-      weight: fontWeights.normal,
-    },
-    boolean: {
-      color: colors.green,
-      weight: fontWeights.normal,
-    },
-    predictive: {
-      color: textColor.muted,
-      weight: fontWeights.normal,
-    },
-    title: {
-      color: colors.yellow,
-      weight: fontWeights.bold,
-    },
-    emphasis: {
-      color: textColor.feature,
-      weight: fontWeights.normal,
-    },
-    "emphasis.strong": {
-      color: textColor.feature,
-      weight: fontWeights.bold,
-    },
-    linkUri: {
-      color: colors.green,
-      weight: fontWeights.normal,
-      underline: true,
-    },
-    linkText: {
-      color: colors.orange,
-      weight: fontWeights.normal,
-      italic: true,
-    },
-  };
-
-  const shadowAlpha: NumberToken = {
-    value: 0.32,
-    type: "number",
-  };
-
-  return {
-    name,
-    backgroundColor,
-    borderColor,
-    textColor,
-    iconColor: textColor,
-    editor,
-    syntax,
-    player,
-    shadowAlpha,
-  };
-}
+export const dark = createTheme(`${name}-dark`, false, neutrals, colors);
+export const light = createTheme(`${name}-light`, true, neutrals, colors);