Skip to content

Commit

Permalink
Update Hamburger design (#1082)
Browse files Browse the repository at this point in the history
  • Loading branch information
connor-baer authored Aug 3, 2021
1 parent 9132468 commit 00ee3ff
Show file tree
Hide file tree
Showing 10 changed files with 451 additions and 81 deletions.
5 changes: 5 additions & 0 deletions .changeset/fluffy-ravens-know.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sumup/circuit-ui': major
---

Tweaked the Hamburger design and aligned its height with the Button component.
5 changes: 5 additions & 0 deletions .changeset/silly-laws-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sumup/circuit-ui': patch
---

Reduced the height of the Button's `kilo` size from 34px to 32px to align it with the other components.
2 changes: 1 addition & 1 deletion packages/circuit-ui/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ const tertiaryStyles = ({
const sizeStyles = ({ theme, size = 'giga' }: ButtonProps & StyleProps) => {
const sizeMap = {
kilo: {
padding: `${theme.spacings.bit} calc(${theme.spacings.mega} - ${BORDER_WIDTH})`,
padding: `calc(${theme.spacings.bit} - ${BORDER_WIDTH}) calc(${theme.spacings.mega} - ${BORDER_WIDTH})`,
},
giga: {
padding: `calc(${theme.spacings.kilo} - ${BORDER_WIDTH}) calc(${theme.spacings.giga} - ${BORDER_WIDTH})`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ exports[`Button styles should render a kilo button 1`] = `
background-color: #FFF;
border-color: #999;
color: #000;
padding: 4px calc(16px - 1px);
padding: calc(4px - 1px) calc(16px - 1px);
}
.circuit-0:focus {
Expand Down
7 changes: 7 additions & 0 deletions packages/circuit-ui/components/Hamburger/Hamburger.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ describe('Hamburger', () => {
expect(actual).toMatchSnapshot();
});

const sizes: HamburgerProps['size'][] = ['kilo', 'giga'];

it.each(sizes)('should render with %s styles', (size) => {
const actual = renderHamburger(create, { ...baseProps, size });
expect(actual).toMatchSnapshot();
});

/**
* Logic tests.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ export default {
};

export const Base = (args: HamburgerProps) => {
const [active, setActive] = useState(false);
const [active, setActive] = useState(args.isActive);
const handleClick = () => {
setActive((prev) => !prev);
};
return <Hamburger isActive={active} onClick={handleClick} {...args} />;
return <Hamburger {...args} isActive={active} onClick={handleClick} />;
};

Base.args = {
Expand Down
131 changes: 82 additions & 49 deletions packages/circuit-ui/components/Hamburger/Hamburger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* limitations under the License.
*/

import { HTMLProps } from 'react';
import { css } from '@emotion/core';
import { Dispatch as TrackingProps } from '@sumup/collector';

Expand Down Expand Up @@ -43,69 +42,103 @@ export interface HamburgerProps
}

const LAYER_HEIGHT = '2px';
const HAMBURGER_WIDTH = '14px';

const buttonStyles = () => css`
const buttonStyles = ({ theme, size }: StyleProps & IconButtonProps) => css`
border: 0;
padding: ${size === 'giga' ? theme.spacings.kilo : theme.spacings.byte};
`;

const Button = styled(IconButton)<IconButtonProps>(buttonStyles);

const boxStyles = ({ theme }: StyleProps) => css`
type BoxProps = Required<Pick<HamburgerProps, 'size'>>;
const boxStyles = css`
position: relative;
display: flex;
justify-content: center;
width: ${theme.iconSizes.mega};
height: ${theme.iconSizes.mega};
transform: translateY(-1px);
`;

const Box = styled.span<HTMLProps<HTMLSpanElement>>(boxStyles);

const layersBaseStyles = () => css`
top: 50%;
width: ${HAMBURGER_WIDTH};
&,
&::after,
&::before {
background-color: currentColor;
border-radius: 1px;
display: block;
height: ${LAYER_HEIGHT};
position: absolute;
transition: width 0.2s ease-out 0.15s, opacity 0.1s ease-in,
transform 0.3s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
const boxSizeStyles = ({ theme, size }: StyleProps & BoxProps) => {
const iconSizeMap = {
giga: 'mega',
kilo: 'kilo',
} as const;
const iconSize = iconSizeMap[size];

&::before,
&::after {
top: 0;
content: '';
}
return css`
width: ${theme.iconSizes[iconSize]};
height: ${theme.iconSizes[iconSize]};
`;
};

&::before {
transform: translateY(-5px);
width: ${HAMBURGER_WIDTH};
}
const Box = styled.span<BoxProps>(boxStyles, boxSizeStyles);

type LayerProps = Required<Pick<HamburgerProps, 'size' | 'isActive'>>;

const widthMap = {
kilo: '14px',
giga: '22px',
} as const;

const offsetMap = {
kilo: '5px',
giga: '7px',
} as const;

&::after {
transform: translateY(5px);
width: ${HAMBURGER_WIDTH};
const layersBaseStyles = ({ size }: LayerProps) => {
const width = widthMap[size];
const offset = offsetMap[size];

return css`
top: 50%;
width: ${width};
&,
&::after,
&::before {
background-color: currentColor;
border-radius: 1px;
display: block;
height: ${LAYER_HEIGHT};
position: absolute;
transition: width 0.2s ease-out 0.15s, opacity 0.1s ease-in,
transform 0.3s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
&::before,
&::after {
top: 0;
content: '';
}
&::before {
transform: translateY(-${offset});
width: calc(${width} * 0.64);
}
&::after {
transform: translateY(${offset});
width: calc(${width} * 0.82);
}
`;
};

const layersActiveStyles = ({ isActive, size }: LayerProps) => {
if (!isActive) {
return null;
}
`;

const layersActiveStyles = ({ isActive }: { isActive?: boolean }) =>
isActive &&
css`
const width = widthMap[size];

return css`
transform: rotate(225deg);
&,
&::before,
&::after {
transition: width 0.2s ease-out, opacity 0.1s ease-out 0.15s,
transition: width 0.2s ease-out 0.15s, opacity 0.1s ease-out 0.15s,
transform 0.3s cubic-bezier(0.215, 0.61, 0.355, 1) 0.15s;
width: ${HAMBURGER_WIDTH};
width: ${width};
}
&::before {
Expand All @@ -117,19 +150,18 @@ const layersActiveStyles = ({ isActive }: { isActive?: boolean }) =>
transform: translateY(0) rotate(-90deg);
}
`;
};

const Layers = styled('span')<{ isActive?: boolean }>(
layersBaseStyles,
layersActiveStyles,
);
const Layers = styled('span')<LayerProps>(layersBaseStyles, layersActiveStyles);

/**
* A hamburger button for menus. Morphs into a close icon when active.
*/
export const Hamburger = ({
isActive,
isActive = false,
activeLabel,
inactiveLabel,
size = 'giga',
tracking = {},
...props
}: HamburgerProps): JSX.Element => {
Expand All @@ -145,13 +177,14 @@ export const Hamburger = ({

return (
<Button
label={isActive ? activeLabel : inactiveLabel}
{...props}
size={size}
label={isActive ? activeLabel : inactiveLabel}
tracking={{ component: 'hamburger', ...tracking }}
type="button"
>
<Box>
<Layers isActive={isActive} />
<Box size={size}>
<Layers isActive={isActive} size={size} />
</Box>
</Button>
);
Expand Down
Loading

0 comments on commit 00ee3ff

Please sign in to comment.