diff --git a/ui/app/components/ui/box/box.js b/ui/app/components/ui/box/box.js
new file mode 100644
index 000000000..d8a630bad
--- /dev/null
+++ b/ui/app/components/ui/box/box.js
@@ -0,0 +1,149 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import classnames from 'classnames'
+import {
+ ALIGN_ITEMS,
+ BLOCK_SIZES,
+ BORDER_STYLE,
+ COLORS,
+ DISPLAY,
+ JUSTIFY_CONTENT,
+ SIZES,
+} from '../../../helpers/constants/design-system'
+
+const ValidSize = PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
+const ArrayOfValidSizes = PropTypes.arrayOf(ValidSize)
+const MultipleSizes = PropTypes.oneOf([ValidSize, ArrayOfValidSizes])
+
+function generateSizeClasses(baseClass, type, main, top, right, bottom, left) {
+ const arr = Array.isArray(main) ? main : []
+ const singleDigit = Array.isArray(main) ? undefined : main
+ if (Array.isArray(main) && ![2, 3, 4].includes(main.length)) {
+ throw new Error(
+ `Expected prop ${type} to have length between 2 and 4, received ${main.length}`,
+ )
+ }
+
+ const isHorizontalAndVertical = arr.length === 2
+ const isTopHorizontalAndBottom = arr.length === 3
+ const isAllFour = arr.length === 4
+ const hasAtLeastTwo = arr.length >= 2
+ const hasAtLeastThree = arr.length >= 3
+ return {
+ [`${baseClass}--${type}-${singleDigit}`]: singleDigit !== undefined,
+ [`${baseClass}--${type}-top-${top}`]: typeof top === 'number',
+ [`${baseClass}--${type}-right-${right}`]: typeof right === 'number',
+ [`${baseClass}--${type}-bottom-${bottom}`]: typeof bottom === 'number',
+ [`${baseClass}--${type}-left-${left}`]: typeof left === 'number',
+ // As long as an array of length >= 2 has been provided, the first number
+ // will always be for the top value.
+ [`${baseClass}--${type}-top-${arr?.[0]}`]: hasAtLeastTwo,
+ // As long as an array of length >= 2 has been provided, the second number
+ // will always be for the right value.
+ [`${baseClass}--${type}-right-${arr?.[1]}`]: hasAtLeastTwo,
+ // If an array has 2 values, the first number is the bottom value. If
+ // instead if has 3 or more values, the third number will be the bottom.
+ [`${baseClass}--${type}-bottom-${arr?.[2]}`]: hasAtLeastThree,
+ [`${baseClass}--${type}-bottom-${arr?.[0]}`]: isHorizontalAndVertical,
+ // If an array has 2 or 3 values, the second number will be the left value
+ [`${baseClass}--${type}-left-${arr?.[1]}`]:
+ isHorizontalAndVertical || isTopHorizontalAndBottom,
+ // If an array has 4 values, the fourth number is the left value
+ [`${baseClass}--${type}-left-${arr?.[3]}`]: isAllFour,
+ }
+}
+
+export default function Box({
+ padding,
+ paddingTop,
+ paddingRight,
+ paddingBottom,
+ paddingLeft,
+ margin,
+ marginTop,
+ marginRight,
+ marginBottom,
+ marginLeft,
+ borderColor,
+ borderWidth,
+ borderRadius,
+ borderStyle,
+ alignItems,
+ justifyContent,
+ display,
+ width,
+ height,
+ children,
+}) {
+ const boxClassName = classnames('box', {
+ // ---Borders---
+ // if borderWidth or borderColor is supplied w/o style, default to solid
+ 'box--border-style-solid':
+ !borderStyle && (Boolean(borderWidth) || Boolean(borderColor)),
+ // if borderColor supplied w/o width, default to 1
+ 'box--border-size-1': !borderWidth && Boolean(borderColor),
+ [`box--border-color-${borderColor}`]: Boolean(borderColor),
+ [`box--rounded-${borderRadius}`]: Boolean(borderRadius),
+ [`box--border-style-${borderStyle}`]: Boolean(borderStyle),
+ [`box--border-size-${borderWidth}`]: Boolean(borderWidth),
+ // Margin
+ ...generateSizeClasses(
+ 'box',
+ 'margin',
+ margin,
+ marginTop,
+ marginRight,
+ marginBottom,
+ marginLeft,
+ ),
+ // Padding
+ ...generateSizeClasses(
+ 'box',
+ 'padding',
+ padding,
+ paddingTop,
+ paddingRight,
+ paddingBottom,
+ paddingLeft,
+ ),
+ // ---Flex/Grid alignment---
+ // if justifyContent or alignItems supplied w/o display, default to flex
+ 'box--display-flex':
+ !display && (Boolean(justifyContent) || Boolean(alignItems)),
+ [`box--justify-content-${justifyContent}`]: Boolean(justifyContent),
+ [`box--align-items-${alignItems}`]: Boolean(alignItems),
+ // display
+ [`box--display-${display}`]: Boolean(display),
+ // width & height
+ [`box--width-${width}`]: Boolean(width),
+ [`box--height-${height}`]: Boolean(height),
+ })
+ // Apply Box styles to any other component using function pattern
+ if (typeof children === 'function') {
+ return children(boxClassName)
+ }
+ return
{label}
@@ -54,7 +54,9 @@ Chip.propTypes = {
borderColor: PropTypes.oneOf(Object.values(COLORS)),
label: PropTypes.string,
children: PropTypes.node,
- labelProps: PropTypes.shape(omit(Typography.propTypes, ['className'])),
+ labelProps: PropTypes.shape({
+ ...omit(Typography.propTypes, ['className']),
+ }),
leftIcon: PropTypes.node,
rightIcon: PropTypes.node,
className: PropTypes.string,
diff --git a/ui/app/components/ui/typography/typography.js b/ui/app/components/ui/typography/typography.js
index 93990b00b..f3ea8fac7 100644
--- a/ui/app/components/ui/typography/typography.js
+++ b/ui/app/components/ui/typography/typography.js
@@ -1,7 +1,13 @@
import React from 'react'
import classnames from 'classnames'
import PropTypes from 'prop-types'
-import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system'
+import {
+ COLORS,
+ FONT_WEIGHT,
+ TEXT_ALIGN,
+ TYPOGRAPHY,
+} from '../../../helpers/constants/design-system'
+import Box from '../box'
const { H6, H7, H8, H9 } = TYPOGRAPHY
@@ -11,16 +17,15 @@ export default function Typography({
color = COLORS.BLACK,
tag,
children,
- spacing = 1,
fontWeight = 'normal',
align,
+ boxProps = {},
}) {
const computedClassName = classnames(
'typography',
className,
`typography--${variant}`,
`typography--align-${align}`,
- `typography--spacing-${spacing}`,
`typography--color-${color}`,
`typography--weight-${fontWeight}`,
)
@@ -33,7 +38,15 @@ export default function Typography({
Tag = H6
}
- return {children}
+ return (
+
+ {(boxClassName) => (
+
+ {children}
+
+ )}
+
+ )
}
Typography.propTypes = {
@@ -41,9 +54,11 @@ Typography.propTypes = {
children: PropTypes.node.isRequired,
color: PropTypes.oneOf(Object.values(COLORS)),
className: PropTypes.string,
- align: PropTypes.oneOf(['center', 'right']),
- spacing: PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8]),
- fontWeight: PropTypes.oneOf(['bold', 'normal']),
+ align: PropTypes.oneOf(Object.values(TEXT_ALIGN)),
+ boxProps: PropTypes.shape({
+ ...Box.propTypes,
+ }),
+ fontWeight: PropTypes.oneOf(Object.values(FONT_WEIGHT)),
tag: PropTypes.oneOf([
'p',
'h1',
diff --git a/ui/app/components/ui/typography/typography.scss b/ui/app/components/ui/typography/typography.scss
index e377f31d2..50a2ec622 100644
--- a/ui/app/components/ui/typography/typography.scss
+++ b/ui/app/components/ui/typography/typography.scss
@@ -4,6 +4,10 @@
.typography {
@include design-system.Paragraph;
+ & b {
+ font-weight: 700;
+ }
+
@each $variant in map.keys(design-system.$typography-variants) {
&--#{$variant} {
@include design-system.typography($variant);
@@ -16,18 +20,16 @@
}
}
- @each $variant, $weight in design-system.$typography-font-weights {
- &--weight-#{$variant} {
+ @each $weight in design-system.$font-weight {
+ &--weight-#{$weight} {
font-weight: $weight;
}
}
- &--align-center {
- text-align: center;
- }
-
- &--align-right {
- text-align: right;
+ @each $alignment in design-system.$text-align {
+ &--align-#{$alignment} {
+ text-align: $alignment;
+ }
}
@for $i from 1 through 8 {
diff --git a/ui/app/components/ui/typography/typography.stories.js b/ui/app/components/ui/typography/typography.stories.js
index 68d567e35..0d0d486b8 100644
--- a/ui/app/components/ui/typography/typography.stories.js
+++ b/ui/app/components/ui/typography/typography.stories.js
@@ -1,23 +1,17 @@
import React from 'react'
import { number, select, text } from '@storybook/addon-knobs'
-import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system'
+import {
+ COLORS,
+ FONT_WEIGHT,
+ TEXT_ALIGN,
+ TYPOGRAPHY,
+} from '../../../helpers/constants/design-system'
import Typography from '.'
export default {
title: 'Typography',
}
-const fontWeightOptions = {
- bold: 'bold',
- normal: 'normal',
-}
-
-const alignOptions = {
- left: undefined,
- center: 'center',
- right: 'right',
-}
-
export const list = () => (
{Object.values(TYPOGRAPHY).map((variant) => (
@@ -26,8 +20,12 @@ export const list = () => (
variant={variant}
color={select('color', COLORS, COLORS.BLACK)}
spacing={number('spacing', 1, { range: true, min: 1, max: 8 })}
- align={select('align', alignOptions, undefined)}
- fontWeight={select('font weight', fontWeightOptions, 'normal')}
+ align={select('align', TEXT_ALIGN, undefined)}
+ fontWeight={select(
+ 'font weight',
+ Object.values(FONT_WEIGHT),
+ FONT_WEIGHT.NORMAL,
+ )}
>
{variant}
@@ -43,8 +41,8 @@ export const TheQuickOrangeFox = () => (
color={select('color', COLORS, COLORS.BLACK)}
variant={select('variant', TYPOGRAPHY, TYPOGRAPHY.Paragraph)}
spacing={number('spacing', 1, { range: true, min: 1, max: 8 })}
- align={select('align', alignOptions, undefined)}
- fontWeight={select('font weight', fontWeightOptions, 'normal')}
+ align={select('align', TEXT_ALIGN, undefined)}
+ fontWeight={select('font weight', FONT_WEIGHT, FONT_WEIGHT.NORMAL)}
>
{text('content', 'The quick orange fox jumped over the lazy dog.')}
diff --git a/ui/app/components/ui/ui-components.scss b/ui/app/components/ui/ui-components.scss
index 2b76068d2..09382d2a3 100644
--- a/ui/app/components/ui/ui-components.scss
+++ b/ui/app/components/ui/ui-components.scss
@@ -2,6 +2,7 @@
@import 'account-mismatch-warning/index';
@import 'alert-circle-icon/index';
@import 'alert/index';
+@import 'box/box';
@import 'breadcrumbs/index';
@import 'button-group/index';
@import 'button/buttons';
diff --git a/ui/app/css/design-system/attributes.scss b/ui/app/css/design-system/attributes.scss
new file mode 100644
index 000000000..2176cddb9
--- /dev/null
+++ b/ui/app/css/design-system/attributes.scss
@@ -0,0 +1,72 @@
+$align-items:
+ baseline,
+ center,
+ flex-end,
+ flex-start,
+ stretch;
+
+$justify-content:
+ center,
+ flex-end,
+ flex-start,
+ space-around,
+ space-between,
+ space-evenly;
+
+$fractions: (
+ 1\/2: 50%,
+ 1\/3: 33.333333%,
+ 2\/3: 66.666667%,
+ 1\/4: 25%,
+ 2\/4: 50%,
+ 3\/4: 75%,
+ 1\/5: 20%,
+ 2\/5: 40%,
+ 3\/5: 60%,
+ 4\/5: 80%,
+ 1\/6: 16.666667%,
+ 2\/6: 33.333333%,
+ 3\/6: 50%,
+ 4\/6: 66.666667%,
+ 5\/6: 83.333333%,
+ 1\/12: 8.333333%,
+ 2\/12: 16.666667%,
+ 3\/12: 25%,
+ 4\/12: 33.333333%,
+ 5\/12: 41.666667%,
+ 6\/12: 50%,
+ 7\/12: 58.333333%,
+ 8\/12: 66.666667%,
+ 9\/12: 75%,
+ 10\/12: 83.333333%,
+ 11\/12: 91.666667%,
+);
+
+$sizes-numeric:
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12;
+
+$sizes-strings:
+ xs,
+ sm,
+ md,
+ lg,
+ xl,
+ none;
+
+$border-style: solid, double, none, dashed, dotted;
+$directions: top, right, bottom, left;
+$display: block, grid, flex, inline-block, inline-grid, inline-flex, list-item;
+$text-align: left, right, center, justify;
+$font-weight: bold, normal, 100, 200, 300, 400, 500, 600, 700, 800, 900;
diff --git a/ui/app/css/design-system/index.scss b/ui/app/css/design-system/index.scss
index d4f8980b9..3d5e5e410 100644
--- a/ui/app/css/design-system/index.scss
+++ b/ui/app/css/design-system/index.scss
@@ -1,3 +1,4 @@
+@forward 'attributes';
@forward 'breakpoints';
@forward 'colors';
@forward 'deprecated-colors';
diff --git a/ui/app/css/design-system/typography.scss b/ui/app/css/design-system/typography.scss
index 73ccab414..707170c24 100644
--- a/ui/app/css/design-system/typography.scss
+++ b/ui/app/css/design-system/typography.scss
@@ -82,11 +82,6 @@ $typography-variants: (
'h9': 0.5rem,
);
-$typography-font-weights: (
- 'bold': 700,
- 'normal': 400,
-);
-
$font-size-h1: map-get($typography-variants, 'h1');
$font-size-h2: map-get($typography-variants, 'h2');
$font-size-h3: map-get($typography-variants, 'h3');
diff --git a/ui/app/css/utilities/_spacing.scss b/ui/app/css/utilities/_spacing.scss
new file mode 100644
index 000000000..ab87c3380
--- /dev/null
+++ b/ui/app/css/utilities/_spacing.scss
@@ -0,0 +1,7 @@
+$theme-spacing-value: 4px;
+
+@function get-spacing($spacing) {
+ $spacingInPx: $spacing * 4px;
+
+ @return $spacingInPx;
+}
diff --git a/ui/app/css/utilities/index.scss b/ui/app/css/utilities/index.scss
index 701193836..abbfed74a 100644
--- a/ui/app/css/utilities/index.scss
+++ b/ui/app/css/utilities/index.scss
@@ -1 +1,2 @@
@forward 'colors';
+@forward 'spacing';
diff --git a/ui/app/helpers/constants/design-system.js b/ui/app/helpers/constants/design-system.js
index 288a1f0be..3663cd879 100644
--- a/ui/app/helpers/constants/design-system.js
+++ b/ui/app/helpers/constants/design-system.js
@@ -1,3 +1,9 @@
+/**
+ * A note about the existence of both singular and plural variable names here:
+ * When dealing with a literal property name, e.g. ALIGN_ITEMS, the constant
+ * should match the property. When detailing a collection of things, it should
+ * match the plural form of the thing. e.g. COLORS, TYPOGRAPHY
+ */
export const COLORS = {
UI1: 'ui-1',
UI2: 'ui-2',
@@ -40,3 +46,111 @@ export const TYPOGRAPHY = {
H9: 'h9',
Paragraph: 'paragraph',
}
+
+const NONE = 'none'
+
+export const SIZES = {
+ XS: 'xs',
+ SM: 'sm',
+ MD: 'md',
+ LG: 'lg',
+ XL: 'xl',
+ NONE,
+}
+
+export const BORDER_STYLE = {
+ DASHED: 'dashed',
+ SOLID: 'solid',
+ DOTTED: 'dotted',
+ DOUBLE: 'double',
+ NONE,
+}
+
+const FLEX_END = 'flex-end'
+const FLEX_START = 'flex-start'
+const CENTER = 'center'
+
+export const ALIGN_ITEMS = {
+ FLEX_START,
+ FLEX_END,
+ CENTER,
+ BASELINE: 'baseline',
+ STRETCH: 'stretch',
+}
+
+export const JUSTIFY_CONTENT = {
+ FLEX_START,
+ FLEX_END,
+ CENTER,
+ SPACE_AROUND: 'space-around',
+ SPACE_BETWEEN: 'space-between',
+ SPACE_EVENLY: 'space-evenly',
+}
+
+export const DISPLAY = {
+ BLOCK: 'block',
+ FLEX: 'flex',
+ GRID: 'grid',
+ INLINE_BLOCK: 'inline-block',
+ INLINE_FLEX: 'inline-flex',
+ INLINE_GRID: 'inline-grid',
+ LIST_ITEM: 'list-item',
+}
+
+const FRACTIONS = {
+ HALF: '1/2',
+ ONE_THIRD: '1/3',
+ TWO_THIRDS: '2/3',
+ ONE_FOURTH: '1/4',
+ TWO_FOURTHS: '2/4',
+ THREE_FOURTHS: '3/4',
+ ONE_FIFTH: '1/5',
+ TWO_FIFTHS: '2/5',
+ THREE_FIFTHS: '3/5',
+ FOUR_FIFTHS: '4/5',
+ ONE_SIXTH: '1/6',
+ TWO_SIXTHS: '2/6',
+ THREE_SIXTHS: '3/6',
+ FOUR_SIXTHS: '4/6',
+ FIVE_SIXTHS: '5/6',
+ ONE_TWELFTH: '1/12',
+ TWO_TWELFTHS: '2/12',
+ THREE_TWELFTHS: '3/12',
+ FOUR_TWELFTHS: '4/12',
+ FIVE_TWELFTHS: '5/12',
+ SIX_TWELFTHS: '6/12',
+ SEVEN_TWELFTHS: '7/12',
+ EIGHT_TWELFTHS: '8/12',
+ NINE_TWELFTHS: '9/12',
+ TEN_TWELFTHS: '10/12',
+ ELEVEN_TWELFTHS: '11/12',
+}
+
+export const BLOCK_SIZES = {
+ ...FRACTIONS,
+ SCREEN: 'screen',
+ MAX: 'max',
+ MIN: 'min',
+ FULL: 'full',
+}
+
+export const TEXT_ALIGN = {
+ LEFT: 'left',
+ CENTER: 'center',
+ RIGHT: 'right',
+ JUSTIFY: 'justify',
+}
+
+export const FONT_WEIGHT = {
+ BOLD: 'bold',
+ NORMAL: 'normal',
+ 100: 100,
+ 200: 200,
+ 300: 300,
+ 400: 400,
+ 500: 500,
+ 600: 600,
+ 700: 700,
+ 800: 800,
+ 900: 900,
+}