base16.ts

  1import chroma from "chroma-js";
  2import { Scale, Color } from "chroma-js";
  3import { color, ColorToken, fontWeights, NumberToken } from "../../tokens";
  4import { withOpacity } from "../../utils/color";
  5import Theme, { buildPlayer, Syntax } from "./theme";
  6
  7export function colorRamp(color: Color): Scale {
  8  let hue = color.hsl()[0];
  9  let endColor = chroma.hsl(hue, 0.88, 0.96);
 10  let startColor = chroma.hsl(hue, 0.68, 0.12);
 11  return chroma
 12    .scale([startColor, color, endColor])
 13    .mode("hsl");
 14}
 15
 16export function createTheme(name: string, isLight: boolean, ramps: { [rampName: string]: Scale }, blend?: number): Theme {
 17  if (isLight) {
 18    for (var rampName in ramps) {
 19      ramps[rampName] = ramps[rampName].domain([1, 0]);
 20    }
 21    ramps.neutral = ramps.neutral.domain([7, 0]);
 22  } else {
 23    ramps.neutral = ramps.neutral.domain([0, 7]);
 24  }
 25
 26  if (blend === undefined) {
 27    blend = isLight ? 0.12 : 0.24;
 28  }
 29
 30  function rampColor(ramp: Scale, index: number): ColorToken {
 31    return color(ramp(index).hex());
 32  }
 33
 34  const backgroundColor = {
 35    // Title bar
 36    100: {
 37      base: rampColor(ramps.neutral, 1.25),
 38      hovered: rampColor(ramps.neutral, 1.5),
 39      active: rampColor(ramps.neutral, 1.75),
 40    },
 41    // Midground (panels, etc)
 42    300: {
 43      base: rampColor(ramps.neutral, 1),
 44      hovered: rampColor(ramps.neutral, 1.25),
 45      active: rampColor(ramps.neutral, 1.5),
 46    },
 47    // Editor
 48    500: {
 49      base: rampColor(ramps.neutral, 0),
 50      hovered: rampColor(ramps.neutral, 0.25),
 51      active: rampColor(ramps.neutral, 0.5),
 52    },
 53    on300: {
 54      base: rampColor(ramps.neutral, 0),
 55      hovered: rampColor(ramps.neutral, 0.25),
 56      active: rampColor(ramps.neutral, 0.5),
 57    },
 58    on500: {
 59      base: rampColor(ramps.neutral, 1.25),
 60      hovered: rampColor(ramps.neutral, 1.5),
 61      active: rampColor(ramps.neutral, 1.75),
 62    },
 63    ok: {
 64      base: withOpacity(rampColor(ramps.green, 0.5), 0.15),
 65      hovered: withOpacity(rampColor(ramps.green, 0.5), 0.20),
 66      active: withOpacity(rampColor(ramps.green, 0.5), 0.25),
 67    },
 68    error: {
 69      base: withOpacity(rampColor(ramps.red, 0.5), 0.15),
 70      hovered: withOpacity(rampColor(ramps.red, 0.5), 0.20),
 71      active: withOpacity(rampColor(ramps.red, 0.5), 0.25),
 72    },
 73    warning: {
 74      base: withOpacity(rampColor(ramps.yellow, 0.5), 0.15),
 75      hovered: withOpacity(rampColor(ramps.yellow, 0.5), 0.20),
 76      active: withOpacity(rampColor(ramps.yellow, 0.5), 0.25),
 77    },
 78    info: {
 79      base: withOpacity(rampColor(ramps.blue, 0.5), 0.15),
 80      hovered: withOpacity(rampColor(ramps.blue, 0.5), 0.20),
 81      active: withOpacity(rampColor(ramps.blue, 0.5), 0.25),
 82    },
 83  };
 84
 85  const borderColor = {
 86    primary: rampColor(ramps.neutral, isLight ? 1.5 : 0),
 87    secondary: rampColor(ramps.neutral, isLight ? 1.25 : 1),
 88    muted: rampColor(ramps.neutral, isLight ? 1 : 3),
 89    active: rampColor(ramps.neutral, isLight ? 4 : 3),
 90    onMedia: withOpacity(rampColor(ramps.neutral, 0), 0.1),
 91    ok: withOpacity(rampColor(ramps.green, 0.5), 0.15),
 92    error: withOpacity(rampColor(ramps.red, 0.5), 0.15),
 93    warning: withOpacity(rampColor(ramps.yellow, 0.5), 0.15),
 94    info: withOpacity(rampColor(ramps.blue, 0.5), 0.15),
 95  };
 96
 97  const textColor = {
 98    primary: rampColor(ramps.neutral, 6),
 99    secondary: rampColor(ramps.neutral, 5),
100    muted: rampColor(ramps.neutral, 5),
101    placeholder: rampColor(ramps.neutral, 4),
102    active: rampColor(ramps.neutral, 7),
103    feature: rampColor(ramps.blue, 0.5),
104    ok: rampColor(ramps.green, 0.5),
105    error: rampColor(ramps.red, 0.5),
106    warning: rampColor(ramps.yellow, 0.5),
107    info: rampColor(ramps.blue, 0.5),
108  };
109
110  const player = {
111    1: buildPlayer(rampColor(ramps.blue, 0.5)),
112    2: buildPlayer(rampColor(ramps.green, 0.5)),
113    3: buildPlayer(rampColor(ramps.magenta, 0.5)),
114    4: buildPlayer(rampColor(ramps.orange, 0.5)),
115    5: buildPlayer(rampColor(ramps.violet, 0.5)),
116    6: buildPlayer(rampColor(ramps.cyan, 0.5)),
117    7: buildPlayer(rampColor(ramps.red, 0.5)),
118    8: buildPlayer(rampColor(ramps.yellow, 0.5)),
119  };
120
121  const editor = {
122    background: backgroundColor[500].base,
123    indent_guide: borderColor.muted,
124    indent_guide_active: borderColor.secondary,
125    line: {
126      active: rampColor(ramps.neutral, 1),
127      highlighted: rampColor(ramps.neutral, 1.25), // TODO: Where is this used?
128    },
129    highlight: {
130      selection: player[1].selectionColor,
131      occurrence: withOpacity(rampColor(ramps.neutral, 2), blend),
132      activeOccurrence: withOpacity(rampColor(ramps.neutral, 2), blend * 2), // TODO: Not hooked up - https://github.com/zed-industries/zed/issues/751
133      matchingBracket: backgroundColor[500].active, // TODO: Not hooked up
134      match: rampColor(ramps.violet, 0.15),
135      activeMatch: withOpacity(rampColor(ramps.violet, 0.4), blend * 2), // TODO: Not hooked up - https://github.com/zed-industries/zed/issues/751
136      related: backgroundColor[500].hovered,
137    },
138    gutter: {
139      primary: textColor.placeholder,
140      active: textColor.active,
141    },
142  };
143
144  const syntax: Syntax = {
145    primary: {
146      color: rampColor(ramps.neutral, 7),
147      weight: fontWeights.normal,
148    },
149    comment: {
150      color: rampColor(ramps.neutral, 5),
151      weight: fontWeights.normal,
152    },
153    punctuation: {
154      color: rampColor(ramps.neutral, 6),
155      weight: fontWeights.normal,
156    },
157    constant: {
158      color: rampColor(ramps.neutral, 4),
159      weight: fontWeights.normal,
160    },
161    keyword: {
162      color: rampColor(ramps.blue, 0.5),
163      weight: fontWeights.normal,
164    },
165    function: {
166      color: rampColor(ramps.yellow, 0.5),
167      weight: fontWeights.normal,
168    },
169    type: {
170      color: rampColor(ramps.cyan, 0.5),
171      weight: fontWeights.normal,
172    },
173    variant: {
174      color: rampColor(ramps.blue, 0.5),
175      weight: fontWeights.normal,
176    },
177    property: {
178      color: rampColor(ramps.blue, 0.5),
179      weight: fontWeights.normal,
180    },
181    enum: {
182      color: rampColor(ramps.orange, 0.5),
183      weight: fontWeights.normal,
184    },
185    operator: {
186      color: rampColor(ramps.orange, 0.5),
187      weight: fontWeights.normal,
188    },
189    string: {
190      color: rampColor(ramps.orange, 0.5),
191      weight: fontWeights.normal,
192    },
193    number: {
194      color: rampColor(ramps.green, 0.5),
195      weight: fontWeights.normal,
196    },
197    boolean: {
198      color: rampColor(ramps.green, 0.5),
199      weight: fontWeights.normal,
200    },
201    predictive: {
202      color: textColor.muted,
203      weight: fontWeights.normal,
204    },
205    title: {
206      color: rampColor(ramps.yellow, 0.5),
207      weight: fontWeights.bold,
208    },
209    emphasis: {
210      color: textColor.feature,
211      weight: fontWeights.normal,
212    },
213    "emphasis.strong": {
214      color: textColor.feature,
215      weight: fontWeights.bold,
216    },
217    linkUri: {
218      color: rampColor(ramps.green, 0.5),
219      weight: fontWeights.normal,
220      underline: true,
221    },
222    linkText: {
223      color: rampColor(ramps.orange, 0.5),
224      weight: fontWeights.normal,
225      italic: true,
226    },
227  };
228
229  const shadowAlpha: NumberToken = {
230    value: blend,
231    type: "number",
232  };
233
234  return {
235    name,
236    backgroundColor,
237    borderColor,
238    textColor,
239    iconColor: textColor,
240    editor,
241    syntax,
242    player,
243    shadowAlpha,
244  };
245}