base16.ts

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