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.24;
 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: withOpacity(neutral[2], blend),
 28    },
 29    300: {
 30      base: neutral[1],
 31      hovered: withOpacity(neutral[2], blend),
 32      active: withOpacity(neutral[2], blend * 1.5),
 33      focused: withOpacity(neutral[2], blend),
 34    },
 35    500: {
 36      base: neutral[0],
 37      hovered: withOpacity(neutral[1], blend),
 38      active: withOpacity(neutral[1], blend * 1.5),
 39      focused: withOpacity(neutral[1], blend),
 40    },
 41    on300: {
 42      base: neutral[0],
 43      hovered: withOpacity(neutral[1], blend),
 44      active: withOpacity(neutral[1], blend * 2),
 45      focused: withOpacity(neutral[1], blend),
 46    },
 47    on500: {
 48      base: neutral[1],
 49      hovered: withOpacity(neutral[2], blend),
 50      active: withOpacity(neutral[2], blend * 2),
 51      focused: withOpacity(neutral[2], blend),
 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    onMedia: withOpacity(neutral[0], 0.1),
 86    ok: withOpacity(accent.green, 0.15),
 87    error: withOpacity(accent.red, 0.15),
 88    warning: withOpacity(accent.yellow, 0.15),
 89    info: withOpacity(accent.blue, 0.15),
 90  };
 91
 92  const textColor = {
 93    primary: neutral[6],
 94    secondary: neutral[5],
 95    muted: neutral[5],
 96    placeholder: neutral[4],
 97    active: neutral[7],
 98    feature: accent.blue,
 99    ok: accent.green,
100    error: accent.red,
101    warning: accent.yellow,
102    info: accent.blue,
103  };
104
105  const player = {
106    1: buildPlayer(accent.blue),
107    2: buildPlayer(accent.green),
108    3: buildPlayer(accent.magenta),
109    4: buildPlayer(accent.orange),
110    5: buildPlayer(accent.violet),
111    6: buildPlayer(accent.cyan),
112    7: buildPlayer(accent.red),
113    8: buildPlayer(accent.yellow),
114  };
115
116  const editor = {
117    background: backgroundColor[500].base,
118    indent_guide: borderColor.muted,
119    indent_guide_active: borderColor.secondary,
120    line: {
121      active: withOpacity(neutral[7], 0.07),
122      highlighted: withOpacity(neutral[7], 0.12),
123      inserted: backgroundColor.ok.active,
124      deleted: backgroundColor.error.active,
125      modified: backgroundColor.info.active,
126    },
127    highlight: {
128      selection: player[1].selectionColor,
129      occurrence: withOpacity(neutral[7], blend / 2),
130      activeOccurrence: withOpacity(neutral[7], blend),
131      matchingBracket: backgroundColor[500].active,
132      match: withOpacity(accent.violet, blend * 2),
133      activeMatch: withOpacity(accent.violet, blend * 3),
134      related: backgroundColor[500].focused,
135    },
136    gutter: {
137      primary: textColor.placeholder,
138      active: textColor.active,
139    },
140  };
141
142  const syntax: Syntax = {
143    primary: {
144      color: neutral[7],
145      weight: fontWeights.normal,
146    },
147    comment: {
148      color: neutral[5],
149      weight: fontWeights.normal,
150    },
151    punctuation: {
152      color: neutral[5],
153      weight: fontWeights.normal,
154    },
155    constant: {
156      color: neutral[4],
157      weight: fontWeights.normal,
158    },
159    keyword: {
160      color: accent.blue,
161      weight: fontWeights.normal,
162    },
163    function: {
164      color: accent.yellow,
165      weight: fontWeights.normal,
166    },
167    type: {
168      color: accent.cyan,
169      weight: fontWeights.normal,
170    },
171    variant: {
172      color: accent.blue,
173      weight: fontWeights.normal,
174    },
175    property: {
176      color: accent.blue,
177      weight: fontWeights.normal,
178    },
179    enum: {
180      color: accent.orange,
181      weight: fontWeights.normal,
182    },
183    operator: {
184      color: accent.orange,
185      weight: fontWeights.normal,
186    },
187    string: {
188      color: accent.orange,
189      weight: fontWeights.normal,
190    },
191    number: {
192      color: accent.green,
193      weight: fontWeights.normal,
194    },
195    boolean: {
196      color: accent.green,
197      weight: fontWeights.normal,
198    },
199    predictive: {
200      color: textColor.muted,
201      weight: fontWeights.normal,
202    },
203    title: {
204      color: accent.yellow,
205      weight: fontWeights.bold,
206    },
207    emphasis: {
208      color: textColor.feature,
209      weight: fontWeights.normal,
210    },
211    "emphasis.strong": {
212      color: textColor.feature,
213      weight: fontWeights.bold,
214    },
215    linkUri: {
216      color: accent.green,
217      weight: fontWeights.normal,
218      underline: true,
219    },
220    linkText: {
221      color: accent.orange,
222      weight: fontWeights.normal,
223      italic: true,
224    },
225  };
226
227  const shadowAlpha: NumberToken = {
228    value: blend,
229    type: "number",
230  };
231
232  return {
233    name,
234    backgroundColor,
235    borderColor,
236    textColor,
237    iconColor: textColor,
238    editor,
239    syntax,
240    player,
241    shadowAlpha,
242  };
243}