Search code examples
javascriptcssreactjssvgstyled-components

How to Make Square Icon Buttons with Rectangular SVG Icons Inside Them


I am attempting to make my own icon button using React, styled-components, and SVG. My icons are generally rectangular, however I want the buttons to be squares and to have the icons centered inside of them. This is where I am getting stuck. I tried tweaking the viewBox dimensions on the SVGs but (1) there is a lot of guesswork in this process, and (2) it screws up the position of the icons inside the buttons. Please can anyone offer some suggestions for how to achieve the square buttons with rectangular icons inside them?

Here is a CodePen with the below code: https://codepen.io/DanielPBank/pen/rPJgPW

const styled = styled.default; //normailly import from npm mmodules

const StyledButton = styled.button`
  appearance: none;
  opacity: 1;
  cursor: pointer;
  display: flex;
  height: ${props => props.size};
  width: ${props => props.size};
  max-height: ${props => props.maxSize}
  max-width: ${props => props.maxSize}
  margin: 1rem;
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 0.88rem;
  box-shadow: 0 0 0 rgba(0, 0, 0, 0);
  border-radius: 8px;
  border: 3px solid ${props => props.active ? '#C23631' : '#222'};
  background-color: ${props => props.active ? '#C09AA1' : '#fcfcfc'};
  color: #222;

  &:hover {
    opacity: 0.70;
    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
  }

  &:disabled {
    opacity: 0.5;
    pointer-events: none;
  }

  &:active {
    border: 3px solid #ccc;
    background-color: #444;
  }
`;

const StyledIcon = styled.svg`
  display: inline-block;
  fill: ${props => props.color || "white"};
  height ${props => props.size};
  width: ${props => props.size};
  user-select: none;
  flex-shrink: 0;
`;

class Icon extends React.Component {
  render() {
    const { size, viewBox, color } = this.props;
    return (
      <StyledIcon
        width={size || '1rem'}
        height={size || '1rem'}
        viewBox={viewBox}
        color={color}
        >
        {this.props.children}
      </StyledIcon>
    );
  }
}

