diff --git a/package-lock.json b/package-lock.json
index 7d2031361d..dc50048fe1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5966,6 +5966,23 @@
"@sinonjs/commons": "^3.0.0"
}
},
+ "node_modules/@storybook/addon-themes": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/@storybook/addon-themes/-/addon-themes-9.0.2.tgz",
+ "integrity": "sha512-f9MYge4YWGxz3DTf61i9z47iWdOu4klzsCxQEVlEjf5kAQYxsIRMoZS8RqAegVpt7d4bPR++cY481FUtDnMbFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ts-dedent": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/storybook"
+ },
+ "peerDependencies": {
+ "storybook": "^9.0.2"
+ }
+ },
"node_modules/@storybook/addon-webpack5-compiler-babel": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@storybook/addon-webpack5-compiler-babel/-/addon-webpack5-compiler-babel-3.0.6.tgz",
@@ -29642,6 +29659,7 @@
"@bundle-stats/utils": "4.21.7",
"ariakit": "2.0.0-next.44",
"d3": "7.9.0",
+ "js-cookie": "2",
"modern-css-reset": "1.4.0",
"react-use": "17.6.0",
"snarkdown": "2.0.0",
@@ -29651,6 +29669,7 @@
"@babel/preset-env": "7.28.5",
"@babel/preset-react": "7.28.5",
"@babel/preset-typescript": "7.28.5",
+ "@storybook/addon-themes": "9.0.2",
"@storybook/addon-webpack5-compiler-babel": "3.0.6",
"@storybook/react": "9.1.15",
"@storybook/react-webpack5": "9.1.15",
@@ -29685,6 +29704,15 @@
"react-router-dom": "^5.0.0"
}
},
+ "packages/ui/node_modules/js-cookie": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
+ "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"packages/utils": {
"name": "@bundle-stats/utils",
"version": "4.21.7",
diff --git a/packages/ui/build/storybook/main.js b/packages/ui/build/storybook/main.js
index 6cf6143fb3..1598ef6691 100644
--- a/packages/ui/build/storybook/main.js
+++ b/packages/ui/build/storybook/main.js
@@ -7,8 +7,10 @@ function getAbsolutePath(value) {
module.exports = {
framework: getAbsolutePath('@storybook/react-webpack5'),
stories: ['../../src/**/*.stories.@(jsx|tsx|mdx)'],
- addons: [getAbsolutePath('@storybook/addon-webpack5-compiler-babel')],
-
+ addons: [
+ getAbsolutePath('@storybook/addon-webpack5-compiler-babel'),
+ getAbsolutePath('@storybook/addon-themes'),
+ ],
docs: {
autodocs: true,
},
diff --git a/packages/ui/build/storybook/preview.tsx b/packages/ui/build/storybook/preview.tsx
index d630d99f5f..e107a40d8b 100644
--- a/packages/ui/build/storybook/preview.tsx
+++ b/packages/ui/build/storybook/preview.tsx
@@ -1,5 +1,6 @@
import React from 'react';
import type { Preview } from '@storybook/react';
+import { withThemeByClassName } from '@storybook/addon-themes';
import '../../src/css/variables.css';
import '../../src/css/default.css';
@@ -7,6 +8,13 @@ import { SvgIcons } from '../../src/assets/icons.svg.jsx';
const preview: Preview = {
decorators: [
+ withThemeByClassName({
+ themes: {
+ light: 'light-theme',
+ dark: 'dark-theme',
+ },
+ defaultTheme: 'light',
+ }),
(Story) => (
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 4a0ebd842a..3b575a14ad 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -39,6 +39,7 @@
"@babel/preset-env": "7.28.5",
"@babel/preset-react": "7.28.5",
"@babel/preset-typescript": "7.28.5",
+ "@storybook/addon-themes": "9.0.2",
"@storybook/addon-webpack5-compiler-babel": "3.0.6",
"@storybook/react": "9.1.15",
"@storybook/react-webpack5": "9.1.15",
@@ -76,6 +77,7 @@
"@bundle-stats/utils": "4.21.7",
"ariakit": "2.0.0-next.44",
"d3": "7.9.0",
+ "js-cookie": "2.2.1",
"modern-css-reset": "1.4.0",
"react-use": "17.6.0",
"snarkdown": "2.0.0",
diff --git a/packages/ui/src/app/app.jsx b/packages/ui/src/app/app.jsx
index 081b798658..e057c0388c 100644
--- a/packages/ui/src/app/app.jsx
+++ b/packages/ui/src/app/app.jsx
@@ -5,6 +5,7 @@ import { HashRouter, NavLink, Route, Switch, useLocation } from 'react-router-do
import { COMPONENT } from '@bundle-stats/utils';
import { URLS } from '../constants';
+import { ThemeProvider } from '../context/theme';
import { BundleAssets } from '../components/bundle-assets';
import { BundleAssetsTotals } from '../components/bundle-assets-totals';
import { BundleModules } from '../components/bundle-modules';
@@ -205,10 +206,12 @@ AppComponent.propTypes = {
};
export const App = (props) => (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
diff --git a/packages/ui/src/app/app.module.css b/packages/ui/src/app/app.module.css
index 970894f3b1..4e6904b61b 100644
--- a/packages/ui/src/app/app.module.css
+++ b/packages/ui/src/app/app.module.css
@@ -24,7 +24,7 @@
.tabsContainer {
border-bottom: 1px solid var(--color-outline);
- background: var(--color-light);
+ background: var(--color-background);
}
.tabs {
diff --git a/packages/ui/src/app/header/header.module.css b/packages/ui/src/app/header/header.module.css
index d2b153b3e7..a5ecfc0cb8 100644
--- a/packages/ui/src/app/header/header.module.css
+++ b/packages/ui/src/app/header/header.module.css
@@ -6,23 +6,7 @@
flex: 0 0 auto;
}
-.toolsGitHub {
- display: block;
- border-radius: 50%;
- opacity: 0.8;
- color: var(--color-text-ultra-dark);
- transition: var(--transition-out);
-}
-
-.toolsGitHub:hover,
-.toolsGitHub:focus,
-.toolsGitHub:active {
- opacity: 1;
- transition: var(--transition-in);
-}
-
-.toolsGitHubIcon {
- display: block;
+.toolsButton {
}
@media (min-width: 1140px) {
diff --git a/packages/ui/src/app/header/header.tsx b/packages/ui/src/app/header/header.tsx
index 15cec26869..0da2d0ef26 100644
--- a/packages/ui/src/app/header/header.tsx
+++ b/packages/ui/src/app/header/header.tsx
@@ -3,32 +3,60 @@ import cx from 'classnames';
import config from '../../config.json';
import I18N from '../../i18n';
+import { useTheme } from '../../context/theme';
import { Container } from '../../ui/container';
import { Icon } from '../../ui/icon';
import { FlexStack } from '../../layout/flex-stack';
import { JobsHeader } from '../../components/jobs-header';
import css from './header.module.css';
+import { Button } from '../../ui/button';
interface HeaderProps {
jobs: React.ComponentProps
['jobs'];
}
-export const Header = ({ className = '', jobs }: HeaderProps & React.ComponentProps<'header'>) => (
-
-
-
-
-
-
-);
+export const Header = ({ className = '', jobs }: HeaderProps & React.ComponentProps<'header'>) => {
+ const theme = useTheme();
+
+ return (
+
+
+
+
+ {theme.name === 'dark' ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+};
diff --git a/packages/ui/src/assets/icons.svg b/packages/ui/src/assets/icons.svg
index 1b0e11fb8f..a66bd03459 100644
--- a/packages/ui/src/assets/icons.svg
+++ b/packages/ui/src/assets/icons.svg
@@ -64,6 +64,13 @@
+
+
+
+
+
+
+
@@ -73,6 +80,10 @@
+
+
+
+
diff --git a/packages/ui/src/assets/icons.svg.jsx b/packages/ui/src/assets/icons.svg.jsx
index 8e34f82032..8769312b7c 100644
--- a/packages/ui/src/assets/icons.svg.jsx
+++ b/packages/ui/src/assets/icons.svg.jsx
@@ -9,6 +9,7 @@ export const SvgIcons = (props) => (
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
+ className="feather feather-arrow-down"
id="arrow"
xmlns="http://www.w3.org/2000/svg"
>
@@ -20,6 +21,7 @@ export const SvgIcons = (props) => (
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
+ className="feather feather-arrow-right-circle"
viewBox="0 0 24 24"
id="arrow-right-circle"
xmlns="http://www.w3.org/2000/svg"
@@ -45,6 +47,7 @@ export const SvgIcons = (props) => (
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
+ className="lucide lucide-chevron-down"
viewBox="0 0 24 24"
id="chevron-down"
xmlns="http://www.w3.org/2000/svg"
@@ -57,6 +60,7 @@ export const SvgIcons = (props) => (
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
+ className="lucide lucide-chevron-up"
viewBox="0 0 24 24"
id="chevron-up"
xmlns="http://www.w3.org/2000/svg"
@@ -97,6 +101,7 @@ export const SvgIcons = (props) => (
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
+ className="feather feather-clock"
id="clock"
xmlns="http://www.w3.org/2000/svg"
>
@@ -109,6 +114,7 @@ export const SvgIcons = (props) => (
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
+ className="feather feather-x-circle"
viewBox="0 0 24 24"
id="close"
xmlns="http://www.w3.org/2000/svg"
@@ -123,6 +129,7 @@ export const SvgIcons = (props) => (
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
+ className="feather feather-git-commit"
id="commit"
xmlns="http://www.w3.org/2000/svg"
>
@@ -157,6 +164,7 @@ export const SvgIcons = (props) => (
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
+ className="feather feather-external-link"
viewBox="0 0 24 24"
id="external-link"
xmlns="http://www.w3.org/2000/svg"
@@ -170,6 +178,7 @@ export const SvgIcons = (props) => (
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
+ className="feather feather-bar-chart"
id="filter"
xmlns="http://www.w3.org/2000/svg"
>
@@ -190,6 +199,7 @@ export const SvgIcons = (props) => (
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
+ className="feather feather-help-circle"
id="help"
xmlns="http://www.w3.org/2000/svg"
>
@@ -216,11 +226,37 @@ export const SvgIcons = (props) => (
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
+ className="feather feather-menu"
id="menu"
xmlns="http://www.w3.org/2000/svg"
>
+
+
+
+
+
+
+
(
+
+
+
+
(
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
+ className="feather feather-alert-triangle"
id="warning"
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/packages/ui/src/assets/icons/monitor.svg b/packages/ui/src/assets/icons/monitor.svg
new file mode 100644
index 0000000000..2cf5563d33
--- /dev/null
+++ b/packages/ui/src/assets/icons/monitor.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/packages/ui/src/assets/icons/moon.svg b/packages/ui/src/assets/icons/moon.svg
new file mode 100644
index 0000000000..3e1e2d6350
--- /dev/null
+++ b/packages/ui/src/assets/icons/moon.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/packages/ui/src/assets/icons/sun.svg b/packages/ui/src/assets/icons/sun.svg
new file mode 100644
index 0000000000..548abb4fe8
--- /dev/null
+++ b/packages/ui/src/assets/icons/sun.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/packages/ui/src/components/asset-info/asset-info.module.css b/packages/ui/src/components/asset-info/asset-info.module.css
index 57a859417c..74d14dc2e3 100644
--- a/packages/ui/src/components/asset-info/asset-info.module.css
+++ b/packages/ui/src/components/asset-info/asset-info.module.css
@@ -9,7 +9,7 @@
}
.assetNameTagEntry {
- background: var(--color-info-dark);
+ background: var(--color-info-intense);
}
.assetNameTagInitial {
@@ -17,7 +17,7 @@
}
.assetNameTagChunk {
- background: var(--color-info-light);
+ background: var(--color-info-muted);
}
.label {
diff --git a/packages/ui/src/components/asset-meta-tag/asset-meta-tag.module.css b/packages/ui/src/components/asset-meta-tag/asset-meta-tag.module.css
index f8fb49d437..6c4fffd838 100644
--- a/packages/ui/src/components/asset-meta-tag/asset-meta-tag.module.css
+++ b/packages/ui/src/components/asset-meta-tag/asset-meta-tag.module.css
@@ -22,7 +22,7 @@
/* tag */
.entry {
- --background-color: var(--color-info-dark);
+ --background-color: var(--color-info-intense);
}
.entry:empty::before {
@@ -38,7 +38,7 @@
}
.chunk {
- --background-color: var(--color-info-light);
+ --background-color: var(--color-info-muted);
}
.chunk:empty::before {
@@ -47,10 +47,10 @@
/* status */
.added {
- background: linear-gradient(-45deg, var(--color-danger-light) 50%, var(--background-color) 50%) !important;
+ background: linear-gradient(-45deg, var(--color-danger-muted) 50%, var(--background-color) 50%) !important;
}
.removed {
- background: linear-gradient(135deg, var(--color-danger-light) 50%, var(--background-color) 50%) !important;
+ background: linear-gradient(135deg, var(--color-danger-muted) 50%, var(--background-color) 50%) !important;
}
diff --git a/packages/ui/src/components/asset-name/asset-name.module.css b/packages/ui/src/components/asset-name/asset-name.module.css
index a7a983123c..85fab8d971 100644
--- a/packages/ui/src/components/asset-name/asset-name.module.css
+++ b/packages/ui/src/components/asset-name/asset-name.module.css
@@ -6,7 +6,7 @@
}
.notPredictiveIcon {
- color: var(--color-warning-dark);
+ color: var(--color-warning-intense);
}
.notPredictiveHoverCard {
diff --git a/packages/ui/src/components/bundle-modules/bundle-modules.module.css b/packages/ui/src/components/bundle-modules/bundle-modules.module.css
index f063d03c7c..2aa9d2791f 100644
--- a/packages/ui/src/components/bundle-modules/bundle-modules.module.css
+++ b/packages/ui/src/components/bundle-modules/bundle-modules.module.css
@@ -1,5 +1,5 @@
.title {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.toolbarSearch {
diff --git a/packages/ui/src/components/bundle-packages/bundle-packages.module.css b/packages/ui/src/components/bundle-packages/bundle-packages.module.css
index f5d63a7cb4..78ecac2709 100644
--- a/packages/ui/src/components/bundle-packages/bundle-packages.module.css
+++ b/packages/ui/src/components/bundle-packages/bundle-packages.module.css
@@ -1,5 +1,5 @@
.title {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.toolbarSearch {
@@ -13,7 +13,7 @@
.packageName {
display: inline-block;
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.packageName:last-child {
@@ -24,7 +24,7 @@
display: inline-block;
margin: 0 var(--space-xxxsmall);
content: '/';
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
}
.packageNameTagDuplicate {
diff --git a/packages/ui/src/components/delta/delta.module.css b/packages/ui/src/components/delta/delta.module.css
index 2d9a12e75e..1624dafd61 100644
--- a/packages/ui/src/components/delta/delta.module.css
+++ b/packages/ui/src/components/delta/delta.module.css
@@ -1,5 +1,5 @@
.root {
- color: var(--color-gray-ultra-light);
+ color: var(--color-text-ultra-muted);
font-family: var(--font-family-fixed);
white-space: nowrap;
}
@@ -9,60 +9,60 @@
}
.HIGH_NEGATIVE {
- color: var(--color-red-dark);
+ color: var(--color-danger-intense);
}
.NEGATIVE {
- color: var(--color-red-dark);
+ color: var(--color-danger-intense);
}
.LOW_NEGATIVE {
- color: var(--color-red-light);
+ color: var(--color-danger-muted);
}
.LOW_POSITIVE {
- color: var(--color-green-light);
+ color: var(--color-success-muted);
}
.POSITIVE {
- color: var(--color-green-dark);
+ color: var(--color-success-intense);
}
.HIGH_POSITIVE {
- color: var(--color-green-dark);
+ color: var(--color-success-intense);
}
.inverted {
padding: 0 2px;
border-radius: var(--radius-xsmall);
- background-color: var(--color-gray-ultra-light);
- color: var(--color-light);
+ background-color: var(--color-text-ultra-muted);
+ color: var(--color-background);
}
.inverted.CHANGE {
- background: var(--color-info-light);
+ background: var(--color-info-muted);
}
.inverted.HIGH_NEGATIVE {
- background: var(--color-red-dark);
+ background: var(--color-danger-intense);
}
.inverted.NEGATIVE {
- background: var(--color-red-dark);
+ background: var(--color-danger-intense);
}
.inverted.LOW_NEGATIVE {
- background: var(--color-red-light);
+ background: var(--color-danger-muted);
}
.inverted.LOW_POSITIVE {
- background: var(--color-green-light);
+ background: var(--color-success-muted);
}
.inverted.POSITIVE {
- background: var(--color-green-dark);
+ background: var(--color-success-intense);
}
.inverted.HIGH_POSITIVE {
- background: var(--color-green-dark);
+ background: var(--color-success-intense);
}
diff --git a/packages/ui/src/components/entry-info/entry-info.module.css b/packages/ui/src/components/entry-info/entry-info.module.css
index 1dc4d76ff4..6f32965709 100644
--- a/packages/ui/src/components/entry-info/entry-info.module.css
+++ b/packages/ui/src/components/entry-info/entry-info.module.css
@@ -5,7 +5,7 @@
bottom: 0;
width: 100vw;
max-width: var(--entry-info-width);
- z-index: var(--layer-high);
+ z-index: calc(var(--layer-high) - 1);
background: var(--color-background);
box-shadow: var(--shadow-layer-high);
}
@@ -36,13 +36,13 @@
position: absolute;
right: calc(var(--space-small) - var(--space-xxxsmall));
top: calc(var(--space-small) - var(--space-xxxsmall));
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
}
.headerClose:hover,
.headerClose:active,
.headerClose:focus {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
/** comon components */
@@ -88,12 +88,12 @@
margin-right: var(--space-xxxsmall);
min-width: 8em;
flex: 0 1 6em;
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.metaLabelTooltip {
margin-left: var(--space-xxxsmall);
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
}
.metaContent {
@@ -126,7 +126,7 @@
.metaLink:active,
.metaLink:focus {
background: var(--color-outline);
- color: var(--color-primary-dark);
+ color: var(--color-primary-intense);
}
@media (min-width: 640px) {
diff --git a/packages/ui/src/components/insight-icon/insight-icon.module.css b/packages/ui/src/components/insight-icon/insight-icon.module.css
index 04e693d398..fd939e3432 100644
--- a/packages/ui/src/components/insight-icon/insight-icon.module.css
+++ b/packages/ui/src/components/insight-icon/insight-icon.module.css
@@ -7,14 +7,14 @@
}
.error {
- background: var(--color-red-50);
+ background: var(--color-highlight-danger);
color: var(--color-danger);
}
.warning {
padding-top: 1px;
- background: var(--color-yellow-100);
- color: var(--color-yellow-900);
+ background: var(--color-highlight-warning);
+ color: var(--color-warning);
}
.info {
diff --git a/packages/ui/src/components/jobs-header/jobs-header.module.css b/packages/ui/src/components/jobs-header/jobs-header.module.css
index 14f4b19d40..4ef0f28057 100644
--- a/packages/ui/src/components/jobs-header/jobs-header.module.css
+++ b/packages/ui/src/components/jobs-header/jobs-header.module.css
@@ -41,17 +41,17 @@
vertical-align: top;
outline: 1px solid var(--color-outline);
background: var(--color-highlight);
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.itemMeta {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
font-size: var(--size-small);
}
.itemMetaIcon {
flex: 0 0 auto;
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
}
.itemMetaText {
diff --git a/packages/ui/src/components/metric-run-info/metric-run-info.module.css b/packages/ui/src/components/metric-run-info/metric-run-info.module.css
index 702d8c400f..1f70ee00b2 100644
--- a/packages/ui/src/components/metric-run-info/metric-run-info.module.css
+++ b/packages/ui/src/components/metric-run-info/metric-run-info.module.css
@@ -12,6 +12,6 @@
.readMoreLink:hover,
.readMoreLink:focus,
.readMoreLink:active {
- color: var(--color-primary-dark);
+ color: var(--color-primary-intense);
text-decoration: none;
}
diff --git a/packages/ui/src/components/metrics-display-selector/metrics-display-selector.module.css b/packages/ui/src/components/metrics-display-selector/metrics-display-selector.module.css
index ad8c0e71ab..cdb2b71bdd 100644
--- a/packages/ui/src/components/metrics-display-selector/metrics-display-selector.module.css
+++ b/packages/ui/src/components/metrics-display-selector/metrics-display-selector.module.css
@@ -4,7 +4,7 @@
}
.dropdownGroupButton {
- padding: calc(var(--space-xxsmall) - 1px);
+ padding: calc(var(--space-2x) - 1px);
}
.dropdownGroupAnchor {
@@ -17,11 +17,11 @@
content: '';
display: block;
width: 1px;
- background: var(--color-outline-dark);
+ background: var(--color-outline);
position: absolute;
left: 0;
- top: var(--space-xxxsmall);
- bottom: var(--space-xxxsmall);
+ top: var(--space);
+ bottom: var(--space);
}
.dropdownGroupAnchor:hover,
@@ -33,7 +33,7 @@
}
.dropdownGroup:hover {
- border-color: var(--color-outline-dark);
+ border-color: var(--color-outline-intense);
transition: var(--transition-in);
}
diff --git a/packages/ui/src/components/metrics-table-title/metrics-table-title.module.css b/packages/ui/src/components/metrics-table-title/metrics-table-title.module.css
index c3c167e6a4..0d7d83b462 100644
--- a/packages/ui/src/components/metrics-table-title/metrics-table-title.module.css
+++ b/packages/ui/src/components/metrics-table-title/metrics-table-title.module.css
@@ -1,5 +1,5 @@
.root {
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
}
.title {
diff --git a/packages/ui/src/components/metrics-table/metrics-table.module.css b/packages/ui/src/components/metrics-table/metrics-table.module.css
index 72322f784a..3f84b063dd 100644
--- a/packages/ui/src/components/metrics-table/metrics-table.module.css
+++ b/packages/ui/src/components/metrics-table/metrics-table.module.css
@@ -18,7 +18,7 @@
/** rows */
.multipleRuns .unchanged th,
.multipleRuns .unchanged td {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.root .empty {
@@ -29,7 +29,7 @@
}
.emptyIcon {
- color: var(--color-outline-dark);
+ color: var(--color-outline-intense);
width: var(--space-large);
height: var(--space-large);
}
diff --git a/packages/ui/src/components/metrics-treemap/metrics-treemap.module.css b/packages/ui/src/components/metrics-treemap/metrics-treemap.module.css
index eeff481d1f..049282ea07 100644
--- a/packages/ui/src/components/metrics-treemap/metrics-treemap.module.css
+++ b/packages/ui/src/components/metrics-treemap/metrics-treemap.module.css
@@ -50,7 +50,7 @@
.tileGroupTitleTotal {
margin-left: var(--space-xxsmall);
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.tileGroupTitle {
@@ -86,41 +86,41 @@
/** Tile group nested variation */
.nested .tileGroup {
- background-color: rgba(27, 26, 33, 0.03);
- outline: 1px solid rgba(255, 255, 255, 0.8);
+ background-color: rgba(var(--color-background-rgba), 0.1);
+ outline: 1px solid rgba(var(--color-background-rgba), 0.8);
}
.nested .tileGroup:has(> .tileGroupTitle:hover),
.nested .tileGroup:has(> .tile:hover) {
- background-color: rgba(27, 26, 33, 0.06);
- outline: 1px solid rgba(255, 255, 255, 1);
+ background-color: rgba(var(--color-background-rgba), 0.06);
+ outline: 1px solid rgba(var(--color-background-rgba), 1);
transition: var(--transition-in);
transition-property: background-color, outline;
}
.nested .tileGroup--NEGATIVE {
- background-color: rgba(194, 31, 37, 0.04);
+ background-color: rgba(240, 0, 0, 0.04);
}
.nested .tileGroup--NEGATIVE:has(> .tileGroupTitle:hover),
.nested .tileGroup--NEGATIVE:has(> .tile:hover) {
- background-color: rgba(194, 31, 37, 0.1);
+ background-color: rgba(240, 0, 0, 0.1);
}
.nested .tileGroup--POSITIVE {
- background-color: rgba(42, 147, 39, 0.04);
+ background-color: rgba(0, 240, 0, 0.04);
}
.nested .tileGroup--POSITIVE:has(> .tileGroupTitle:hover),
.nested .tileGroup--POSITIVE:has(> .tile:hover) {
- background-color: rgba(42, 147, 39, 0.1);
+ background-color: rgba(0, 240, 0, 0.1);
}
/* reset bacground color when having a not changed group */
.nested .tileGroup--POSITIVE > .tileGroup--NO_CHANGE,
.nested .tileGroup--NEGATIVE > .tileGroup--NO_CHANGE {
background-color: var(--color-background);
- background-image: linear-gradient(rgba(27, 26, 33, 0.1), rgba(27, 26, 33, 0.1));
+ background-image: linear-gradient(var(--color-background-rgba), 0.1);
}
.nested .tileGroup--POSITIVE > .tileGroup--NO_CHANGE:has(> .tileGroupTitle:hover),
@@ -128,7 +128,7 @@
.nested .tileGroup--NEGATIVE > .tileGroup--NO_CHANGE:has(> .tileGroupTitle:hover),
.nested .tileGroup--NEGATIVE > .tileGroup--NEGATIVE > .tileGroup--NO_CHANGE:has(> .tile:hover) {
background-color: var(--color-background);
- background-image: linear-gradient(rgba(27, 26, 33, 0.1), rgba(27, 26, 33, 0.1));
+ background-image: linear-gradient(var(--color-background-rgba), 0.1);
}
/** Leaf wrapper button */
@@ -222,7 +222,7 @@
/** Tile diff variation */
.tile {
- outline: 1px solid rgba(255, 255, 255, 1);
+ outline: 1px solid rgba(var(--color-background-rgba), 1);
color: var(--color-text);
}
@@ -244,11 +244,11 @@
}
.tile-NO_CHANGE {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.tile-NO_CHANGE::before {
- color: var(--color-gray-50);
+ color: var(--gray-3);
opacity: 0.4;
}
@@ -257,27 +257,27 @@
}
.tile-HIGH_NEGATIVE::before {
- color: var(--color-red-100);
+ color: var(--red-5);
}
.tile-NEGATIVE::before {
- color: var(--color-red-50);
+ color: var(--red-4);
}
.tile-LOW_NEGATIVE::before {
- color: var(--color-red-50);
+ color: var(--red-3);
}
.tile-LOW_POSITIVE::before {
- color: var(--color-green-50);
+ color: var(--green-3);
}
.tile-POSITIVE::before {
- color: var(--color-green-50);
+ color: var(--green-4);
}
.tile-HIGH_POSITIVE::before {
- color: var(--color-green-100);
+ color: var(--green-5);
}
/** empty message */
@@ -312,7 +312,7 @@
outline: none;
filter: drop-shadow(var(--shadow-layer-high));
will-change: filter;
- background: var(--color-light);
+ background: var(--color-background);
font-size: var(--size-small);
pointer-events: none;
}
diff --git a/packages/ui/src/components/module-info/module-info.module.css b/packages/ui/src/components/module-info/module-info.module.css
index 9e23e52bae..e0fc56e5ec 100644
--- a/packages/ui/src/components/module-info/module-info.module.css
+++ b/packages/ui/src/components/module-info/module-info.module.css
@@ -13,7 +13,7 @@
.chunksItem-info:hover,
.chunksItem-info:active,
.chunksItem-info:focus {
- color: var(--color-info-dark);
+ color: var(--color-info-intense);
}
.chunksItem-danger {
@@ -23,11 +23,11 @@
.chunksItem-danger:hover,
.chunksItem-danger:active,
.chunksItem-danger:focus {
- color: var(--color-danger-dark);
+ color: var(--color-danger-intense);
}
.chunksItem-default {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.chunksItem-default:hover,
diff --git a/packages/ui/src/components/package-info/package-info.module.css b/packages/ui/src/components/package-info/package-info.module.css
index 7cadb01564..6510863c27 100644
--- a/packages/ui/src/components/package-info/package-info.module.css
+++ b/packages/ui/src/components/package-info/package-info.module.css
@@ -9,5 +9,5 @@
}
.packageTitleId {
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
}
diff --git a/packages/ui/src/components/run-info/run-info.module.css b/packages/ui/src/components/run-info/run-info.module.css
index c0f73602aa..fd4c38133f 100644
--- a/packages/ui/src/components/run-info/run-info.module.css
+++ b/packages/ui/src/components/run-info/run-info.module.css
@@ -1,5 +1,5 @@
.title {
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
font-weight: bold;
font-size: var(--size-xsmall);
text-transform: uppercase;
@@ -40,11 +40,11 @@
margin-left: calc(var(--space-xxxsmall) / 2);
margin-right: calc(var(--space-xxxsmall) / 2);
content: '/';
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
}
.baselineMetric {
- color: var(--color-text-ultra-light);
+ color: var(--color-text-muted);
font-size: var(--size-small);
line-height: var(--line-height-number);
height: var(--line-height-number);
diff --git a/packages/ui/src/components/sort-button/sort-button.module.css b/packages/ui/src/components/sort-button/sort-button.module.css
index 1702051235..8ff21d38ff 100644
--- a/packages/ui/src/components/sort-button/sort-button.module.css
+++ b/packages/ui/src/components/sort-button/sort-button.module.css
@@ -3,7 +3,7 @@
display: inline-block;
border-radius: var(--radius-small);
position: relative;
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
}
.toggle {
@@ -17,7 +17,7 @@
display: block;
overflow: hidden;
right: -2px; /* compensate for the button padding */
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
transition: var(--transition-out);
}
@@ -35,7 +35,7 @@
.direction:hover,
.direction:active,
.direction:focus {
- color: var(--color-text-dark);
+ color: var(--color-text-intense);
transition: var(--transition-in);
}
diff --git a/packages/ui/src/context/index.ts b/packages/ui/src/context/index.ts
new file mode 100644
index 0000000000..7b1f54ecf9
--- /dev/null
+++ b/packages/ui/src/context/index.ts
@@ -0,0 +1 @@
+export * from './theme';
diff --git a/packages/ui/src/context/theme.tsx b/packages/ui/src/context/theme.tsx
new file mode 100644
index 0000000000..f209d33d01
--- /dev/null
+++ b/packages/ui/src/context/theme.tsx
@@ -0,0 +1,52 @@
+import type { ReactNode } from 'react';
+import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
+
+import { getCurrentTheme, switchTheme, useCookieTheme, type ThemeName } from '../utils/theme';
+
+type ThemeContextProps = {
+ name: ThemeName;
+ update: (newTheme: ThemeName) => void;
+};
+
+const ThemeContext = createContext({
+ name: 'light',
+ update: () => {},
+});
+
+export type ThemeProviderProps = {
+ children: ReactNode;
+};
+
+export const ThemeProvider = (props: ThemeProviderProps) => {
+ const { children } = props;
+
+ const [cookieValue, setCookieValue] = useCookieTheme();
+ const [theme, setTheme] = useState((cookieValue as ThemeName) || getCurrentTheme());
+
+ const updateTheme = useCallback(
+ (nextTheme: ThemeName) => {
+ setCookieValue(nextTheme);
+ switchTheme(nextTheme);
+ },
+ [setTheme, setCookieValue],
+ );
+
+ // Sync classNames on load
+ useEffect(() => {
+ switchTheme(theme);
+ }, [theme]);
+
+ const value = useMemo(
+ () => ({
+ name: theme,
+ update: updateTheme,
+ }),
+ [theme, updateTheme],
+ );
+
+ return {children};
+};
+
+export const useTheme = (): ThemeContextProps => {
+ return useContext(ThemeContext);
+};
diff --git a/packages/ui/src/css/default.css b/packages/ui/src/css/default.css
index fd5cb6750d..92fbb8fc05 100644
--- a/packages/ui/src/css/default.css
+++ b/packages/ui/src/css/default.css
@@ -60,6 +60,6 @@ a {
a:hover,
a:active,
a:focus {
- color: var(--color-primary-dark);
+ color: var(--color-primary-intense);
outline: none;
}
diff --git a/packages/ui/src/css/variables.css b/packages/ui/src/css/variables.css
index 3439061246..bb463b1d87 100644
--- a/packages/ui/src/css/variables.css
+++ b/packages/ui/src/css/variables.css
@@ -1,4 +1,231 @@
+/*
+ * Color scheme:
+ * https://coolors.co/287acc-da444e-55b950-efcc1a-192339
+ * https://hihayk.github.io/scale/#4/5/50/90/-51/0/0/14/353138/53/49/56/white
+ * https://www.radix-ui.com/colors/custom
+ */
+:root, .light-theme {
+ --branding-1: #fafdff;
+ --branding-2: #f2f9ff;
+ --branding-3: #e3f4ff;
+ --branding-4: #d3ecff;
+ --branding-5: #c2e2ff;
+ --branding-6: #aad5ff;
+ --branding-7: #8cc3ff;
+ --branding-8: #5fa9ff;
+ --branding-9: #1a477f;
+ --branding-10: #2b5892;
+ --branding-11: #1f6cc7;
+ --branding-12: #08376e;
+
+ --gray-1: #fcfcfe;
+ --gray-2: #f9f9fc;
+ --gray-3: #f0eff5;
+ --gray-4: #e8e7ef;
+ --gray-5: #e0e0ea;
+ --gray-6: #d9d8e3;
+ --gray-7: #cecddb;
+ --gray-8: #bbb9cc;
+ --gray-9: #8d8c9d;
+ --gray-10: #838192;
+ --gray-11: #646371;
+ --gray-12: #201f29;
+
+ --red-1: #fffcfc;
+ --red-2: #fff8f7;
+ --red-3: #feebea;
+ --red-4: #ffdcdb;
+ --red-5: #ffcecc;
+ --red-6: #fbbfbc;
+ --red-7: #f2aba9;
+ --red-8: #e8918f;
+ --red-9: #da444e;
+ --red-10: #cc3341;
+ --red-11: #c93340;
+ --red-12: #631a1f;
+
+ --blue-1: #fcfdff;
+ --blue-2: #f5f9fe;
+ --blue-3: #ebf3fc;
+ --blue-4: #ddecfe;
+ --blue-5: #cde2fc;
+ --blue-6: #b9d5f5;
+ --blue-7: #a1c4ed;
+ --blue-8: #7dade4;
+ --blue-9: #287acc;
+ --blue-10: #166cbd;
+ --blue-11: #2275c7;
+ --blue-12: #123559;
+
+ --green-1: #fbfefa;
+ --green-2: #f5fbf5;
+ --green-3: #e7f7e5;
+ --green-4: #d8f2d5;
+ --green-5: #c6eac2;
+ --green-6: #b0dfac;
+ --green-7: #93cf8e;
+ --green-8: #67bb62;
+ --green-9: #55b950;
+ --green-10: #4aad46;
+ --green-11: #2e7f2b;
+ --green-12: #223e20;
+
+ --yellow-1: #fefdfb;
+ --yellow-2: #fffbe9;
+ --yellow-3: #fff5c0;
+ --yellow-4: #ffec9b;
+ --yellow-5: #ffe278;
+ --yellow-6: #f5d674;
+ --yellow-7: #e2c568;
+ --yellow-8: #d0ae3b;
+ --yellow-9: #ffd00c;
+ --yellow-10: #f2c724;
+ --yellow-11: #947300;
+ --yellow-12: #443b1f;
+
+ --color-background-rgba: 255, 255, 255;
+ --color-background: rgba(var(--color-background-rgba), 1);
+ --color-backdrop: rgba(0, 0, 0, 0.05);
+ --color-shadow: rgba(0, 0, 0, 0.25);
+}
+
+.dark-theme {
+ --branding-1: #0a111c;
+ --branding-2: #0f1925;
+ --branding-3: #0f2746;
+ --branding-4: #0b3160;
+ --branding-5: #133d72;
+ --branding-6: #1e4b83;
+ --branding-7: #295a99;
+ --branding-8: #306bb6;
+ --branding-9: #3984e1;
+ --branding-10: #2b77d3;
+ --branding-11: #7fb7ff;
+ --branding-12: #cee3ff;
+
+ --gray-1: #111015;
+ --gray-2: #19191e;
+ --gray-3: #222229;
+ --gray-4: #2a2933;
+ --gray-5: #31303c;
+ --gray-6: #3a3947;
+ --gray-7: #474656;
+ --gray-8: #605f70;
+ --gray-9: #6e6c7e;
+ --gray-10: #7b7a8c;
+ --gray-11: #b3b2c5;
+ --gray-12: #eeeef2;
+
+ --red-1: #160f0f;
+ --red-2: #1f1313;
+ --red-3: #3a1314;
+ --red-4: #4f1116;
+ --red-5: #60191e;
+ --red-6: #712629;
+ --red-7: #8a3638;
+ --red-8: #b3474b;
+ --red-9: #da444e;
+ --red-10: #b94c50;
+ --red-11: #ff8e8e;
+ --red-12: #ffd2d0;
+
+ --blue-1: #08121d;
+ --blue-2: #0d1927;
+ --blue-3: #072848;
+ --blue-4: #003265;
+ --blue-5: #003e78;
+ --blue-6: #094d8a;
+ --blue-7: #175ea2;
+ --blue-8: #1c71c2;
+ --blue-9: #287acc;
+ --blue-10: #166cbd;
+ --blue-11: #6db9ff;
+ --blue-12: #c8e4ff;
+
+ --green-1: #0d130c;
+ --green-2: #141a13;
+ --green-3: #1b2a1a;
+ --green-4: #1e3a1c;
+ --green-5: #264824;
+ --green-6: #2e582b;
+ --green-7: #366833;
+ --green-8: #3e7a3a;
+ --green-9: #55b950;
+ --green-10: #48ad44;
+ --green-11: #71d26b;
+ --green-12: #bef2ba;
+
+ --yellow-1: #13110b;
+ --yellow-2: #1b1810;
+ --yellow-3: #2a230a;
+ --yellow-4: #382b00;
+ --yellow-5: #453500;
+ --yellow-6: #52430c;
+ --yellow-7: #65551e;
+ --yellow-8: #806c29;
+ --yellow-9: #ebc013;
+ --yellow-10: #e0b500;
+ --yellow-11: #fbd13d;
+ --yellow-12: #f9e9b8;
+
+ --color-background-rgba: 17, 17, 17;
+ --color-background: rgba(var(--color-background-rgba), 1);
+ --color-backdrop: rgba(255, 255, 255, 0.05);
+ --color-shadow: rgba(0, 0, 0, 0.6);
+}
+
:root {
+ /* Functional color variables */
+ --color-text-ultra-muted: var(--gray-8);
+ --color-text-muted: var(--gray-9);
+ --color-text: var(--gray-10);
+ --color-text-intense: var(--gray-11);
+ --color-heading: var(--gray-12);
+
+ --color-branding-muted: var(--branding-8);
+ --color-branding: var(--branding-10);
+ --color-branding-intense: var(--branding-12);
+
+ --color-success-muted: var(--green-8);
+ --color-success: var(--green-9);
+ --color-success-intense: var(--green-10);
+
+ --color-info-muted: var(--blue-8);
+ --color-info: var(--blue-10);
+ --color-info-intense: var(--blue-12);
+
+ --color-warning-muted: var(--yellow-9);
+ --color-warning: var(--yellow-10);
+ --color-warning-intense: var(--yellow-11);
+
+ --color-danger-muted: var(--red-8);
+ --color-danger: var(--red-9);
+ --color-danger-intense: var(--red-11);
+
+ --color-primary-muted: var(--color-info-muted);
+ --color-primary: var(--color-info);
+ --color-primary-intense: var(--color-info-intense);
+
+ --color-secondary-muted: var(--color-success-muted);
+ --color-secondary: var(--color-success);
+ --color-secondary-intense: var(--color-success-intense);
+
+ --color-highlight: var(--gray-2);
+ --color-highlight-primary: var(--blue-2);
+ --color-highlight-secondary: var(--green-2);
+ --color-highlight-success: var(--green-2);
+ --color-highlight-info: var(--blue-3);
+ --color-highlight-warning: var(--yellow-2);
+ --color-highlight-danger: var(--red-3);
+
+ --color-outline: var(--gray-4);
+ --color-outline-intense: var(--gray-5);
+ --color-outline-success: var(--green-4);
+ --color-outline-info: var(--blue-4);
+ --color-outline-warning: var(--yellow-5);
+ --color-outline-danger: var(--red-4);
+
+ /** Font family */
--font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;
--font-family-fixed: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
@@ -28,158 +255,6 @@
--line-height-heading: 1.33;
--line-height-number: 1.2em;
- /*
- * Color scheme:
- * https://coolors.co/287acc-da444e-55b950-efcc1a-192339
- * http://mcg.mbitson.com/#!?mcgpalette0=%23da444e&mcgpalette1=%2355b950&mcgpalette2=%23ebc013&mcgpalette3=%232a2933&mcgpalette4=%23287acc&themename=bundle-stats
- * https://hihayk.github.io/scale/#4/5/50/90/-51/0/0/14/353138/53/49/56/white
- */
- --color-gray-25: #f9f9f9;
- --color-gray-50: #e5e5e7;
- --color-gray-100: #d2d2d4;
- --color-gray-200: #959499;
- --color-gray-300: #6a6970;
- --color-gray-400: #4a4952;
- --color-gray-500: #2a2933;
- --color-gray-600: #25242e;
- --color-gray-700: #1f1f27;
- --color-gray-800: #191920;
- --color-gray-900: #0f0f14;
-
- --color-gray-ultra-light: var(--color-gray-200);
- --color-gray-light: var(--color-gray-300);
- --color-gray: var(--color-gray-500);
- --color-gray-dark: var(--color-gray-700);
- --color-gray-ultra-dark: var(--color-gray-900);
-
- --color-red-25: #fdf5f6;
- --color-red-50: #fbe9ea;
- --color-red-100: #f4c7ca;
- --color-red-200: #eda2a7;
- --color-red-300: #e57c83;
- --color-red-400: #e06069;
- --color-red-500: #da444e;
- --color-red-600: #d63e47;
- --color-red-700: #d0353d;
- --color-red-800: #cb2d35;
- --color-red-900: #c21f25;
- --color-red-ultra-light: var(--color-red-100);
- --color-red-light: var(--color-red-300);
- --color-red: var(--color-red-500);
- --color-red-dark: var(--color-red-700);
- --color-red-ultra-dark: var(--color-red-900);
-
- --color-blue-25: #f3f8fd;
- --color-blue-50: #e5eff9;
- --color-blue-100: #bfd7f0;
- --color-blue-200: #94bde6;
- --color-blue-300: #69a2db;
- --color-blue-400: #488ed4;
- --color-blue-500: #287acc;
- --color-blue-600: #2472c7;
- --color-blue-700: #1e67c0;
- --color-blue-800: #185db9;
- --color-blue-900: #0f4aad;
-
- --color-blue-ultra-light: var(--color-blue-100);
- --color-blue-light: var(--color-blue-300);
- --color-blue: var(--color-blue-500);
- --color-blue-dark: var(--color-blue-700);
- --color-blue-ultra-dark: var(--color-blue-900);
-
- --color-green-25: #f6fbf6;
- --color-green-50: #ebf7ea;
- --color-green-100: #cceacb;
- --color-green-200: #aadca8;
- --color-green-300: #88ce85;
- --color-green-400: #6fc46a;
- --color-green-500: #55b950;
- --color-green-600: #4eb249;
- --color-green-700: #44aa40;
- --color-green-800: #3ba237;
- --color-green-900: #2a9327;
-
- --color-green-ultra-light: var(--color-green-100);
- --color-green-light: var(--color-green-300);
- --color-green: var(--color-green-500);
- --color-green-dark: var(--color-green-700);
- --color-green-ultra-dark: var(--color-green-900);
-
- --color-yellow-25: #fffdf2;
- --color-yellow-50: #fdf7e3;
- --color-yellow-100: #f9ecb8;
- --color-yellow-200: #f5e089;
- --color-yellow-300: #f1d35a;
- --color-yellow-400: #eec936;
- --color-yellow-500: #ebc013;
- --color-yellow-600: #e9ba11;
- --color-yellow-700: #e5b20e;
- --color-yellow-800: #e2aa0b;
- --color-yellow-900: #dd9c06;
-
- --color-yellow-light-background: var(--color-yellow-25);
- --color-yellow-ultra-light: var(--color-yellow-100);
- --color-yellow-light: var(--color-yellow-300);
- --color-yellow: var(--color-yellow-500);
- --color-yellow-ultra-dark: var(--color-yellow-700);
- --color-yellow-dark: var(--color-yellow-900);
-
- --color-dark: #111;
- --color-light: #fff;
-
- /* Functional color variables */
- --color-branding-light: #2B76D4;
- --color-branding: #1a477f;
- --color-branding-dark: #09182A;
-
- --color-text-ultra-light: var(--color-gray-ultra-light);
- --color-text-light: var(--color-gray-light);
- --color-text: var(--color-gray);
- --color-text-dark: var(--color-gray-dark);
- --color-text-ultra-dark: var(--color-gray-ultra-dark);
- --color-heading: var(--color-gray-ultra-dark);
-
- --color-success-light: var(--color-green-light);
- --color-success: var(--color-green);
- --color-success-dark: var(--color-green-dark);
- --color-info-light: var(--color-blue-light);
- --color-info: var(--color-blue);
- --color-info-dark: var(--color-blue-dark);
- --color-warning-light: var(--color-yellow-light);
- --color-warning: var(--color-yellow);
- --color-warning-dark: var(--color-yellow-dark);
- --color-danger-light: var(--color-red-light);
- --color-danger: var(--color-red);
- --color-danger-dark: var(--color-red-dark);
-
- --color-primary-light: var(--color-blue-light);
- --color-primary: var(--color-blue);
- --color-primary-dark: var(--color-blue-dark);
-
- --color-secondary-light: var(--color-green-light);
- --color-secondary: var(--color-green);
- --color-secondary-dark: var(--color-green-dark);
-
- --color-background: #fff;
- --color-highlight: var(--color-gray-25);
- --color-highlight-primary: var(--color-blue-25);
- --color-highlight-secondary: var(--color-green-25);
- --color-highlight-success: var(--color-green-25);
- --color-highlight-info: var(--color-blue-25);
- --color-highlight-warning: var(--color-yellow-25);
- --color-highlight-danger: var(--color-red-25);
-
- --color-outline: var(--color-gray-50);
- --color-outline-dark: var(--color-gray-100);
-
- --color-outline-success: var(--color-green-100);
- --color-outline-info: var(--color-blue-100);
- --color-outline-warning: var(--color-yellow-200);
- --color-outline-danger: var(--color-red-100);
-
- --color-backdrop: rgba(0, 0, 0, 0.05);
- --color-shadow: rgba(0, 0, 0, 0.25);
-
--transition-duration-in: 0.2s;
--transition-duration-out: 0.17s;
--transition-in: all var(--transition-duration-in) ease-in;
@@ -211,3 +286,8 @@
--entry-info-width: 64rem;
--entry-info-top: var(--header-height);
}
+
+.no-motion * {
+ transition: none !important;
+ animation: none !important;
+}
diff --git a/packages/ui/src/index.js b/packages/ui/src/index.js
index eb3b4cb0ef..42305df59b 100644
--- a/packages/ui/src/index.js
+++ b/packages/ui/src/index.js
@@ -1,5 +1,6 @@
export * from './ui';
export * from './layout';
+export * from './context';
export * from './components';
export * from './utils';
export * from './app';
diff --git a/packages/ui/src/layout/box/box.module.css b/packages/ui/src/layout/box/box.module.css
index 45a70f4fa5..9ace28804f 100644
--- a/packages/ui/src/layout/box/box.module.css
+++ b/packages/ui/src/layout/box/box.module.css
@@ -24,7 +24,7 @@
.outlineHover:focus,
.outlineHover:active,
.outlineHover:hover {
- border-color: var(--color-outline-dark);
+ border-color: var(--color-outline-intense);
transition: var(--transition-in);
}
diff --git a/packages/ui/src/layout/footer/footer.module.css b/packages/ui/src/layout/footer/footer.module.css
index fd4d868a78..5a929b4161 100644
--- a/packages/ui/src/layout/footer/footer.module.css
+++ b/packages/ui/src/layout/footer/footer.module.css
@@ -14,6 +14,6 @@
.navItem + .navItem::before {
content: '•';
margin-right: var(--space-xxsmall);
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
font-size: var(--size-large);
}
diff --git a/packages/ui/src/prototypes/typography.module.css b/packages/ui/src/prototypes/typography.module.css
index e06d43fc13..5b3851bf29 100644
--- a/packages/ui/src/prototypes/typography.module.css
+++ b/packages/ui/src/prototypes/typography.module.css
@@ -8,7 +8,7 @@
align-items: center;
font-size: var(--size-xlarge);
font-weight: 700;
- color: var(--color-light);
+ color: var(--color-background);
}
.headerLogo {
diff --git a/packages/ui/src/prototypes/typography.stories.jsx b/packages/ui/src/prototypes/typography.stories.jsx
index fbdfb93b9d..5d3dec1679 100644
--- a/packages/ui/src/prototypes/typography.stories.jsx
+++ b/packages/ui/src/prototypes/typography.stories.jsx
@@ -12,22 +12,20 @@ export default {
};
export const Typography = () => (
-
-
-
+
+
+
);
// eslint-disable-next-line react/prop-types
const Item = ({ colorName, valueName = 'normal' }) => {
- const colorFullName = valueName === 'normal'
- ? colorName
- : [colorName, valueName].join('-');
+ const colorFullName = valueName === 'normal' ? colorName : [colorName, valueName].join('-');
return (
(
);
-const COLORS = [
- 'blue',
- 'red',
- 'green',
- 'yellow',
- 'gray',
-];
+const COLORS = ['branding', 'primary', 'secondary', 'success', 'info', 'warning', 'danger'];
-const NAMES = [
- 'ultra-light',
- 'light',
- 'normal',
- 'dark',
- 'ultra-dark',
-];
+const NAMES = ['muted', 'normal', 'intense'];
export const ColorScheme = () => (
-
-
-
-
-
-
-
+
+
+
+
+
{COLORS.map((colorName) => (
{NAMES.map((valueName) => (
-
+
))}
))}
@@ -96,10 +76,7 @@ export const ColorScheme = () => (
Chart colors
{CHART_COLORS.map((color) => (
-
+
))}
diff --git a/packages/ui/src/ui/button/button.module.css b/packages/ui/src/ui/button/button.module.css
index 1d27662ab7..70eab4c1d8 100644
--- a/packages/ui/src/ui/button/button.module.css
+++ b/packages/ui/src/ui/button/button.module.css
@@ -13,7 +13,7 @@
border: var(--border-width) solid transparent;
background: none;
transition: var(--transition-out);
- color: var(--color-text-ultra-light);
+ color: var(--color-text-muted);
font-family: var(--font-family);
white-space: nowrap;
}
@@ -26,7 +26,7 @@ a.root {
.root:active,
.root:focus {
outline: none;
- color: var(--color-text-light);
+ color: var(--color-text);
transition: var(--transition-in);
}
@@ -49,7 +49,7 @@ a.root {
.primary:hover,
.primary:active,
.primary:focus {
- color: var(--color-primary-dark);
+ color: var(--color-primary-intense);
}
.secondary {
@@ -59,7 +59,7 @@ a.root {
.secondary:hover,
.secondary:active,
.secondary:focus {
- color: var(--color-secondary-dark);
+ color: var(--color-secondary-intense);
}
.danger {
@@ -69,7 +69,7 @@ a.root {
.danger:hover,
.danger:active,
.danger:focus {
- color: var(--color-danger-dark);
+ color: var(--color-danger-intense);
}
.warning {
@@ -79,7 +79,7 @@ a.root {
.warning:hover,
.warning:active,
.warning:focus {
- color: var(--color-warning-dark);
+ color: var(--color-warning-intense);
}
.info {
@@ -89,7 +89,7 @@ a.root {
.info:hover,
.info:active,
.info:focus {
- color: var(--color-info-dark);
+ color: var(--color-info-intense);
}
.success {
@@ -99,21 +99,21 @@ a.root {
.success:hover,
.success:active,
.success:focus {
- color: var(--color-success-dark);
+ color: var(--color-success-intense);
}
/**
* Solid Kind variation
*/
.solid {
- background: var(--color-text-ultra-light);
+ background: var(--color-text-ultra-muted);
color: var(--color-background);
}
.solid:hover,
.solid:active,
.solid:focus {
- background: var(--color-text-light);
+ background: var(--color-text-muted);
color: var(--color-background);
}
@@ -124,7 +124,7 @@ a.root {
.solid--primary:hover,
.solid--primary:active,
.solid--primary:focus {
- background: var(--color-primary-dark);
+ background: var(--color-primary-intense);
}
.solid--secondary {
@@ -134,7 +134,7 @@ a.root {
.solid--secondary:hover,
.solid--secondary:active,
.solid--secondary:focus {
- background: var(--color-secondary-dark);
+ background: var(--color-secondary-intense);
}
.solid--danger {
@@ -144,7 +144,7 @@ a.root {
.solid--danger:hover,
.solid--danger:active,
.solid--danger:focus {
- background: var(--color-danger-dark);
+ background: var(--color-danger-intense);
}
.solid--warning {
@@ -154,7 +154,7 @@ a.root {
.solid--warning:hover,
.solid--warning:active,
.solid--warning:focus {
- background: var(--color-warning-dark);
+ background: var(--color-warning-intense);
}
.solid--info {
@@ -164,7 +164,7 @@ a.root {
.solid--info:hover,
.solid--info:active,
.solid--info:focus {
- background: var(--color-info-dark);
+ background: var(--color-info-intense);
}
.solid--success {
@@ -174,7 +174,7 @@ a.root {
.solid--success:hover,
.solid--success:active,
.solid--success:focus {
- background: var(--color-success-dark);
+ background: var(--color-success-intense);
}
/**
@@ -227,7 +227,7 @@ a.root {
.outline:hover,
.outline:active,
.outline:focus {
- border-color: var(--color-outline-dark);
+ border-color: var(--color-outline-intense);
}
.outline--primary {
@@ -237,7 +237,7 @@ a.root {
.outline--primary:hover,
.outline--primary:active,
.outline--primary:focus {
- border-color: var(--color-outline-primary-dark);
+ border-color: var(--color-outline-primary-intense);
}
.outline--secondary {
@@ -247,7 +247,7 @@ a.root {
.outline--secondary:hover,
.outline--secondary:active,
.outline--secondary:focus {
- border-color: var(--color-outline-secondary-dark);
+ border-color: var(--color-outline-secondary-intense);
}
.outline--danger {
@@ -257,7 +257,7 @@ a.root {
.outline--danger:hover,
.outline--danger:active,
.outline--danger:focus {
- border-color: var(--color-outline-danger-dark);
+ border-color: var(--color-outline-danger-intense);
}
.outline--warning {
@@ -267,7 +267,7 @@ a.root {
.outline--warning:hover,
.outline--warning:active,
.outline--warning:focus {
- border-color: var(--color-outline-warning-dark);
+ border-color: var(--color-outline-warning-intense);
}
.outline--info {
@@ -277,7 +277,7 @@ a.root {
.outline--info:hover,
.outline--info:active,
.outline--info:focus {
- border-color: var(--color-outline-info-dark);
+ border-color: var(--color-outline-info-intense);
}
.outline--success {
@@ -287,7 +287,7 @@ a.root {
.outline--success:hover,
.outline--success:active,
.outline--success:focus {
- border-color: var(--color-outline-success-dark);
+ border-color: var(--color-outline-success-intense);
}
diff --git a/packages/ui/src/ui/copy-to-clipboard/copy-to-clipboard.module.css b/packages/ui/src/ui/copy-to-clipboard/copy-to-clipboard.module.css
index 549b7c205d..9861eeeaff 100644
--- a/packages/ui/src/ui/copy-to-clipboard/copy-to-clipboard.module.css
+++ b/packages/ui/src/ui/copy-to-clipboard/copy-to-clipboard.module.css
@@ -1,7 +1,7 @@
.root {
white-space: nowrap;
/** override tooltip styles */
- color: var(--color-text-ultra-light) !important;
+ color: var(--color-text-ultra-muted) !important;
}
/** done state */
diff --git a/packages/ui/src/ui/dialog/dialog.module.css b/packages/ui/src/ui/dialog/dialog.module.css
index 91f9bd890d..eb83037d2b 100644
--- a/packages/ui/src/ui/dialog/dialog.module.css
+++ b/packages/ui/src/ui/dialog/dialog.module.css
@@ -47,13 +47,13 @@
top: var(--space-xxsmall);
padding: var(--space-xxxsmall);
flex: 0 0 auto;
- color: var(--color-outline-dark);
+ color: var(--color-outline-intense);
}
.headerClose:hover,
.headerClose:focus,
.headerClose:active {
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
}
.content {
diff --git a/packages/ui/src/ui/dropdown/dropdown.module.css b/packages/ui/src/ui/dropdown/dropdown.module.css
index 43ce8e2379..71c0c80281 100644
--- a/packages/ui/src/ui/dropdown/dropdown.module.css
+++ b/packages/ui/src/ui/dropdown/dropdown.module.css
@@ -3,7 +3,7 @@
}
.button[disabled] {
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
}
.dropdown {
@@ -32,7 +32,7 @@
padding: calc(var(--space-xxxsmall) + var(--space-xxxsmall) / 2);
background: none;
text-align: left;
- color: var(--color-text-light);
+ color: var(--color-text-muted);
font-size: var(--size-small);
line-height: var(--line-height);
text-decoration: none;
@@ -56,5 +56,5 @@
.itemActive:focus,
.itemActive:active {
background: var(--color-highlight-info);
- color: var(--color-primary-dark);
+ color: var(--color-primary-intense);
}
diff --git a/packages/ui/src/ui/empty-set/empty-set.module.css b/packages/ui/src/ui/empty-set/empty-set.module.css
index 95a169861d..c2b63d373b 100644
--- a/packages/ui/src/ui/empty-set/empty-set.module.css
+++ b/packages/ui/src/ui/empty-set/empty-set.module.css
@@ -1,3 +1,3 @@
.root {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
diff --git a/packages/ui/src/ui/filters/filters.module.css b/packages/ui/src/ui/filters/filters.module.css
index 22b3c0d1a4..d4d399d10d 100644
--- a/packages/ui/src/ui/filters/filters.module.css
+++ b/packages/ui/src/ui/filters/filters.module.css
@@ -9,13 +9,14 @@
position: relative;
width: 100%;
max-width: 240px;
- color: var(--color-text);
+ color: var(--color-text-muted);
}
.filterControl {
margin: 0;
flex: 0 0 auto;
height: 1em;
+ background: var(--color-background);
}
.filterLabel {
@@ -27,11 +28,11 @@
}
.filterControl[disabled] + .filterLabel {
- color: var(--color-text-light);
+ color: var(--color-text-ultra-muted);
}
.filter:hover {
- color: var(--color-dark);
+ color: var(--color-text);
}
/** Filter single */
@@ -41,14 +42,15 @@
border-radius: var(--radius-small);
font-size: var(--size-small);
line-height: var(--space-small);
- color: var(--color-text-ultra-light);
+ color: var(--color-text-muted);
transition: var(--transition-out);
}
.filterSingle:hover,
.filterSingle:active,
.filterSingle:focus {
- border-color: var(--color-outline-dark);
+ border-color: var(--color-outline-intense);
+ color: var(--color-text);
transition: var(--transition-in);
}
@@ -87,7 +89,7 @@
right: 100%;
bottom: 0;
width: var(--space-small);
- background: linear-gradient(90deg, rgba(255, 255, 255, 0), var(--color-highlight) 75%);
+ background: linear-gradient(90deg, rgba(var(--color-background-rgb), 0), var(--color-highlight) 75%);
}
/** filter hover state */
@@ -111,7 +113,7 @@
padding: var(--space-xxsmall) 0;
text-align: center;
font-size: var(--size-small);
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.filterGroupActions {
diff --git a/packages/ui/src/ui/hover-card/hover-card.module.css b/packages/ui/src/ui/hover-card/hover-card.module.css
index e5ecc95b17..0fde739352 100644
--- a/packages/ui/src/ui/hover-card/hover-card.module.css
+++ b/packages/ui/src/ui/hover-card/hover-card.module.css
@@ -25,6 +25,6 @@
outline: none;
filter: drop-shadow(var(--shadow-layer-high));
will-change: filter;
- background: var(--color-light);
+ background: var(--color-background);
font-size: var(--size-small);
}
diff --git a/packages/ui/src/ui/icon/icon.tsx b/packages/ui/src/ui/icon/icon.tsx
index c25805358b..edfeff537b 100644
--- a/packages/ui/src/ui/icon/icon.tsx
+++ b/packages/ui/src/ui/icon/icon.tsx
@@ -7,26 +7,29 @@ const ICONS = {
ARROW: 'arrow',
ARROW_RIGHT_CIRCLE: 'arrow-right-circle',
CANCEL: 'close',
+ CHECK: 'check',
CHEVRON_DOWN: 'chevron-down',
CHEVRON_UP: 'chevron-up',
- CHECK: 'check',
- CLOSE: 'close',
- CLOCK: 'clock',
- COMMIT: 'commit',
CLIPBOARD: 'clipboard',
CLIPBOARD_CHECK: 'clipboard-check',
+ CLOCK: 'clock',
+ CLOSE: 'close',
+ COMMIT: 'commit',
DOWNLOAD: 'download',
ERROR: 'error',
EXTERNAL_LINK: 'external-link',
FILTER: 'filter',
GITHUB: 'github',
- INFO: 'info',
HELP: 'help',
- TABLE: 'table',
- TREEMAP: 'treemap',
+ INFO: 'info',
MENU: 'menu',
+ MONITOR: 'monitor',
+ MOON: 'moon',
MORE_VERTICAL: 'more-vertical',
SORT: 'sort',
+ SUN: 'sun',
+ TABLE: 'table',
+ TREEMAP: 'treemap',
WARNING: 'warning',
} as const;
diff --git a/packages/ui/src/ui/input/input.module.css b/packages/ui/src/ui/input/input.module.css
index 7c74ecdfd1..ff21b2454f 100644
--- a/packages/ui/src/ui/input/input.module.css
+++ b/packages/ui/src/ui/input/input.module.css
@@ -1,7 +1,8 @@
.root {
border: 1px solid var(--color-outline);
border-radius: var(--radius-small);
- color: var(--color-text-light);
+ color: var(--color-text-muted);
+ background: var(--color-background);
width: 100%;
transition: var(--transition-in);
appearance: none;
@@ -9,23 +10,23 @@
}
.root::-webkit-input-placeholder {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.root::-moz-placeholder {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.root:-ms-input-placeholder {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.root:-moz-placeholder {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.root:focus {
- border-color: var(--color-outline-dark);
+ border-color: var(--color-outline-intense);
color: inherit;
transition: var(--transition-out);
}
diff --git a/packages/ui/src/ui/tabs/tabs.module.css b/packages/ui/src/ui/tabs/tabs.module.css
index be926f0eee..960d336045 100644
--- a/packages/ui/src/ui/tabs/tabs.module.css
+++ b/packages/ui/src/ui/tabs/tabs.module.css
@@ -10,7 +10,7 @@
position: relative;
display: inline-block;
padding: var(--space-xsmall) var(--space-small);
- color: var(--color-text-ultra-light);
+ color: var(--color-text-ultra-muted);
font-weight: bold;
text-decoration: none;
transition: var(--ui-transition-out);
diff --git a/packages/ui/src/ui/tag/tag.module.css b/packages/ui/src/ui/tag/tag.module.css
index bcc30aa849..2f881a818a 100644
--- a/packages/ui/src/ui/tag/tag.module.css
+++ b/packages/ui/src/ui/tag/tag.module.css
@@ -2,8 +2,8 @@
display: inline-block;
font-family: var(--font-family);
font-weight: bold;
- background: var(--color-text-ultra-light);
- color: var(--color-light);
+ background: var(--color-text-ultra-muted);
+ color: var(--color-background);
text-align: center;
text-transform: uppercase;
}
@@ -17,7 +17,7 @@ a.root:hover,
a.root:focus,
a.root:active {
opacity: 0.8;
- color: var(--color-light);
+ color: var(--color-background);
text-decoration: none;
transition: var(--transition-in);
}
@@ -32,7 +32,7 @@ a.root:active {
}
.warning {
- background: var(--color-warning-dark);
+ background: var(--color-warning-intense);
}
.danger {
diff --git a/packages/ui/src/ui/textarea/textarea.module.css b/packages/ui/src/ui/textarea/textarea.module.css
index 3adf14eb88..36e5439b9b 100644
--- a/packages/ui/src/ui/textarea/textarea.module.css
+++ b/packages/ui/src/ui/textarea/textarea.module.css
@@ -1,7 +1,7 @@
.root {
border: 1px solid var(--color-outline);
border-radius: var(--radius-small);
- color: var(--color-text-light);
+ color: var(--color-text-muted);
width: 100%;
resize: vertical;
transition: var(--transition-in);
@@ -10,29 +10,29 @@
}
.root::-webkit-input-placeholder {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.root::-moz-placeholder {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.root:-ms-input-placeholder {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.root:-moz-placeholder {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
.root:focus {
- border-color: var(--color-outline-dark);
+ border-color: var(--color-outline-intense);
color: inherit;
transition: var(--transition-out);
}
.root[readonly=""] {
- color: var(--color-text-light);
+ color: var(--color-text-muted);
}
/** Size modifier */
diff --git a/packages/ui/src/ui/tooltip/tooltip.module.css b/packages/ui/src/ui/tooltip/tooltip.module.css
index d18a1d3eff..d6517e7975 100644
--- a/packages/ui/src/ui/tooltip/tooltip.module.css
+++ b/packages/ui/src/ui/tooltip/tooltip.module.css
@@ -14,7 +14,7 @@
z-index: var(--layer-high);
padding: var(--space-xxxsmall) var(--space-xxsmall);
border-radius: var(--radius-small);
- background: var(--color-text);
+ background: var(--color-text-intense);
max-width: 28em;
color: var(--color-background);
font-size: var(--size-small);
@@ -25,5 +25,5 @@
.arrow svg {
display: block;
- fill: var(--color-text);
+ fill: var(--color-text-intense);
}
diff --git a/packages/ui/src/utils/index.ts b/packages/ui/src/utils/index.ts
index 0c490affe6..7f522192be 100644
--- a/packages/ui/src/utils/index.ts
+++ b/packages/ui/src/utils/index.ts
@@ -1,3 +1,4 @@
export * from './colors';
+export * from './theme';
export * from './jobs';
export * from './components';
diff --git a/packages/ui/src/utils/theme.ts b/packages/ui/src/utils/theme.ts
new file mode 100644
index 0000000000..d6321be7ea
--- /dev/null
+++ b/packages/ui/src/utils/theme.ts
@@ -0,0 +1,53 @@
+import { useCookie } from 'react-use';
+import Cookies from 'js-cookie';
+import { wait } from '@bundle-stats/utils';
+
+export type ThemeName = 'light' | 'dark';
+
+// classList required timeout to allow to disable motion during the switch
+const CLASSLIST_UPDATE_TIMEOUT = 10;
+
+const COOKIE_NAME = 'theme';
+
+export const getCurrentTheme = (): ThemeName => {
+ const { matches } = window.matchMedia('(prefers-color-scheme: dark)');
+ return matches ? 'dark' : 'light';
+};
+
+export const getCookieTheme = (): ThemeName | null => {
+ const nextTheme = Cookies.get(COOKIE_NAME);
+
+ if (!nextTheme) {
+ return null;
+ }
+
+ if (['light', 'dark'].includes(nextTheme)) {
+ return nextTheme as ThemeName;
+ }
+
+ return null;
+};
+
+export const switchTheme = async (newTheme: ThemeName) => {
+ const htmlElm = document.querySelector('html');
+
+ htmlElm?.classList.add('no-motion');
+
+ await wait(CLASSLIST_UPDATE_TIMEOUT);
+
+ if (newTheme === 'dark') {
+ htmlElm?.classList.remove('light-theme');
+ htmlElm?.classList.add('dark-theme');
+ } else {
+ htmlElm?.classList.remove('dark-theme');
+ htmlElm?.classList.add('light-theme');
+ }
+
+ await wait(CLASSLIST_UPDATE_TIMEOUT);
+
+ htmlElm?.classList.remove('no-motion');
+};
+
+export const useCookieTheme = () => {
+ return useCookie(COOKIE_NAME);
+};
diff --git a/packages/utils/src/utils/index.js b/packages/utils/src/utils/index.js
index ec2361b496..d95124df60 100644
--- a/packages/utils/src/utils/index.js
+++ b/packages/utils/src/utils/index.js
@@ -5,3 +5,4 @@ export * from './insights';
export * from './file-types';
export * from './format';
export * from './metrics';
+export * from './wait';
diff --git a/packages/utils/src/utils/wait.ts b/packages/utils/src/utils/wait.ts
new file mode 100644
index 0000000000..7b1a4bf9c8
--- /dev/null
+++ b/packages/utils/src/utils/wait.ts
@@ -0,0 +1,6 @@
+export const wait = (timeout = 0): Promise =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ return resolve();
+ }, timeout);
+});