Skip to content

MOAMOA FE Design System

airman5573 edited this page Sep 22, 2022 · 17 revisions

모아모아의 디자인 시스템을 정립합니다.

Layout Components

Flex

flex와 관련된 props를 받습니다.
ex) alignItems, justifyContens 등

Grid

grid와 관련된 props를 받습니다.
ex) columns, columnGap, rows, rowGap 등

Responsive

Flex와 Grid는 mediaQuery속성을 객체로 받습니다.

Wrapper Component

const Wrapper = (children, width, height, padding, margin, border ... 등의 다양한 props) => <div>{children}</div>;

Common Components

공통 컴포넌트는 자주 사용될것들로 지정합니다.
컴포넌트 개발시 props만 잘 주면 다른곳에서도 쓸 수 있을것 같을때 공통 컴포넌트로 승격시킵니다.

Component Structure

예를들어 컴포넌트는 아래와 같이 작성합니다.

import Button from '@components/button';

// Props를 맨 위에 둡니다
export type StudyListProps = {
  children?: React.ReactNode;
  variant?: 'primary' | 'secondary';
  onAddButtonClick: React.MouseEventHandler<HTMLButtonElement>;
  onRemoveButtonClick: React.MouseEventHandler<HTMLButtonElement>;
};

// Component는 화살표 함수로 표현하고, React.FC<Props>로 타입을 명시합니다
// - return type을 더 쉽게 강제할 수 있기 때문입니다
// - PropsWithChildren을 쓰지 않은 이유는, children이 optional로 고정되어 있기 때문입니다
//   반면 React.FC는 개발자가 사용시에 직접 컨트롤 할 수 있어 더 안정적입니다
const StudyList: React.FC<StudyListProps> = ({
  children,
  variant,
  onAddButtonClick: handleAddButtonClick,
  onRemoveButtonClick: handleRemoveButtonClick,
}) => {
  return (
    <Self>
      <List>
        <Item>리팩토링 스터디</Item>
        <Item>프론트 스터디</Item>
        <Item>자바 스터디</Item>
      </List>
      <AddButton onClick={handleAddButtonClick}>추가하기</AddButton>
      <RemoveButton onClick={handleRemoveButtonClick}>삭제하기</RemoveButton>
    </Self>
  );
};

export default StudyList;

// styled component는 component에 같이 둡니다.
// 응집성 + 캡슐화를 위해서 필요합니다.
// 응집성 -> 위에서 바로 사용하는 컴포넌트들을 바로 아래쪽에 둡니다.
// 캡슐화 -> 이 컴포넌트에서만 사용하는 하위컴포넌트(Self, StudyList, AddStudyButton ..)들은 외부에 노출하지 않습니다.
// Self를 다른 컴포넌트에서 상속받아 사용하고 싶다면, (사용하는쪽에서 이곳의) Component를 import해서 상속 받으면 됩니다.
// 가장 바깥 컴포넌트는 Self로 명명합니다.
// 원래라면 S.StudyList라고 하는데 S를 빼고 싶은데 빼면 컴포넌트와 이름이 같아지기 때문에 Self로 명명했습니다.
const Self = styled.div`
  ${({ theme }) => css`
    padding: ${theme.s10}; // 사이즈는 2px단위로 입력하기 때문에 theme을 활용합니다
    max-width: ${theme.s250};
    border: ${theme.s1} solid ${theme.color.black}; // color도 theme에서 가져옵니다
  `}
`;

const List = styled.ul`
  ${({ theme }) => css`
    padding: ${theme.s5};
    margin-bottom: ${theme.s10};
  `}
`;

const Item = styled.li`
  ${({ theme }) => css`
    padding: ${theme.s2};
    border-bottom: ${theme.s1};
  `}
`;

type AddButtonProps = {
  children: React.ReactNode;
  onClick: React.MouseEventHandler<HTMLButtonElement>;
};

type RemoveButtonProps = AddButtonProps;

// 공통 컴포넌트는 이렇게 도메인에 대한 정보를 입혀서 활용합니다.
const AddButton = ({ children, onClick }: AddButtonProps) => (
  <Button width={100} height={50}>
    {children}
  </Button>
);

// Button Props가 못받는 특수한 경우들은 상속을 받아 사용합니다
const RemoveButton = ({ children, onClick }: RemoveButtonProps) => styled.button(Button)`
  transition: opacity ease 0.3s;
  hover: {
    opacity: 0.8;
  }
`;

// Button Props의 기본 props를 넣어주면서 추가적인 CSS를 넣어주는 경우
const OpenButton = ({ children, onClick }: RemoveButtonProps) => () => {
  const Component = () => <Button onClick={onClick} width={100}>{children}</Button>;
  return styled(Component)`
    transition: opacity ease 0.3s;
    hover: {
      opacity: 0.8;
    }
 `;
};

Sizing

2px 단위로 작성합니다.

참고

Clone this wiki locally