base16.ts

  1// NOTE – This should be removed
  2// I (Nate) need to come back and check if we are still using this anywhere
  3
  4import chroma, { Color, Scale } from "chroma-js";
  5import { fontWeights } from "../../common";
  6import { withOpacity } from "../../utils/color";
  7import Theme, { buildPlayer, Syntax } from "./theme";
  8
  9export function colorRamp(color: Color): Scale {
 10  let hue = color.hsl()[0];
 11  let endColor = chroma.hsl(hue, 0.88, 0.96);
 12  let startColor = chroma.hsl(hue, 0.68, 0.12);
 13  return chroma.scale([startColor, color, endColor]).mode("hsl");
 14}
 15
 16export function createTheme(
 17  name: string,
 18  isLight: boolean,
 19  color_ramps: { [rampName: string]: Scale }
 20): Theme {
 21  let ramps: typeof color_ramps = {};
 22  // Chromajs mutates the underlying ramp when you call domain. This causes problems because
 23  // we now store the ramps object in the theme so that we can pull colors out of them.
 24  // So instead of calling domain and storing the result, we have to construct new ramps for each
 25  // theme so that we don't modify the passed in ramps.
 26  // This combined with an error in the type definitions for chroma js means we have to cast the colors
 27  // function to any in order to get the colors back out from the original ramps.
 28  if (isLight) {
 29    for (var rampName in color_ramps) {
 30      ramps[rampName] = chroma
 31        .scale((color_ramps[rampName].colors as any)())
 32        .domain([1, 0]);
 33    }
 34    ramps.neutral = chroma
 35      .scale((color_ramps.neutral.colors as any)())
 36      .domain([7, 0]);
 37  } else {
 38    for (var rampName in color_ramps) {
 39      ramps[rampName] = chroma
 40        .scale((color_ramps[rampName].colors as any)())
 41        .domain([0, 1]);
 42    }
 43    ramps.neutral = chroma
 44      .scale((color_ramps.neutral.colors as any)())
 45      .domain([0, 7]);
 46  }
 47
 48  let blend = isLight ? 0.12 : 0.24;
 49
 50  function sample(ramp: Scale, index: number): string {
 51    return ramp(index).hex();
 52  }
 53  const darkest = ramps.neutral(isLight ? 7 : 0).hex();
 54
 55  const backgroundColor = {
 56    // Title bar
 57    100: {
 58      base: sample(ramps.neutral, 1.25),
 59      hovered: sample(ramps.neutral, 1.5),
 60      active: sample(ramps.neutral, 1.75),
 61    },
 62    // Midground (panels, etc)
 63    300: {
 64      base: sample(ramps.neutral, 1),
 65      hovered: sample(ramps.neutral, 1.25),
 66      active: sample(ramps.neutral, 1.5),
 67    },
 68    // Editor
 69    500: {
 70      base: sample(ramps.neutral, 0),
 71      hovered: sample(ramps.neutral, 0.25),
 72      active: sample(ramps.neutral, 0.5),
 73    },
 74    on300: {
 75      base: sample(ramps.neutral, 0),
 76      hovered: sample(ramps.neutral, 0.5),
 77      active: sample(ramps.neutral, 1),
 78    },
 79    on500: {
 80      base: sample(ramps.neutral, 1),
 81      hovered: sample(ramps.neutral, 1.5),
 82      active: sample(ramps.neutral, 2),
 83    },
 84    ok: {
 85      base: withOpacity(sample(ramps.green, 0.5), 0.15),
 86      hovered: withOpacity(sample(ramps.green, 0.5), 0.2),
 87      active: withOpacity(sample(ramps.green, 0.5), 0.25),
 88    },
 89    error: {
 90      base: withOpacity(sample(ramps.red, 0.5), 0.15),
 91      hovered: withOpacity(sample(ramps.red, 0.5), 0.2),
 92      active: withOpacity(sample(ramps.red, 0.5), 0.25),
 93    },
 94    on500Error: {
 95      base: sample(ramps.red, 0.05),
 96      hovered: sample(ramps.red, 0.1),
 97      active: sample(ramps.red, 0.15),
 98    },
 99    warning: {
100      base: withOpacity(sample(ramps.yellow, 0.5), 0.15),
101      hovered: withOpacity(sample(ramps.yellow, 0.5), 0.2),
102      active: withOpacity(sample(ramps.yellow, 0.5), 0.25),
103    },
104    on500Warning: {
105      base: sample(ramps.yellow, 0.05),
106      hovered: sample(ramps.yellow, 0.1),
107      active: sample(ramps.yellow, 0.15),
108    },
109    info: {
110      base: withOpacity(sample(ramps.blue, 0.5), 0.15),
111      hovered: withOpacity(sample(ramps.blue, 0.5), 0.2),
112      active: withOpacity(sample(ramps.blue, 0.5), 0.25),
113    },
114    on500Info: {
115      base: sample(ramps.blue, 0.05),
116      hovered: sample(ramps.blue, 0.1),
117      active: sample(ramps.blue, 0.15),
118    },
119    on500Ok: {
120      base: sample(ramps.green, 0.05),
121      hovered: sample(ramps.green, 0.1),
122      active: sample(ramps.green, 0.15),
123    },
124  };
125
126  const borderColor = {
127    primary: sample(ramps.neutral, isLight ? 1.5 : 0),
128    secondary: sample(ramps.neutral, isLight ? 1.25 : 1),
129    muted: sample(ramps.neutral, isLight ? 1.25 : 3),
130    active: sample(ramps.neutral, isLight ? 4 : 3),
131    onMedia: withOpacity(darkest, 0.1),
132    ok: sample(ramps.green, 0.3),
133    error: sample(ramps.red, 0.3),
134    warning: sample(ramps.yellow, 0.3),
135    info: sample(ramps.blue, 0.3),
136  };
137
138  const textColor = {
139    primary: sample(ramps.neutral, 6),
140    secondary: sample(ramps.neutral, 5),
141    muted: sample(ramps.neutral, 4),
142    placeholder: sample(ramps.neutral, 3),
143    active: sample(ramps.neutral, 7),
144    feature: sample(ramps.blue, 0.5),
145    ok: sample(ramps.green, 0.5),
146    error: sample(ramps.red, 0.5),
147    warning: sample(ramps.yellow, 0.5),
148    info: sample(ramps.blue, 0.5),
149    onMedia: darkest,
150  };
151
152  const player = {
153    1: buildPlayer(sample(ramps.blue, 0.5)),
154    2: buildPlayer(sample(ramps.green, 0.5)),
155    3: buildPlayer(sample(ramps.magenta, 0.5)),
156    4: buildPlayer(sample(ramps.orange, 0.5)),
157    5: buildPlayer(sample(ramps.violet, 0.5)),
158    6: buildPlayer(sample(ramps.cyan, 0.5)),
159    7: buildPlayer(sample(ramps.red, 0.5)),
160    8: buildPlayer(sample(ramps.yellow, 0.5)),
161  };
162
163  const editor = {
164    background: backgroundColor[500].base,
165    indent_guide: borderColor.muted,
166    indent_guide_active: borderColor.secondary,
167    line: {
168      active: sample(ramps.neutral, 1),
169      highlighted: sample(ramps.neutral, 1.25), // TODO: Where is this used?
170    },
171    highlight: {
172      selection: player[1].selectionColor,
173      occurrence: withOpacity(sample(ramps.neutral, 3.5), blend),
174      activeOccurrence: withOpacity(sample(ramps.neutral, 3.5), blend * 2), // TODO: Not hooked up - https://github.com/zed-industries/zed/issues/751
175      matchingBracket: backgroundColor[500].active, // TODO: Not hooked up
176      match: sample(ramps.violet, 0.15),
177      activeMatch: withOpacity(sample(ramps.violet, 0.4), blend * 2), // TODO: Not hooked up - https://github.com/zed-industries/zed/issues/751
178      related: backgroundColor[500].hovered,
179    },
180    gutter: {
181      primary: textColor.placeholder,
182      active: textColor.active,
183    },
184  };
185
186  const syntax: Syntax = {
187    primary: {
188      color: sample(ramps.neutral, 7),
189      weight: fontWeights.normal,
190    },
191    "variable.special": {
192      color: sample(ramps.blue, 0.8),
193      weight: fontWeights.normal,
194    },
195    comment: {
196      color: sample(ramps.neutral, 5),
197      weight: fontWeights.normal,
198    },
199    punctuation: {
200      color: sample(ramps.neutral, 6),
201      weight: fontWeights.normal,
202    },
203    constant: {
204      color: sample(ramps.neutral, 4),
205      weight: fontWeights.normal,
206    },
207    keyword: {
208      color: sample(ramps.blue, 0.5),
209      weight: fontWeights.normal,
210    },
211    function: {
212      color: sample(ramps.yellow, 0.5),
213      weight: fontWeights.normal,
214    },
215    type: {
216      color: sample(ramps.cyan, 0.5),
217      weight: fontWeights.normal,
218    },
219    constructor: {
220      color: sample(ramps.cyan, 0.5),
221      weight: fontWeights.normal,
222    },
223    property: {
224      color: sample(ramps.blue, 0.6),
225      weight: fontWeights.normal,
226    },
227    enum: {
228      color: sample(ramps.orange, 0.5),
229      weight: fontWeights.normal,
230    },
231    operator: {
232      color: sample(ramps.orange, 0.5),
233      weight: fontWeights.normal,
234    },
235    string: {
236      color: sample(ramps.orange, 0.5),
237      weight: fontWeights.normal,
238    },
239    number: {
240      color: sample(ramps.green, 0.5),
241      weight: fontWeights.normal,
242    },
243    boolean: {
244      color: sample(ramps.green, 0.5),
245      weight: fontWeights.normal,
246    },
247    predictive: {
248      color: textColor.muted,
249      weight: fontWeights.normal,
250    },
251    title: {
252      color: sample(ramps.yellow, 0.5),
253      weight: fontWeights.bold,
254    },
255    emphasis: {
256      color: textColor.feature,
257      weight: fontWeights.normal,
258    },
259    "emphasis.strong": {
260      color: textColor.feature,
261      weight: fontWeights.bold,
262    },
263    linkUri: {
264      color: sample(ramps.green, 0.5),
265      weight: fontWeights.normal,
266      underline: true,
267    },
268    linkText: {
269      color: sample(ramps.orange, 0.5),
270      weight: fontWeights.normal,
271      italic: true,
272    },
273  };
274
275  const shadow = withOpacity(
276    ramps
277      .neutral(isLight ? 7 : 0)
278      .darken()
279      .hex(),
280    blend
281  );
282
283  return {
284    name,
285    isLight,
286    backgroundColor,
287    borderColor,
288    textColor,
289    iconColor: textColor,
290    editor,
291    syntax,
292    player,
293    shadow,
294    ramps,
295  };
296}