theme.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. import '@emotion/react';
  2. import color from 'color';
  3. import CHART_PALETTE from 'app/constants/chartPalette';
  4. import {DataCategory} from 'app/types';
  5. const colors = {
  6. white: '#FFFFFF',
  7. black: '#1D1127',
  8. gray100: '#E7E1EC',
  9. gray200: '#C6BECF',
  10. gray300: '#9386A0',
  11. gray400: '#776589',
  12. gray500: '#2B1D38',
  13. yellow100: '#FDE8b4',
  14. yellow200: '#FFD577',
  15. yellow300: '#FFC227',
  16. purple100: '#D4D1EC',
  17. purple200: '#A396DA',
  18. purple300: '#6C5FC7',
  19. blue100: '#D2DFF7',
  20. blue200: '#6e9ef7',
  21. blue300: '#3D74DB',
  22. orange100: '#FFF1ED',
  23. orange200: '#F9C7B9',
  24. orange300: '#F69C7D',
  25. orange400: '#FF7738',
  26. orange500: '#BA4A23',
  27. red100: '#FCC6C8',
  28. red200: '#FD918F',
  29. red300: '#F55459',
  30. green100: '#B6ECDF',
  31. green200: '#7DD6BE',
  32. green300: '#33BF9E',
  33. pink100: '#FDC9D7',
  34. pink200: '#FA93AB',
  35. pink300: '#F05781',
  36. };
  37. /**
  38. * This is not in the gray palette because it should [generally] only be used for backgrounds
  39. */
  40. const backgroundSecondary = '#FAF9FB';
  41. const lightAliases = {
  42. /**
  43. * Primary text color
  44. */
  45. textColor: colors.gray500,
  46. /**
  47. * Text that should not have as much emphasis
  48. */
  49. subText: colors.gray400,
  50. /**
  51. * Background for the main content area of a page?
  52. */
  53. bodyBackground: backgroundSecondary,
  54. /**
  55. * Primary background color
  56. */
  57. background: colors.white,
  58. /**
  59. * Secondary background color used as a slight contrast against primary background
  60. */
  61. backgroundSecondary,
  62. /**
  63. * Background for the header of a page
  64. */
  65. headerBackground: colors.white,
  66. /**
  67. * Primary border color
  68. */
  69. border: colors.gray200,
  70. /**
  71. * Inner borders, e.g. borders inside of a grid
  72. */
  73. innerBorder: colors.gray100,
  74. /**
  75. * Border around modals
  76. */
  77. modalBorder: 'none',
  78. /**
  79. * Box shadow on the modal
  80. */
  81. modalBoxShadow: 'none',
  82. /**
  83. * A color that denotes a "success", or something good
  84. */
  85. success: colors.green300,
  86. /**
  87. * A color that denotes an error, or something that is wrong
  88. */
  89. error: colors.red300,
  90. /**
  91. * A color that indicates something is disabled where user can not interact or use
  92. * it in the usual manner (implies that there is an "enabled" state)
  93. */
  94. disabled: colors.gray200,
  95. /**
  96. * Indicates that something is "active" or "selected"
  97. */
  98. active: colors.purple300,
  99. /**
  100. * Indicates that something has "focus", which is different than "active" state as it is more temporal
  101. * and should be a bit subtler than active
  102. */
  103. focus: backgroundSecondary,
  104. /**
  105. * Inactive
  106. */
  107. inactive: colors.gray200,
  108. /**
  109. * Link color indicates that something is clickable
  110. */
  111. linkColor: colors.blue300,
  112. linkHoverColor: colors.blue300,
  113. /**
  114. * Secondary button colors
  115. */
  116. secondaryButtonBorder: colors.gray200,
  117. secondaryButtonText: colors.gray500,
  118. /**
  119. * Primary button colors
  120. */
  121. primaryButtonBorder: '#3d328e',
  122. primaryButtonBorderActive: '#352b7b',
  123. /**
  124. * Gradient for sidebar
  125. */
  126. sidebarGradient:
  127. 'linear-gradient(294.17deg,#2f1937 35.57%,#452650 92.42%,#452650 92.42%)',
  128. /**
  129. * Form placeholder text color
  130. */
  131. formPlaceholder: colors.gray200,
  132. /**
  133. * Default form text color
  134. */
  135. formText: colors.gray500,
  136. /**
  137. * Form input border
  138. */
  139. formInputBorder: colors.gray200,
  140. /**
  141. *
  142. */
  143. rowBackground: backgroundSecondary,
  144. /**
  145. * Color of lines that flow across the background of the chart to indicate axes levels
  146. * (This should only be used for yAxis)
  147. */
  148. chartLineColor: colors.gray100,
  149. /**
  150. * Color for chart label text
  151. */
  152. chartLabel: colors.gray200,
  153. /**
  154. * Default Progressbar color
  155. */
  156. progressBar: colors.purple300,
  157. /**
  158. * Default Progressbar color
  159. */
  160. progressBackground: colors.gray100,
  161. /**
  162. * Background of alerts
  163. */
  164. alertBackgroundAlpha: 0.3,
  165. /**
  166. * Background of default badge (mainly used in NavTabs)
  167. */
  168. badgeBackground: colors.gray200,
  169. /**
  170. * Overlay for partial opacity
  171. */
  172. overlayBackgroundAlpha: 'rgba(255, 255, 255, 0.7)',
  173. /**
  174. * Tag progress bars
  175. */
  176. tagBarHover: colors.purple200,
  177. tagBar: colors.gray200,
  178. /**
  179. * Color for badge text
  180. */
  181. badgeText: colors.white,
  182. /**
  183. * Search filter "token" background
  184. */
  185. searchTokenBackground: {
  186. valid: '#E8F3FE',
  187. validActive: color('#E8F3FE').darken(0.02).string(),
  188. invalid: colors.red100,
  189. invalidActive: color(colors.red100).darken(0.02).string(),
  190. },
  191. /**
  192. * Search filter "token" border
  193. */
  194. searchTokenBorder: {
  195. valid: '#B5DAFF',
  196. validActive: color('#B5DAFF').darken(0.15).string(),
  197. invalid: colors.red300,
  198. invalidActive: color(colors.red300).darken(0.15).string(),
  199. },
  200. /**
  201. * Count on button when active
  202. */
  203. buttonCountActive: colors.gray100,
  204. /**
  205. * Count on button
  206. */
  207. buttonCount: colors.gray400,
  208. };
  209. const dataCategory = {
  210. [DataCategory.ERRORS]: CHART_PALETTE[4][3],
  211. [DataCategory.TRANSACTIONS]: CHART_PALETTE[4][2],
  212. [DataCategory.ATTACHMENTS]: CHART_PALETTE[4][1],
  213. [DataCategory.DEFAULT]: CHART_PALETTE[4][0],
  214. };
  215. const generateAlertTheme = (alias: Aliases) => ({
  216. muted: {
  217. background: colors.gray200,
  218. backgroundLight: alias.backgroundSecondary,
  219. border: alias.border,
  220. iconColor: 'inherit',
  221. },
  222. info: {
  223. background: colors.blue300,
  224. backgroundLight: color(colors.blue100).alpha(alias.alertBackgroundAlpha).string(),
  225. border: colors.blue200,
  226. iconColor: colors.blue300,
  227. },
  228. warning: {
  229. background: colors.yellow300,
  230. backgroundLight: color(colors.yellow100).alpha(alias.alertBackgroundAlpha).string(),
  231. border: colors.yellow300,
  232. iconColor: colors.yellow300,
  233. },
  234. success: {
  235. background: colors.green300,
  236. backgroundLight: color(colors.green100).alpha(alias.alertBackgroundAlpha).string(),
  237. border: colors.green200,
  238. iconColor: colors.green300,
  239. },
  240. error: {
  241. background: colors.red300,
  242. backgroundLight: color(colors.red100).alpha(alias.alertBackgroundAlpha).string(),
  243. border: colors.red200,
  244. iconColor: colors.red300,
  245. textLight: colors.red200,
  246. },
  247. });
  248. const generateBadgeTheme = (alias: Aliases) => ({
  249. default: {
  250. background: alias.badgeBackground,
  251. indicatorColor: alias.badgeBackground,
  252. color: alias.badgeText,
  253. },
  254. alpha: {
  255. background: `linear-gradient(90deg, ${colors.pink300}, ${colors.yellow300})`,
  256. indicatorColor: colors.orange400,
  257. color: alias.badgeText,
  258. },
  259. beta: {
  260. background: `linear-gradient(90deg, ${colors.purple300}, ${colors.pink300})`,
  261. indicatorColor: colors.purple300,
  262. color: alias.badgeText,
  263. },
  264. new: {
  265. background: `linear-gradient(90deg, ${colors.blue300}, ${colors.green300})`,
  266. indicatorColor: colors.green300,
  267. color: alias.badgeText,
  268. },
  269. review: {
  270. background: colors.purple300,
  271. indicatorColor: colors.purple300,
  272. color: alias.badgeText,
  273. },
  274. warning: {
  275. background: colors.yellow300,
  276. indicatorColor: colors.yellow300,
  277. color: alias.badgeText,
  278. },
  279. });
  280. const tag = {
  281. default: {
  282. background: colors.gray100,
  283. iconColor: colors.purple300,
  284. },
  285. promotion: {
  286. background: colors.orange100,
  287. iconColor: colors.orange400,
  288. },
  289. highlight: {
  290. background: colors.purple100,
  291. iconColor: colors.purple300,
  292. },
  293. warning: {
  294. background: colors.yellow100,
  295. iconColor: colors.yellow300,
  296. },
  297. success: {
  298. background: colors.green100,
  299. iconColor: colors.green300,
  300. },
  301. error: {
  302. background: colors.red100,
  303. iconColor: colors.red300,
  304. },
  305. info: {
  306. background: colors.blue100,
  307. iconColor: colors.blue300,
  308. },
  309. white: {
  310. background: colors.white,
  311. iconColor: colors.gray500,
  312. },
  313. black: {
  314. background: colors.gray500,
  315. iconColor: colors.white,
  316. },
  317. };
  318. const level = {
  319. sample: colors.purple300,
  320. info: colors.blue300,
  321. warning: colors.yellow300,
  322. error: colors.orange400,
  323. fatal: colors.red300,
  324. default: colors.gray300,
  325. };
  326. const generateButtonTheme = (alias: Aliases) => ({
  327. borderRadius: '3px',
  328. default: {
  329. color: alias.secondaryButtonText,
  330. colorActive: alias.secondaryButtonText,
  331. background: alias.background,
  332. backgroundActive: alias.background,
  333. border: alias.secondaryButtonBorder,
  334. borderActive: alias.secondaryButtonBorder,
  335. focusShadow: color(colors.gray200).alpha(0.5).string(),
  336. },
  337. primary: {
  338. color: colors.white,
  339. colorActive: colors.white,
  340. background: colors.purple300,
  341. backgroundActive: '#4e3fb4',
  342. border: alias.primaryButtonBorder,
  343. borderActive: alias.primaryButtonBorderActive,
  344. focusShadow: color(colors.purple300).alpha(0.4).string(),
  345. },
  346. success: {
  347. color: colors.white,
  348. colorActive: colors.white,
  349. background: '#3fa372',
  350. backgroundActive: colors.green300,
  351. border: '#7ccca5',
  352. borderActive: '#7ccca5',
  353. focusShadow: color(colors.green300).alpha(0.5).string(),
  354. },
  355. danger: {
  356. color: colors.white,
  357. colorActive: colors.white,
  358. background: colors.red300,
  359. backgroundActive: '#bf2a1d',
  360. border: '#bf2a1d',
  361. borderActive: '#7d1c13',
  362. focusShadow: color(colors.red300).alpha(0.5).string(),
  363. },
  364. link: {
  365. color: colors.blue300,
  366. colorActive: colors.blue300,
  367. background: 'transparent',
  368. border: false,
  369. borderActive: false,
  370. backgroundActive: 'transparent',
  371. focusShadow: false,
  372. },
  373. disabled: {
  374. color: alias.disabled,
  375. colorActive: alias.disabled,
  376. border: alias.disabled,
  377. borderActive: alias.disabled,
  378. background: alias.background,
  379. backgroundActive: alias.background,
  380. focusShadow: false,
  381. },
  382. form: {
  383. color: alias.textColor,
  384. colorActive: alias.textColor,
  385. background: alias.background,
  386. backgroundActive: alias.background,
  387. border: alias.formInputBorder,
  388. borderActive: alias.formInputBorder,
  389. focusShadow: false,
  390. },
  391. });
  392. const iconSizes = {
  393. xs: '12px',
  394. sm: '16px',
  395. md: '20px',
  396. lg: '24px',
  397. xl: '32px',
  398. xxl: '72px',
  399. };
  400. const commonTheme = {
  401. breakpoints: ['800px', '992px', '1200px', '1440px', '2560px'],
  402. ...colors,
  403. iconSizes,
  404. iconDirections: {
  405. up: '0',
  406. right: '90',
  407. down: '180',
  408. left: '270',
  409. },
  410. // Try to keep these ordered plz
  411. zIndex: {
  412. // Generic z-index when you hope your component is isolated and
  413. // does not need to battle others for z-index priority
  414. initial: 1,
  415. breadcrumbs: {
  416. header: 2,
  417. gridCellError: 1,
  418. iconWrapper: 1,
  419. },
  420. truncationFullValue: 10,
  421. traceView: {
  422. spanTreeToggler: 900,
  423. dividerLine: 909,
  424. rowInfoMessage: 910,
  425. minimapContainer: 999,
  426. },
  427. header: 1000,
  428. errorMessage: 1000,
  429. dropdown: 1001,
  430. dropdownAutocomplete: {
  431. // needs to be below actor but above other page elements (e.g. pagination)
  432. // (e.g. Issue Details "seen" dots on chart is 2)
  433. // stream header is 1000
  434. menu: 1007,
  435. // needs to be above menu
  436. actor: 1008,
  437. },
  438. globalSelectionHeader: 1009,
  439. settingsSidebarNavMask: 1017,
  440. settingsSidebarNav: 1018,
  441. sidebarPanel: 1019,
  442. sidebar: 1020,
  443. orgAndUserMenu: 1030,
  444. // Sentry user feedback modal
  445. sentryErrorEmbed: 1090,
  446. // If you change modal also update shared-components.less
  447. // as the z-index for bootstrap modals lives there.
  448. modal: 10000,
  449. toast: 10001,
  450. // tooltips and hovercards can be inside modals sometimes.
  451. hovercard: 10002,
  452. tooltip: 10003,
  453. },
  454. grid: 8,
  455. borderRadius: '4px',
  456. borderRadiusBottom: '0 0 4px 4px',
  457. borderRadiusTop: '4px 4px 0 0',
  458. headerSelectorRowHeight: 44,
  459. headerSelectorLabelHeight: 28,
  460. dropShadowLightest: '0 1px 2px rgba(0, 0, 0, 0.04)',
  461. dropShadowLight: '0 2px 0 rgba(37, 11, 54, 0.04)',
  462. dropShadowHeavy: '0 1px 4px 1px rgba(47,40,55,0.08), 0 4px 16px 0 rgba(47,40,55,0.12)',
  463. // Relative font sizes
  464. fontSizeRelativeSmall: '0.9em',
  465. fontSizeExtraSmall: '11px',
  466. fontSizeSmall: '12px',
  467. fontSizeMedium: '14px',
  468. fontSizeLarge: '16px',
  469. fontSizeExtraLarge: '18px',
  470. headerFontSize: '22px',
  471. settings: {
  472. // Max-width for settings breadcrumbs
  473. // i.e. organization, project, or team
  474. maxCrumbWidth: '240px',
  475. containerWidth: '1440px',
  476. headerHeight: '69px',
  477. sidebarWidth: '220px',
  478. },
  479. sidebar: {
  480. background: '#2f2936',
  481. color: '#9586a5',
  482. divider: '#493e54',
  483. badgeSize: '22px',
  484. smallBadgeSize: '11px',
  485. collapsedWidth: '70px',
  486. expandedWidth: '220px',
  487. mobileHeight: '54px',
  488. menuSpacing: '15px',
  489. },
  490. text: {
  491. family: '"Rubik", "Avenir Next", sans-serif',
  492. familyMono: '"IBM Plex", Monaco, Consolas, "Courier New", monospace',
  493. lineHeightHeading: '1.15',
  494. lineHeightBody: '1.4',
  495. },
  496. dataCategory,
  497. tag,
  498. level,
  499. charts: {
  500. colors: CHART_PALETTE[CHART_PALETTE.length - 1],
  501. // We have an array that maps `number + 1` --> list of `number` colors
  502. getColorPalette: (length: number) =>
  503. CHART_PALETTE[Math.min(CHART_PALETTE.length - 1, length + 1)] as string[],
  504. previousPeriod: colors.gray200,
  505. symbolSize: 6,
  506. },
  507. diff: {
  508. removedRow: 'hsl(358deg 89% 65% / 15%)',
  509. removed: 'hsl(358deg 89% 65% / 30%)',
  510. addedRow: 'hsl(100deg 100% 87% / 18%)',
  511. added: 'hsl(166deg 58% 47% / 32%)',
  512. },
  513. // Similarity spectrum used in "Similar Issues" in group details
  514. similarity: {
  515. empty: '#e2dee6',
  516. colors: ['#ec5e44', '#f38259', '#f9a66d', '#98b480', '#57be8c'],
  517. },
  518. space: [0, 8, 16, 20, 30],
  519. // used as a gradient,
  520. businessIconColors: ['#EA5BC2', '#6148CE'],
  521. demo: {
  522. headerSize: '70px',
  523. },
  524. };
  525. const darkAliases = {
  526. ...lightAliases,
  527. bodyBackground: colors.black,
  528. headerBackground: colors.gray500,
  529. background: colors.black,
  530. backgroundSecondary: colors.gray500,
  531. border: colors.gray400,
  532. innerBorder: colors.gray500,
  533. modalBorder: `1px solid ${colors.gray400}`,
  534. modalBoxShadow: '0 15px 40px 0 rgb(67 62 75 / 30%), 0 1px 15px 0 rgb(67 61 74 / 15%)',
  535. textColor: colors.white,
  536. subText: colors.gray200,
  537. linkColor: colors.blue200,
  538. linkHoverColor: colors.blue300,
  539. disabled: colors.gray400,
  540. active: colors.pink300,
  541. focus: colors.gray500,
  542. inactive: colors.gray200,
  543. error: colors.red300,
  544. success: colors.green300,
  545. primaryButtonBorder: colors.purple200,
  546. primaryButtonBorderActive: colors.purple200,
  547. secondaryButtonText: colors.purple200,
  548. secondaryButtonBorder: colors.purple200,
  549. sidebarGradient: 'linear-gradient(6.01deg, #0A090F -8.44%, #1B0921 85.02%)',
  550. formPlaceholder: colors.gray400,
  551. formText: colors.white,
  552. formInputBorder: colors.gray400,
  553. rowBackground: colors.gray500,
  554. chartLineColor: colors.gray500,
  555. chartLabel: colors.gray400,
  556. progressBar: colors.purple200,
  557. progressBackground: colors.gray400,
  558. badgeBackground: colors.gray400,
  559. alertBackgroundAlpha: 0.1,
  560. overlayBackgroundAlpha: 'rgba(18, 9, 23, 0.7)',
  561. tagBarHover: colors.purple300,
  562. tagBar: colors.gray400,
  563. businessIconColors: [colors.pink100, colors.pink300],
  564. badgeText: colors.black,
  565. searchTokenBackground: {
  566. valid: '#1F1A3D',
  567. validActive: color('#1F1A3D').lighten(0.05).string(),
  568. invalid: color(colors.red300).darken(0.8).string(),
  569. invalidActive: color(colors.red300).darken(0.7).string(),
  570. },
  571. searchTokenBorder: {
  572. valid: '#554E80',
  573. validActive: color('#554E80').lighten(0.15).string(),
  574. invalid: color(colors.red300).darken(0.5).string(),
  575. invalidActive: color(colors.red300).darken(0.4).string(),
  576. },
  577. buttonCountActive: colors.gray100,
  578. buttonCount: colors.gray400,
  579. };
  580. export const lightTheme = {
  581. ...commonTheme,
  582. ...lightAliases,
  583. alert: generateAlertTheme(lightAliases),
  584. badge: generateBadgeTheme(lightAliases),
  585. button: generateButtonTheme(lightAliases),
  586. };
  587. export const darkTheme: Theme = {
  588. ...commonTheme,
  589. ...darkAliases,
  590. alert: generateAlertTheme(darkAliases),
  591. badge: generateBadgeTheme(darkAliases),
  592. button: generateButtonTheme(darkAliases),
  593. };
  594. export type Theme = typeof lightTheme;
  595. export type Aliases = typeof lightAliases;
  596. export type Color = keyof typeof colors;
  597. export type IconSize = keyof typeof iconSizes;
  598. export default commonTheme;
  599. type MyTheme = Theme;
  600. /**
  601. * Configure Emotion to use our theme
  602. */
  603. declare module '@emotion/react' {
  604. export interface Theme extends MyTheme {}
  605. }
  606. // This should never be used directly (except in storybook)
  607. export {lightAliases as aliases};