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}