오늘은 나만의 icon package를 제작하였습니다

fantasticon을 이용해서 icon svg 파일들을 webfont로 제작하여 publish 하였습니다

 

Directory

 

components: Icon컴포넌트가 위치한곳

constants: Icon의 name이 있는곳

icons: icon svg파일들이 있는곳

templates: webfont 추출시 필요한 template

Webfont generate 

피그마에서 다음과같이 svg 아이콘을 만든뒤

icons 폴더에 넣어주고

// fantasticonrc.json

{
  "inputDir": "./src/icons",
  "outputDir": "./dist",
  "normalize": true,
  "fontTypes": ["svg", "woff"],
  "assetTypes": ["css", "ts", "json"],
  "templates": {
    "css": "src/templates/css.hbs"
  }
}


// templates/css.hbs

@font-face {
    font-family: "{{ name }}";
    src: {{{ fontSrc }}};
    font-display: block;
}

i[class*=" {{ prefix }}-"]::before,
i[class^="{{ prefix }}-"]::before {
    font-family: {{ name }} !important;
    font-style: normal;
    font-weight: 400 !important;
    font-variant: normal;
    text-transform: none;
    line-height: 1;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

{{# each codepoints}}
    .{{ ../prefix }}-{{ @key }}::before {
        content: "\\{{ codepoint this }}";
    }
{{/ each}}

fantasticon 설정파일과 hbs 템플릿 파일을 생성한뒤 npm fantasticon을 실행하면 woff 파일이 정상적으로 만들어집니다

Icon 컴포넌트 & Bundle

components 에는 리액트 코드에서 사용될 Icon 컴포넌트를 만든뒤 tsup 으로 번들링해주었습니다

import { CSSProperties } from 'react';
import icons from '../constants';

export interface IconProps {
  name: (typeof icons)[number];
  size?: number;
  irName?: string;
  color?: CSSProperties['color'];
}

const Icon = ({ color = '#000', irName, name, size = 24 }: IconProps) => {
  return (
    <i
      className={`icon icon-${name}`}
      style={{
        fontSize: size,
        color,
      }}
    >
      {irName && <span>{irName}</span>}
    </i>
  );
};

export default Icon;

 

아이콘의 이름을 props로 받기위해 constants.ts 파일을 생성해주는 script도 작성하였습니다

#!/bin/bash

icon_path='src/icons'
constant_file='src/constants/index.ts'

echo 'const icons = [' >"$constant_file"

function convert_name {
  file_name=$(basename "$1")
  file_name_without_extension="${file_name%.*}"
  dash_replaced="${file_name_without_extension// /-}"

  echo "$dash_replaced"
}

for file in "$icon_path"/*; do
  if [ -f "$file" ]; then
    converted_name=$(convert_name "$file")
    echo "  '${converted_name}'", >>"$constant_file"
  fi
done

{
  echo '] as const;'
  echo '' >>"$constant_file"
  echo 'export default icons' >>"$constant_file"
} >>"$constant_file"

npx eslint --fix "$constant_file"

 

그다음 npm tsup 을 하면 dist에 Icon컴포넌트가 정상적으로 번들링되는 모습을 확인할수있습니다

// tsup.config.js
import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['src/components/index.tsx'],
  format: ['esm', 'cjs'],
  dts: true,
  splitting: false,
  sourcemap: true,
  clean: true,
  minify: true,
});

 

그 다음 npm publish를 통해 npm 에 배포하면 끝

사용

import Icon from "@breadlee/icons";

function App() {
  return (
    <>
      아이콘 입니다
      <Icon name="arrow_down" color="red" size={24} />
    </>
  );
}

export default App;

 

 

icons.woff 파일과 icons.css파일 배포전략을 생각해봐야 할것같습니다

지금은 임시로 https://www.jsdelivr.com/?docs=gh 무료 cdn을 활용하여

<link href="https://cdn.jsdelivr.net/npm/@breadlee/icons/dist/icons.css" rel="stylesheet" />
<link
  as="font"
  crossOrigin="anonymous"
  href="https://cdn.jsdelivr.net/npm/@breadlee/icons/dist/icons.woff"
  rel="preload"
  type="font/woff"
/>

다음과 같이 불러와서 테스트하고 있습니다

 

자세한 소스는 여기서 확인가능합니다

https://github.com/leeyc924/leeyc-package/tree/main/packages/icons

'SideProject > Design System' 카테고리의 다른 글

6. utils  (2) 2024.02.18
5. ui  (5) 2024.02.13
3. npm publish  (2) 2024.02.02
2. eslint, tsconifg 설정  (3) 2024.02.01
1. Monorepo 구성  (2) 2024.02.01

+ Recent posts