class Undo extends React.Component {
  render() {
    return (
      <Icon
        {...this.props}
        x="0px"
        y="0px"
        viewBox="50 50 950 950"
        fill="#222"
        >
        <path
          d="M588.5,913.8c44.4,0,88.9,0,133.3,0c18,0,36.1,0,54.1,0c11.6,0,23.1-1.9,34.6-3.5     c10.3-1.5,20.3-4.1,29.9-8.1c14.8-6,29.1-12,41.9-21.5c12.4-9.2,24.7-18.5,34.6-30.4c20-24.2,36.5-51.4,42.8-82.6     c3.1-15.2,4.8-30.8,4.8-46.3c0-20.5,0-40.9,0-61.4c0-47.9,0-95.8,0-143.7c0-50.2-19.5-99.2-55.2-134.8     c-35.2-35.2-83.9-55.1-133.6-55.2c-23.7,0-47.3,0-71,0c-50,0-100,0-150,0c-59.9,0-119.8,0-179.7,0c-52.3,0-104.6,0-156.9,0     c-28.3,0-56.7,0-85,0c-1.3,0-2.6,0-3.9,0c-18,0-36.8,7.8-49.5,20.5c-6.8,6.3-11.7,13.8-14.5,22.5c-4.4,8.3-6.4,17.3-6,27     c0.8,18.1,6.8,36.9,20.5,49.5c13.7,12.6,30.5,20.5,49.5,20.5c22,0,43.9,0,65.9,0c52.6,0,105.1,0,157.7,0c62.8,0,125.7,0,188.5,0     c53.8,0,107.6,0,161.4,0c24.5,0,48.9-0.1,73.4,0c5.2,0,10.4,0.3,15.6,1c-6.2-0.8-12.4-1.7-18.6-2.5c10.3,1.4,20.3,4.1,29.9,8.1     c-5.6-2.4-11.1-4.7-16.7-7.1c9.4,4,18.2,9.1,26.3,15.2c-4.7-3.6-9.4-7.3-14.2-10.9c8.4,6.5,15.8,13.9,22.3,22.3     c-3.6-4.7-7.3-9.4-10.9-14.2c6.1,8.1,11.2,16.9,15.2,26.3c-2.4-5.6-4.7-11.1-7.1-16.7c4,9.6,6.7,19.6,8.1,29.9     c-0.8-6.2-1.7-12.4-2.5-18.6c1.3,10.7,1,21.6,1,32.3c0,17.5,0,34.9,0,52.4c0,39.9,0,79.8,0,119.7c0,12.5,0.6,25.1-1,37.5     c0.8-6.2,1.7-12.4,2.5-18.6c-1.4,10.3-4.1,20.3-8.1,29.9c2.4-5.6,4.7-11.1,7.1-16.7c-4,9.4-9.1,18.2-15.2,26.3     c3.6-4.7,7.3-9.4,10.9-14.2c-6.5,8.4-13.9,15.8-22.3,22.3c4.7-3.6,9.4-7.3,14.2-10.9c-8.1,6.1-16.9,11.2-26.3,15.2     c5.6-2.4,11.1-4.7,16.7-7.1c-9.6,4-19.6,6.7-29.9,8.1c6.2-0.8,12.4-1.7,18.6-2.5c-9.6,1.2-19.2,1-28.8,1c-14.5,0-29,0-43.5,0     c-35.4,0-70.8,0-106.1,0c-8.3,0-16.6,0-24.9,0c-17.9,0-36.8,7.8-49.5,20.5c-6.8,6.3-11.7,13.8-14.5,22.5c-4.4,8.3-6.4,17.3-6,27     c0.8,18.1,6.8,36.9,20.5,49.5C552.7,905.8,569.6,913.8,588.5,913.8L588.5,913.8z"
          />
        <path
          d="M296.1,130.8c-24.3,24.3-48.7,48.7-73,73c-38.8,38.8-77.6,77.6-116.4,116.4c-8.9,8.9-17.9,17.9-26.8,26.8     c-26.7,26.7-26.8,72.2,0,99c24.3,24.4,48.7,48.7,73,73.1c38.8,38.9,77.6,77.7,116.4,116.6c8.9,8.9,17.9,17.9,26.8,26.8     c12.7,12.7,31.6,20.5,49.5,20.5c17.2,0,37.7-7.6,49.5-20.5c12.2-13.3,21.3-30.9,20.5-49.5c-0.8-18.6-7.1-36.1-20.5-49.5     c-24.3-24.4-48.7-48.7-73-73.1c-38.8-38.9-77.6-77.7-116.4-116.6c-8.9-8.9-17.9-17.9-26.8-26.8c0,33,0,66,0,99     c24.3-24.3,48.7-48.7,73-73c38.8-38.8,77.6-77.6,116.4-116.4c8.9-8.9,17.9-17.9,26.8-26.8c12.7-12.7,20.5-31.5,20.5-49.5     c0-17.2-7.6-37.7-20.5-49.5c-13.3-12.2-30.9-21.3-49.5-20.5C327,111.1,309.5,117.4,296.1,130.8L296.1,130.8z"
          />
      </Icon>
    );
  }
}

class Button extends React.Component {
  render() {
    const { icon, size, maxSize, active, disabled } = this.props;
    return (
      <StyledButton
        type="button"
        ref={this.buttonRef}
        size={size}
        maxSize={maxSize}
        active={active}
        disabled={disabled}
        >
        {icon}
      </StyledButton>
    );
  }
}

class Application extends React.Component {
  render() {
    return (
      <div>
        <Button
          icon={<Undo color="black" size="100%"/>}
          size="25%"
          maxSize="5rem"
          />
      </div>
    )
  }
}

ReactDOM.render(<Application />, document.getElementById('content'));

Solution

  • Your icon is square alright. The button is rectangle because of button's default padding. In StyledButton, add padding: 0; and your button should be squared.