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}