diff --git a/ui/app/components/app/account-menu/index.scss b/ui/app/components/app/account-menu/index.scss
index ee3e82bc0..39c5b97f9 100644
--- a/ui/app/components/app/account-menu/index.scss
+++ b/ui/app/components/app/account-menu/index.scss
@@ -203,7 +203,7 @@
}
&__check-mark-icon {
- background-image: url("images/check-white.svg");
+ background-image: url("/images/check-white.svg");
height: 18px;
width: 18px;
background-repeat: no-repeat;
diff --git a/ui/app/components/ui/color-indicator/color-indicator.js b/ui/app/components/ui/color-indicator/color-indicator.js
new file mode 100644
index 000000000..83718a0f7
--- /dev/null
+++ b/ui/app/components/ui/color-indicator/color-indicator.js
@@ -0,0 +1,50 @@
+import React from 'react'
+import classnames from 'classnames'
+import PropTypes from 'prop-types'
+import { COLORS } from '../../../helpers/constants/design-system'
+
+export default function ColorIndicator({
+ size = 'small',
+ type = 'outlined',
+ color = COLORS.UI4,
+ borderColor,
+ iconClassName,
+}) {
+ const colorIndicatorClassName = classnames('color-indicator', {
+ 'color-indicator--filled': type === 'filled' || Boolean(iconClassName),
+ 'color-indicator--partial-filled': type === 'partial-filled',
+ [`color-indicator--border-color-${borderColor}`]: Boolean(borderColor),
+ [`color-indicator--color-${color}`]: true,
+ [`color-indicator--size-${size}`]: true,
+ })
+
+ return (
+
+ {iconClassName ? (
+
+ ) : (
+
+ )}
+
+ )
+}
+
+ColorIndicator.SIZES = {
+ LARGE: 'large',
+ MEDIUM: 'medium',
+ SMALL: 'small,',
+}
+
+ColorIndicator.TYPES = {
+ FILLED: 'filled',
+ PARTIAL: 'partial-filled',
+ OUTLINE: 'outline',
+}
+
+ColorIndicator.propTypes = {
+ color: PropTypes.oneOf(Object.values(COLORS)),
+ borderColor: PropTypes.oneOf(Object.values(COLORS)),
+ size: PropTypes.oneOf(Object.values(ColorIndicator.SIZES)),
+ iconClassName: PropTypes.string,
+ type: PropTypes.oneOf(Object.values(ColorIndicator.TYPES)),
+}
diff --git a/ui/app/components/ui/color-indicator/color-indicator.scss b/ui/app/components/ui/color-indicator/color-indicator.scss
new file mode 100644
index 000000000..b6eb93104
--- /dev/null
+++ b/ui/app/components/ui/color-indicator/color-indicator.scss
@@ -0,0 +1,61 @@
+@use "utilities";
+@use "design-system";
+
+$sizes: (
+ 'large': 6,
+ 'medium': 5,
+ 'small': 4,
+);
+
+.color-indicator {
+ $self: &;
+
+ border: 1px solid transparent;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &__inner-circle {
+ background-color: transparent;
+ }
+
+ @each $variant, $size in $sizes {
+ &--size-#{$variant} {
+ height: #{2 * $size}px;
+ width: #{2 * $size}px;
+ border-radius: #{$size}px;
+
+ #{$self}__inner-circle {
+ border-radius: #{$size}px;
+ height: #{$size}px;
+ width: #{$size}px;
+ }
+
+ #{$self}__icon {
+ font-size: #{1.25 * $size}px;
+ }
+ }
+ }
+
+ @each $variant, $color in design-system.$color-map {
+ &--color-#{$variant} {
+ border-color: $color;
+ {$self}--partial-filled #{$self}__inner-circle {
+ background-color: $color;
+ }
+ {$self}--filled {
+ background-color: $color;
+ }
+ & #{$self}__icon {
+ color: #{utilities.choose-contrast-color($color)};
+ }
+ }
+ }
+
+ // separate iterator to ensure borderColor takes precedence
+ @each $variant, $color in design-system.$color-map {
+ &--border-color-#{$variant} {
+ border-color: $color;
+ }
+ }
+}
diff --git a/ui/app/components/ui/color-indicator/color-indicator.stories.js b/ui/app/components/ui/color-indicator/color-indicator.stories.js
new file mode 100644
index 000000000..81d7a2159
--- /dev/null
+++ b/ui/app/components/ui/color-indicator/color-indicator.stories.js
@@ -0,0 +1,27 @@
+import React from 'react'
+import { select } from '@storybook/addon-knobs'
+import { COLORS } from '../../../helpers/constants/design-system'
+import ColorIndicator from './color-indicator'
+
+export default {
+ title: 'ColorIndicator',
+}
+
+export const colorIndicator = () => (
+
+)
+
+export const withIcon = () => (
+
+)
diff --git a/ui/app/components/ui/color-indicator/index.js b/ui/app/components/ui/color-indicator/index.js
new file mode 100644
index 000000000..257ecdc0b
--- /dev/null
+++ b/ui/app/components/ui/color-indicator/index.js
@@ -0,0 +1 @@
+export { default } from './color-indicator'
diff --git a/ui/app/components/ui/ui-components.scss b/ui/app/components/ui/ui-components.scss
index f7f18b0a2..2317072e1 100644
--- a/ui/app/components/ui/ui-components.scss
+++ b/ui/app/components/ui/ui-components.scss
@@ -9,6 +9,7 @@
@import 'check-box/index';
@import 'chip/chip';
@import 'circle-icon/index';
+@import 'color-indicator/color-indicator';
@import 'currency-display/index';
@import 'currency-input/index';
@import 'dialog/dialog';
diff --git a/ui/app/css/design-system/typography.scss b/ui/app/css/design-system/typography.scss
index ff0725ea8..73ccab414 100644
--- a/ui/app/css/design-system/typography.scss
+++ b/ui/app/css/design-system/typography.scss
@@ -1,4 +1,4 @@
-$fa-font-path: 'fonts/fontawesome';
+$fa-font-path: '/fonts/fontawesome';
@import '../../../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome';
@import '../../../../node_modules/@fortawesome/fontawesome-free/scss/solid';
@@ -8,63 +8,63 @@ $fa-font-path: 'fonts/fontawesome';
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
- src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
+ src: local('Roboto Thin'), local('Roboto-Thin'), url('/fonts/Roboto/Roboto-Thin.ttf') format('truetype');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
- src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
+ src: local('Roboto Light'), local('Roboto-Light'), url('/fonts/Roboto/Roboto-Light.ttf') format('truetype');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
- src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
+ src: local('Roboto'), local('Roboto-Regular'), url('/fonts/Roboto/Roboto-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
- src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
+ src: local('Roboto Medium'), local('Roboto-Medium'), url('/fonts/Roboto/Roboto-Medium.ttf') format('truetype');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
- src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
+ src: local('Roboto Bold'), local('Roboto-Bold'), url('/fonts/Roboto/Roboto-Bold.ttf') format('truetype');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
- src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
+ src: local('Roboto Black'), local('Roboto-Black'), url('/fonts/Roboto/Roboto-Black.ttf') format('truetype');
}
@font-face {
font-family: 'Euclid';
font-style: normal;
font-weight: 400;
- src: url('fonts/Euclid/EuclidCircularB-Regular-WebXL.ttf') format('truetype');
+ src: url('/fonts/Euclid/EuclidCircularB-Regular-WebXL.ttf') format('truetype');
}
@font-face {
font-family: 'Euclid';
font-style: italic;
font-weight: 400;
- src: url('fonts/Euclid/EuclidCircularB-RegularItalic-WebXL.ttf') format('truetype');
+ src: url('/fonts/Euclid/EuclidCircularB-RegularItalic-WebXL.ttf') format('truetype');
}
@font-face {
font-family: 'Euclid';
font-style: normal;
font-weight: 700;
- src: url('fonts/Euclid/EuclidCircularB-Bold-WebXL.ttf') format('truetype');
+ src: url('/fonts/Euclid/EuclidCircularB-Bold-WebXL.ttf') format('truetype');
}
$font-family: Euclid, Roboto, Helvetica, Arial, sans-serif;
diff --git a/ui/app/css/utilities/_colors.scss b/ui/app/css/utilities/_colors.scss
new file mode 100644
index 000000000..da0cb9a66
--- /dev/null
+++ b/ui/app/css/utilities/_colors.scss
@@ -0,0 +1,75 @@
+@use "sass:math";
+@use "sass:map";
+
+/**
+ * Calculate the luminance for a color.
+ * See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
+ *
+ * Note:
+ * @Gudahtt and @brad-decker have identified a potential performance concern
+ * that **might** impact build times if this function becomes widely used. It
+ * has not been validated, and is predicated on user-land math.pow functions
+ * prior to dart-sass 1.25.0. However, if the build times of sass raise
+ * exponentially a lookup table of all possible values for the math.pow call
+ * can be used to bypass the issue. This will require some logic updates as
+ * well. For reference, the blog post where the performance concern was
+ * originally made is here https://bit.ly/3c3qXzq (css-tricks)
+ */
+@function luminance($color) {
+ $channels: (
+ 'red': red($color),
+ 'green': green($color),
+ 'blue': blue($color),
+ );
+
+ $values: (
+ 'red': map.get($channels, 'red') / 255,
+ 'blue': map.get($channels, 'red') / 255,
+ 'green': map.get($channels, 'red') / 255,
+ );
+
+ @each $name, $value in $values {
+ @if $value <= 0.03928 {
+ $value: $value / 12.92;
+ }
+
+ @else {
+ $value: ($value + 0.055) / 1.055;
+ $value: math.pow($value, 2.4);
+ }
+
+ $values: map.merge($values, ($name: $value));
+ }
+
+ @return (0.2126 * map.get($values, 'red'))
+ + (0.7152 * map.get($values, 'green'))
+ + (0.0722 * map.get($values, 'blue'));
+}
+
+/**
+ * Calculate the contrast ratio between two colors.
+ * See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
+ */
+@function contrast($back, $front) {
+ $backLum: luminance($back) + 0.05;
+ $foreLum: luminance($front) + 0.05;
+
+ @return max($backLum, $foreLum) / min($backLum, $foreLum);
+}
+
+/**
+ * Determine whether to use dark or light text on top of given color.
+ * Returns black for dark text and white for light text.
+ */
+@function choose-contrast-color($color) {
+ $lightContrast: contrast($color, white);
+ $darkContrast: contrast($color, black);
+
+ @if ($lightContrast > $darkContrast) {
+ @return white;
+ }
+
+ @else {
+ @return black;
+ }
+}
diff --git a/ui/app/css/utilities/index.scss b/ui/app/css/utilities/index.scss
new file mode 100644
index 000000000..701193836
--- /dev/null
+++ b/ui/app/css/utilities/index.scss
@@ -0,0 +1 @@
+@forward 'colors';