본 게시물에 언급된 SCSS는 GITHUB 저장소 및 NPM을 통해 설치 가능합니다.
https://github.com/ntswitblog/icon-scss-mixins-witblog
왜 Pure CSS ICON을 사용하는가?
일반적으로 웹에서 ICON 이미지를 적용할 때, sprite 이미지를 주로 사용한다. 하지만 라인, 원형, 삼각형, 사각형 같은 단순한 도형의 아이콘은 png나 svg 이미지 대신 Pure CSS로 그리는 경우가 종종 있으며, CSS로 ICON을 표현하면 이미지의 요청과 전송량을 줄여 UI 성능을 개선할 수 있다. 하위 브라우저를 대응하는 서비스에서는 Pure CSS ICON을 적용하기 어렵겠지만, 모바일과 IE9 이상의 PC 브라우저 또는 모바일 브라우저만 대응하는 서비스라면 Pure CSS ICON을 사용하는 것도 좋은 방법이다.
※ 하지만 디자이너가 직접 만든 이미지와 완전히 동일하기는 어렵고, IE8과 같은 하위 브라우저 대응 시에는 이슈가 발생하여 유사 모양의 문자로 대체가 필요할 수도 있다. 그 때문에 Pure CSS ICON이 많이 사용된다면, 사전에 디자이너에게 어떤 아이콘이 CSS로 그려지는지, Pure CSS ICON에 어떤 장단점이 있는지 등에 대한 정보 공유와 협의가 필요하다.
CSS ICON을 위한 SCSS Mixin 제작한 이유
대부분 서비스에서 X, <, >, 메뉴, 삼각형 등과 같이 단순한 아이콘들을 자주 사용하지만, 하위 브라우저 이슈로 인해 sprite 이미지를 주로 사용한다. CSS로 그리는 경우에는 매번 포토샵이나 pixel perfect와 같은 도구로 디자이너에게 전달받은 이미지와 CSS로 그린 아이콘을 직접 대보며 CSS 코드를 복사/붙여넣기하여 다시 그리고 있다. 이런 경우, CSS 코드의 일부를 제공하는 zeplin과 같은 디자인 가이드의 장점을 충분히 활용하지 못하게 된다. 이처럼 반복되고 불필요한 작업을 최소화하고자 반복적으로 사용되는 아이콘을 넓이, 높이, 선의 두께, 색상 등만 입력하면 쉽게 Pure CSS ICON이 그려질 수 있도록 CSS ICON을 SCSS mixin으로 제작하였다.
CSS ICON Mixin 장단점
장점
- 이미지 요청량 감소로 인한 로딩 속도 개선
- 유지보수의 편리함
- 아이콘 변경 시, 이미지 추가/변경없이 크기, 형태, 색상 등 CSS으로 비교적 자유롭게 변경 가능
- 비교적 쉽게 애니메이션 기능 적용 가능
단점
- 하위 브라우저 대응 문제
- mixin 변수의 복잡성
- SCSS에 대한 이해와 작업자의 숙련도 필요
- 일부 CSS 속성의 성능 이슈
- 몇몇 아이콘은 그림자 추가할 때,
box-shadow
속성 값만 추가하면 간단하게 해결할 수 있다는 장점이 있지만,box-shadow
와 같이 CSS 속성은 모바일 기기에서 성능 저하의 원인이 됨 – (관련된 stackoverflow ISSUE 보기)
- 몇몇 아이콘은 그림자 추가할 때,
CSS ICON Mixin 제작 기준
CSS ICON을 위해 요소의 개수가 늘어나거나 CSS 코드가 길어지는 것은 성능을 오히려 저하시킬 수 있기 때문에 아이콘의 요소와 가상 요소(:before, :after)만을 사용하여 단순하게 그릴 수 있는 도형을 선택했다.
2. 일반적으로 자주 사용되는 아이콘인가
Mixin은 CSS에서 반복적으로 사용하는 스타일을 재사용하기 위해 정의하기 때문에 대부분의 서비스에서 일반적으로 자주 사용하는 아이콘을 제작했다.
3. 하위 브라우저 대응이 필요한 경우, 문자(<, >, X)로 대체 가능한가
하위 브라우저에서 적용되지 않는 속성(예: transform)이 필요한 아이콘의 경우, 하위 브라우저 대응에 대한 대비책이 필요하기 때문에 의미상으로 대체 가능한 문자가 있는 것이 좋다.
CSS ICON Mixin 사용하기
사용법
1. 아래 1~6번 아이콘 중에서 필요한 CSS ICON의 SCSS @mixin을 공통으로 쓰이는 scss 파일에 가져온다.
$sqrt_2: 1.41421356237; @mixin iconCancel($size: 24px, $thick: 2px, $color: #000, $padding: 0, $display: inline-block, $position: relative){ ...Mixin 내용 생략... }
2. 아이콘의 HTML 요소를 생성한다.
<!-- 부모 없이 i태그만 쓰는 경우 --> <i class="icon_cancel" role="img" aria-label="닫기"></i> <!-- `role`을 가진 부모 태그가 있는 경우 --> <button type="button" aria-label="닫기"><i class="icon_cancel"></i></button>
3. 해당 요소의 CSS 선택자 내에 SCSS Mixin을 @include 하여 해당 아이콘에 원하는 사이즈, 색상 등의 파라미터 값을 지정한다.
.icon_cancel { @include iconCancel($size: 24px, $thick: 2px, $color: #000, $padding: 0, $display: inline-block, $position: relative); }
1. X (취소/삭제 아이콘)
X 아이콘은 취소, 삭제, 닫기 등의 용도로 자주 쓰이는 아이콘이다.
[Codepen 예시]
[SCSS Parameter]
Name | Description | Type | Default value |
---|---|---|---|
$size |
아이콘 넓이/높이 값 (짝수값 입력 권장) | Number |
24px |
$thick |
아이콘 내 막대의 두께 값 | Number |
2px |
$color |
아이콘 막대 색상 값 | Color |
#000 |
$padding |
아이콘 여백 값 | Number |
20px |
$display |
아이콘 display 속성 값 | inline-block/block |
inline-block |
$position |
아이콘 position 속성 값 | relative/absolute |
relative |
[SCSS @mixin]
$sqrt_2: 1.41421356237; @mixin iconCancel($size: 21px, $thick: 2px, $color: #000, $padding: 0, $display: inline-block, $position: relative) { position: $position; display: $display; width: $size + $padding*2; height: $size + $padding*2; &:before, &:after { content: ''; position: absolute; top: 50%; left: 50%; width: round($size*$sqrt_2 - $thick); height: $thick; background-color: $color; } &:before { -webkit-transform: translate(-50%, -50%) rotate(45deg); transform: translate(-50%, -50%) rotate(45deg); } &:after { -webkit-transform: translate(-50%, -50%) rotate(135deg); transform: translate(-50%, -50%) rotate(135deg); } }
[그림으로 이해하는 @mixin]
모든 아이콘은 공통적으로 아래의 4가지 설명을 참고하길 바란다.
- 앞으로 1~6번 아이콘 모두 ‘그림으로 이해하는 @mixin’에서 그림은 주로
@include
로 읽어오는$파라미터
값을 활용하여 구한 속성 값을 위주로 보여준다. - 그림에서
&
은 아이콘 요소를 가리키며, 노란색 영역 또는 노란색 점선 내에 회색 영역은 아이콘 요소의 영역이고,&:before
와&:after
은 각각 핑크색과 초록색으로 각각의 가상 요소를 나타낸다. - 1~4번 아이콘 요소(
&
)는 아이콘의 위치와 사이즈를 잡아주는 역할을 하며, 가상 요소(&:before
또는&:before
,&:after
)로 아이콘 모양이 그려진다. - 5~6번 아이콘은 아이콘 요소(
&
), 가상 요소(&:before, &:after
)로 아이콘 모양이 그려지며, 하늘색 영역은 아이콘 요소(&
)의margin
영역을 보여준다. 반드시 부모 요소가 자식인 아이콘 요소를 감싸주어야 부모 요소에 아이콘의 영역이 잡히며, 부모 요소로 아이콘의 위치를 잡아주어야 한다.
- X 아이콘은
:before
와:after
가상 요소를 이용하여, 두 개의 막대를 그려준다. 막대의height
값은 위 그림과 같이 아이콘의 파라미터 값으로 설정한$thick
값이며, 막대의width
값은 위 그림과 같이$size
와$thick
으로 구할 수 있다.
그 후, 가상 요소에position: absolute; top: 50%; left: 50%;
과transform: translate(-50%, -50%);
속성 값을 적용하면 막대는 아이콘 영역의 중앙에 배치되며,transform
의rotate
을 이용하여 막대를 회전시킨다.
[특징 및 주의사항]
- 아이콘 요소에
border-radius
와background-color
속성을 이용하여 둥근 배경을 적용할 수 있다. 배경 내 여백은 파라미터 값인$padding
으로 설정할 수 있다. - 그림자 추가 시,
transform
속성으로 인해 그림자 위치가 예상과 다르기 때문에box-shadow
속성은 적용하지 않는 것이 좋다.
$size
값이 홀수인 경우, 최소 단위가 1px인 브라우저에서는 아이콘을 정확히 중앙 정렬하지 못하기 때문에 아이콘이 영역 밖으로 아주 조금 튀어나올 수 있다. 그러므로$size
값은 짝수로 입력해주는 것이 더 좋다.
[브라우저 호환성]
- 지원 범위 : transform속성 적용 가능 범위 (IE9이상)
2. Triangle (삼각형 아이콘)
삼각형 아이콘은 펼침/접힘 상태를 나타내는 화살표, 더보기용 화살표 등의 용도로 자주 쓰인다.
[Codepen 예시]
[SCSS Parameter]
Name | Description | Type | Default value |
---|---|---|---|
$direction |
화살표 방향 (‘up’, ‘down’, ‘left’, ‘right’) | String |
'up' |
$width |
아이콘 넓이 값 (‘up’ or ‘down’인 경우, 짝수값 입력 권장) | number |
20px |
$height |
아이콘 높이 값 (‘left’ or ‘right’인 경우, 짝수값 입력 권장) | number |
10px |
$color |
아이콘 색상 값 | Color |
#000 |
$display |
아이콘 display 속성 값 | inline-block/block |
inline-block |
[SCSS @mixin]
@mixin iconTriangle($direction: 'up', $width: 20px, $height: 10px, $color: #000, $display: inline-block) { display: $display; width: $width; height: $height; &:before { content: ''; display: block; @if ($direction == 'up') { border-bottom: $height solid $color; border-left: round($width/2) solid transparent; border-right: round($width/2) solid transparent; } @else if ($direction == 'down') { border-top: $height solid $color; border-left: round($width/2) solid transparent; border-right: round($width/2) solid transparent; } @else if ($direction == 'left') { border-top: round($height/2) solid transparent; border-bottom: round($height/2) solid transparent; border-right: $width solid $color; } @else if ($direction == 'right') { border-top: round($height/2) solid transparent; border-bottom: round($height/2) solid transparent; border-left: $width solid $color; } } }
[그림으로 이해하는 @mixin]
- CSS로 삼각형 아이콘을 그릴 때, 위와 같이 보통 방향별
border
값을 이용하여 그린다. 아이콘 요소로도 그릴 수 있지만, 아이콘 요소에서는 위치와 영역을 잡아주기 위해 여기서 삼각형 아이콘은:before
가상 요소에 그려지도록 제작하였다. - 위 방향 화살표로 예를 들자면, 첫 번째 그림과 같이 위를 가리킬 수 있도록
border-bottom
에 먼저 아이콘의 색상($color
)과 높이 값($height
)을 주며, 양옆에border-left
와border-right
에는 아이콘 넓이($width
)의 1/2값을border-width
로 주고,transparent
를border-color
로 준다.
[특징 및 주의사항]
- 그림자 추가 시(
box-shadow
속성 적용), 삼각형이 아닌 사각형 박스 형태의 그림자가 나타나기 때문에box-shadow
속성을 적용할 수 없다. - 방향에 따라 일부
border
의 두께 값으로round(height/2)
(height를 2로 나눈 값을 반올림한 값)이 적용되기 때문에 삼각형 화살표의 방향이'up'
또는'down'
인 경우에는$width
값을 짝수로,'left'
또는'right'
인 경우에는$height
값을 짝수로 지정해주는 것이 더 정확하다.- 홀수값을 지정하여 해당 값이 반올림되면 아이콘 태그 영역의 1px 밖으로 삼각형 아이콘이 튀어나간다. 홀수일 때, 아이콘이 태그 영역 밖으로 튀어나가는 현상이 불편하다면 해당 값의 소수점이 반올림이 아닌 버림 처리되도록 mixin의
round()
함수를floor()
함수로 변경해주는 것도 하나의 방법이다.
- 홀수값을 지정하여 해당 값이 반올림되면 아이콘 태그 영역의 1px 밖으로 삼각형 아이콘이 튀어나간다. 홀수일 때, 아이콘이 태그 영역 밖으로 튀어나가는 현상이 불편하다면 해당 값의 소수점이 반올림이 아닌 버림 처리되도록 mixin의
[브라우저 호환성]
3. Angle (화살표 아이콘)
Angle 화살표 아이콘은 컨텐츠 좌/우 이동용 화살표, 더보기 화살표, 펼침/접힘 상태를 표현하는 아이콘 등 다양한 용도로 사용되는 아이콘이다.
[Codepen 예시]
[Parameter]
Name | Description | Type | Default value |
---|---|---|---|
$direction |
아이콘 화살표 방향 (‘up’, ‘down’, ‘left’, ‘right’) | String |
'up' |
$width |
아이콘 넓이 값 | number |
50px |
$height |
아이콘 높이 값 | number |
25px |
$thick |
아이콘 화살표 두께 값 | number |
2px |
$color |
아이콘 색상 값 | color |
#222 |
$display |
아이콘 display 속성 값 | inline-block/block |
inline-block |
$position |
아이콘 position 속성 값 | relative/absolute |
relative |
[SCSS @mixin]
$sqrt_2: 1.41421356237; @mixin iconAngle($direction: 'up', $width: 50px, $height: 25px, $thick: 2px, $color: #222, $display: inline-block, $position: relative) { position: $position; display: $display; width: $width; height: $height; @if ($direction == 'up' or $direction == 'down') { $size: floor($width/$sqrt_2 - 2*$thick); $rate: 2*$height/$width; $margin-top: round($rate*$size/(2*$sqrt_2)); &:before { content: ''; position: absolute; top: 50%; left: 50%; width: $size; height: $size; @if ($rate == 1) { -webkit-transform: translate(-50%, -50%) rotate(45deg); transform: translate(-50%, -50%) rotate(45deg); } @else { -webkit-transform: translate(-50%, -50%) scale(1, $rate) rotate(45deg); transform: translate(-50%, -50%) scale(1, $rate) rotate(45deg); } @if ($direction == 'up') { margin-top: $margin-top; border-top: $thick solid $color; border-left: $thick solid $color; } @else if ($direction == 'down') { margin-top: - $margin-top; border-right: $thick solid $color; border-bottom: $thick solid $color; } } } @else if ($direction == 'left' or $direction == 'right') { $size: floor($height/$sqrt_2 - 2*$thick); $rate: 2*$width/$height; $margin-left: round($rate*$size/(2*$sqrt_2)); &:before { content: ''; position: absolute; top: 50%; left: 50%; width: $size; height: $size; @if ($rate == 1) { -webkit-transform: translate(-50%, -50%) rotate(45deg); transform: translate(-50%, -50%) rotate(45deg); } @else { -webkit-transform: translate(-50%, -50%) scale($rate, 1) rotate(45deg); transform: translate(-50%, -50%) scale($rate, 1) rotate(45deg); } @if ($direction == 'left') { margin-left: $margin-left; border-left: $thick solid $color; border-bottom: $thick solid $color; } @else if ($direction == 'right') { margin-left: - $margin-left; border-top: $thick solid $color; border-right: $thick solid $color; } } } }
[그림으로 이해하는 @mixin]
- Angle 아이콘은 아이콘 요소 내에서
:before
에 그려지는데,:before
의width, height
의 속성 값인$size
는 위/아래 화살표의 경우에는 위 그림의 수식처럼 파라미터 값인$width
와$thick
으로 구한다. - 만약 아이콘의
2*$height/$width
이라는 비율을 나타내는$rate
값이 1이라면, 아이콘 화살표의 각은 직각이다. 하지만,$rate
가 1이 아니라면 화살표는 직각이 아니기 때문에transform
의scale(1, $rate)
속성 값을 이용하여 아이콘을 비율에 맞게 확대/축소한다.
- 아이콘을 중앙 정렬하기 위해
postion: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
를 적용하면 위 그림과 같이 영역 밖으로 화살표가 튀어나오는 현상이 발생한다. 화살표가 두 방향의border
로만 그려진 아이콘이지만,:before
의 원래 영역은 박스 형태이기 때문에 실제:before
의 영역은 보이는 영역의 2배이다. 이 때문에margin-top
값으로 화살표 모양이 아이콘 영역 내에 오도록 위치를 조정해야 한다.margin-top
값을 구하는 방법은 위 그림의 수식을 참고하길 바란다.
- 왼쪽/오른쪽 화살표의 경우,
:before
의$size
값은 위/아래 화살표와 달리$width
대신$height
와$thick
으로 계산한다. $rate
값 역시2*$height/$width
이 아닌2*$width/$height
이라는 비율로 적용되기 때문에 화살표가 직각이 아닌 경우에는transform
의 속성 값은scale($rate, 1)
로 적용된다.
- 왼쪽/오른쪽 화살표는 위/아래 화살표와 달리 좌측 또는 우측으로 화살표 영역이 튀어나오기 때문에 위 그림과 같이
margin-top
대신margin-left
값이 적용된다.
[특징 및 주의사항]
- 그림자 추가 시
:before
에box-shadow
속성 값을 적용하면 된다. 하지만, 가급적box-shadow
속성은 적용하지 않는 것이 좋다.- 화살표 영역 밖에도 사각형 형태의 그림자가 생기는 문제로
overflow:hidden
을 아이콘 요소에 적용하였지만, 이로 인해 아이콘 영역 밖으로 노출된 그림자가 잘리는 현상이 발생한다. 또한,transform
속성으로 인해 그림자 위치가 예상과 다르게 표현된다. overflow:hidden
속성 때문에 다른 아이콘과 달리$size
값의 소수점 자리값을floor()
버림 처리하였다. 만약 그림자 속성을 사용하지 않는다면,overflow:hidden
속성을 제거하는 것이 좋다.
- 화살표 영역 밖에도 사각형 형태의 그림자가 생기는 문제로
[브라우저 호환성]
- 지원 범위 : transform속성 적용 가능 범위 (IE9이상)
4. Arrow (화살표 아이콘)
Arrow 화살표 아이콘은 컨텐츠 좌/우 이동용 화살표, 페이지 상단(TOP) 이동을 표현하는 화살표 등의 용도로 사용되는 아이콘이다.
[Codepen 예시]
[Parameter]
Name | Description | Type | Default value |
---|---|---|---|
$direction |
아이콘 화살표 방향 (‘up’, ‘down’, ‘left’, ‘right’) | String |
'up' |
$width |
아이콘 넓이 값 | number |
50px |
$height |
아이콘 높이 값 | number |
50px |
$thick |
아이콘 화살표 두께 값 | number |
2px |
$color |
아이콘 색상 값 | color |
#222 |
$display |
아이콘 display 속성 값 | inline-block/block |
inline-block |
$position |
아이콘 position 속성 값 | relative/absolute |
relative |
[SCSS @mixin]
$sqrt_2: 1.41421356237; @mixin iconArrow($direction: 'up', $width: 50px, $height: 50px, $thick: 2px, $color: #222, $display: inline-block, $position: relative) { position: $position; display: $display; width: $width; height: $height; &:before { content: ''; position: absolute; border-top: $thick solid $color; border-left: $thick solid $color; @if ($direction == 'up' or $direction == 'down') { $size: round($width/$sqrt_2 - $thick); left: 50%; width: $size; height: $size; @if ($direction == 'up') { top: round($width/2); -webkit-transform: translate(-50%, -50%) rotate(45deg); transform: translate(-50%, -50%) rotate(45deg); } @else if ($direction == 'down') { bottom: round($width/2); -webkit-transform: translate(-50%, 50%) rotate(-135deg); transform: translate(-50%, 50%) rotate(-135deg); } } @else if ($direction == 'left' or $direction == 'right') { $size: round($height/$sqrt_2 - $thick); top: 50%; width: $size; height: $size; @if ($direction == 'left') { left: round($height/2); -webkit-transform: translate(-50%, -50%) rotate(-45deg); transform: translate(-50%, -50%) rotate(-45deg); } @else if ($direction == 'right') { right: round($height/2); -webkit-transform: translate(50%, -50%) rotate(135deg); transform: translate(50%, -50%) rotate(135deg); } } } &:after { content: ''; position: absolute; background-color: $color; @if ($direction == 'up' or $direction == 'down') { @if ($direction == 'up') { top: round($thick/$sqrt_2); } @else if ($direction == 'down') { bottom: round($thick/$sqrt_2); } left: 50%; width: $thick; height: $height - round($thick/$sqrt_2); -webkit-transform: translate(-50%, 0); transform: translate(-50%, 0); } @else if ($direction == 'left' or $direction == 'right') { top: 50%; @if ($direction == 'left') { left: round($thick/$sqrt_2); } @else if ($direction == 'right') { right: round($thick/$sqrt_2); } width: $width - round($thick/$sqrt_2); height: $thick; -webkit-transform: translate(0, -50%); transform: translate(0, -50%); } } }
[그림으로 이해하는 @mixin]
- Arrow 아이콘은 아이콘 태그로 영역을 잡아주고,
:before
가상 요소로는 화살표 끝을,:after
가상 요소로는 화살표 막대를 그려준다. :before
부터 살펴보자면, 가상 요소의width, height
값은$size
값이며, 값을 구하는 수식은 위의 그림을 참고하길 바란다.
- 위로 화살표로 예를 들자면, 화살표 모양을 아이콘 영역의 상단은 맨 위에 정렬하고, 수평으로는 중앙 정렬하기 위해 우선
postion: absolute; top: 0; left: 50%; transform: translate(-50%,-50%);
을 적용하였다. 하지만, Angle 아이콘처럼:before
는 원래 박스 형태이기 때문에 보이는 화살표 모양보다 2배 더 크게:before
영역이 잡힌다. 이 때문에top
값으로 화살표 모양이 아이콘 영역 내에 오도록 위치를 조정해야 한다.top
값은$width/2
값이다. - 아래 화살표는 같은 원리지만,
top
대신bottom에
같은 값을 적용하면 된다.
- 왼쪽/오른쪽 화살표의
$size
값을 구하는 식은 위/아래 화살표와 달리$width
대신$height
값을 이용한다.
- 왼쪽/오른쪽 화살표 역시 같은 원리지만,
top
에50%
값을 적용하고 각각left
나right
에$height/2
값을 적용하면 된다.
:after
로 그려지는 화살표 막대는 먼저 위로 화살표로 예를 들자면,width
값은$thick
파라미터 값이며height
는 막대가 화살표 모양 밖으로 튀어나오지 않도록$height
에서$thick/$sqrt_2
를 빼준 값으로 적용한다.- 그 후, 기존의 방법대로 수평 정렬을 하고
top
에는$thick/$sqrt_2
값을 적용하여 화살표 모양과 겹쳐지지 않게 상단에 여백을 준다. - 다른 방향의 화살표도 같은 원리이므로 위의 그림을 참고하길 바란다.
[특징 및 주의사항]
- 그림자 추가 시(
box-shadow
속성 적용), 화살표 외 영역에 그림자가 나타나기 때문에box-shadow
속성을 적용할 수 없다.
[브라우저 호환성]
- 지원 범위 : transform속성 적용 가능 범위 (IE9이상)
5. Menu (햄버거 메뉴 아이콘)
햄버거 메뉴 아이콘은 메뉴 버튼에 자주 사용되는 아이콘이다.
[Codepen 예시]
[SCSS Parameter]
Name | Description | Type | Default value |
---|---|---|---|
$width |
아이콘 막대 넓이 값 | number |
21px |
$thick |
아이콘 막대 두께 값 | number |
2px |
$interval |
아이콘 막대 간 간격 | number |
6px |
$color |
아이콘 색상 값 | color |
#000 |
$padding |
아이콘 여백 값 | number |
0 |
[SCSS @mixin]
@mixin iconMenuRequiredParent($width: 21px, $thick: 2px, $interval: 6px, $color: #000, $padding: 0) { position: relative; display: block; margin: $thick+$interval+$padding $padding; &, &:before, &:after { width: $width; height: $thick; background-color: $color; } &:before, &:after { content: ''; position: absolute; left: 0; } &:before { top: -($thick+$interval); } &:after { top: $thick+$interval; } }
[그림으로 이해하는 @mixin]
- 메뉴 아이콘은 아이콘 요소와 2개의 가상 요소에 파라미터 값인
$thick
으로 막대의height
값을,$width
로width
값을,$color
로background-color
값을 지정하여 3개의 막대를 그려준다. - 그 후, 아이콘 요소를 중심으로 가상 요소의 위치를 잡아준다. 가상 요소의 위치는
position: absolute
에top
값으로 각각±($thick+$interval)
을 적용하여 배치한다. - 마지막으로 부모 요소가 아이콘을 감쌌을 때, 영역이 잡힐 수 있도록 아이콘 요소의
margin-top/bottom
값으로$padding
값과 가상 요소의top
값($thick+$interval)
을 더한 값을 적용한다.
[특징 및 주의사항]
- 아이콘 요소를 감싸는 부모 요소가 반드시 필요하다. (보통, 메뉴 버튼으로 많이 사용되기 때문에
<button>
이 부모 요소가 된다.) border-radius
와background-color
속성을 이용하여 둥근 배경을 아이콘의 부모 요소에 적용할 수 있다. 배경 내 여백은 아이콘 요소의$padding
값으로 설정할 수 있다.- 아이콘 요소와 두 가상 요소에 공통된
box-shadow
속성을 적용하면 그림자를 추가할 수 있다.
[브라우저 호환성]
- 지원 범위 : 가상선택자(:before, :after) 적용 가능 범위(IE8이상)
- 이슈 : 안드로이드 하위에서 margin 관련 이슈 발생
BEFORE | AFTER |
---|---|
안드로이드 하위(4.X.X)에서 margin-top, margin-bottom 값과 관련된 이슈로 인해 상하 영역이 잡히지 않는 문제 발생 | 아이콘 태그의 부모 요소에 width/height값 추가하여 이슈 해결 |
6. More (더보기 아이콘)
더보기 아이콘은 수직/수평 형태로 더보기 버튼용 아이콘으로 자주 쓰인다.
[Codepen 예시]
[SCSS Parameter]
Name | Description | Type | Default value |
---|---|---|---|
$direction |
아이콘 방향 (‘vertical’, ‘horizontal’) | string |
'vertical' |
$size |
아이콘 내 원 넓이/높이 값 | number |
4px |
$interval |
아이콘 내 원 간 간격 | number |
4px |
$color |
아이콘 색상 값 | color |
#000 |
$padding |
아이콘 여백 값 | number |
0 |
[SCSS @mixin]
@mixin iconMoreRequiredParent($direction: 'vertical', $size: 4px, $interval: 4px, $color: #000, $padding: 0) { position: relative; display: block; margin: $size+$interval+$padding; &, &:before, &:after { width: $size; height: $size; border-radius: 50%; background-color: $color; } &:before, &:after { content: ''; position: absolute; } @if ($direction == 'vertical') { &:before { top: -($size+$interval); left: 0; } &:after { top: $size+$interval; left: 0; } } @else if ($direction == 'horizontal') { &:before { top: 0; left: -($size+$interval); } &:after { top: 0; left: $size+$interval; } } }
[그림으로 이해하는 @mixin]
- 더보기 아이콘의 제작 원리는 5번 메뉴 아이콘과 거의 유사하다.
- 다만, 수평/수직 방향을 설정할 수 있기 때문에 가상 요소의 위치 값과 아이콘 요소의
margin
값에는 차이가 있다. 또한, 수직 방향의 경우에는 위치 값을top
값으로 주지만, 수평 방향의 경우에는 동일한 위치 값을left
값으로 준다.margin
값은 메뉴 아이콘과 달리margin-top/bottom
값과 동일한 값을margin-left/right
에도 똑같이 적용한다.
[특징 및 주의사항]
- 아이콘 요소를 감싸는 부모 요소가 반드시 필요하다. (보통, 메뉴 버튼으로 많이 사용되기 때문에
<button>
이 부모 요소가 된다.) border-radius
와background-color
속성을 이용하여 둥근 배경을 아이콘의 부모 요소에 적용할 수 있다. 배경 내 여백은 아이콘 요소의$padding
값으로 설정할 수 있다.- 아이콘 요소와 두 가상 요소에 공통된
box-shadow
속성을 을 적용하면 그림자를 추가할 수 있다.
[브라우저 호환성]
- 지원 범위 : border-radius속성 적용 가능 범위 (IE9이상)
- 이슈 : 위 5번 메뉴 아이콘과 동일한 이슈 발생
ICON 특징 비교
아이콘명 | 가상 요소 | 부모 요소 필요 | 용도 | 브라우저 호환성 | 대체가능한 문자 |
---|---|---|---|---|---|
X | 2개 (:before, :after) | X | 취소, 삭제 | transform 적용 범위 (IE9↑) |
X, × |
Triangle | 1개 (:before) | X | 좌우상하 화살표 | :before ,:after 가상 선택자 적용 범위(IE8↑) |
<, >, ∧, ∨ |
Angle | 1개 (:before) | X | 좌우상하 화살표 | transform 적용 범위 (IE9↑) |
<, >, ∧, ∨ |
Arrow | 2개 (:before, :after) | X | 좌우상하 화살표 | transform 적용 범위 (IE9↑) |
↑, ↓, ←, → |
Menu | 2개 (:before, :after) | O | 메뉴 | :before ,:after 가상 선택자 적용 범위(IE8↑) |
없음 |
More | 2개 (:before, :after) | O | 더보기 | border-radius 적용 범위 (IE9↑) |
… |
Pure CSS ICON vs SVG Sprite Image ICON 비교
성능 비교
Pure CSS ICON과 SVG Sprite Image ICON의 성능을 비교하기 위해 1개의 공통 Sprite Image ICON과 각각의 방법으로 생성한 22개의 ICON로 만든 페이지로 실제 테스트를 진행하였다. 그 결과, 단순한 도형 외에는 Sprite Image를 사용하기 때문에 이미지 요청 수는 동일하며 전체 로딩 속도에는 별 차이가 없었지만 이미지 요청량과 Paint과정에서 성능 차이를 발견하였다. 실제 성능 테스트 결과는 아래와 같다.
[Paint 속도 비교]
- Paint의 속도 비교 결과, 미미한 차이지만 Pure CSS ICON이 더 빠르게 페인트 된다. 특히, Layer root인 document의 자손이 Paint될 때, 속도가 더 차이가 난다. 그 이유는 아래 Paint Event log 비교를 통해 알 수 있다.
Pure CSS ICON | Sprite Image ICON |
---|---|
[Paint Event log 비교]
- 두 방법의 Paint Event log를 비교해보면, Pure CSS ICON의 테스트 페이지에서는 검색 아이콘에서 Sprite Image ICON이 1개만 사용되었기 때문에 Layer root(#document)의 Paint 과정에서 자식 Layer의 1회 Update Layer tree와 1회 Paint가 발생하였다. 반면, Sprite Image ICON의 테스트 페이지에서는 총 23개의 Sprite Image ICON이 사용되었기 때문에 Layer root(#document)의 Paint 과정에서 추가적으로 23회 Update Layer tree와 23회 Paint가 발생하였다.
- Sprite Image ICON의 테스트 페이지의 ‘DevTool > Event log’에서 하위 Paint 항목을 클릭해보면, 파란색 영역이 잡히는데 이 영역은 Network에서 요청된 SVG Sprite 판의 영역과 같은 크기이다. CSSTrigger를 참고해보면, Sprite Image는
background
관련 속성을 사용하기 때문에 사용 시 이미지 요청 시 Paint가 발생한다. 즉, 아이콘이 추가될 때마다 Paint에 대한 비용이 증가할 수 밖에 없다. - 따라서, Sprite Image ICON을 사용하면 Pure CSS ICON보다 더 많은 Paint 시간이 소요된다.
Pure CSS ICON | Sprite Image ICON |
---|---|
[Paint flashing & Network 비교]
- 아래 그림과 같이 ‘devTool > Rendering > Paint flashing’을 설정한 후, ‘devTool > Network’에서 확인해보면 앞서 말한 바와 같이 Sprite Image ICON은 paint 비용이 더 많이 소모됨을 확인할 수 있다.
- 두 테스트 페이지 모두 1개의 Sprite용 SVG 파일을 사용하고 있기 때문에 이미지 요청 수는 동일하지만, 이미지 요청량에서는 Pure CSS ICON을 많이 사용할수록 더 많은 차이가 발생한다.
Pure CSS ICON | Sprite Image ICON |
---|---|
결론
위 테스트 결과, 성능 면에서 Pure CSS ICON은 Paint와 이미지 요청량에서는 차이가 있지만 성능이 좋은 최신 디바이스들의 로딩 속도에 큰 영향을 주진 않는다. CSS로 그릴 수 있는 아이콘을 많이 사용한다면 Pure CSS ICON을 사용하는 것이 좋겠지만, 요청량에 유의미한 차이가 없다면 무조건 Pure CSS ICON을 사용하기보다는 장단점을 고려하여 상황에 따라 작업자가 편한 방법을 사용하는 것이 좋다.
어떤 경우, 어떤 방법을 쓰면 좋을까?
- IE8과 같은 하위 브라우저를 대응하는 경우, Pure CSS Icon을 사용하면 하위 브라우저에서 문자로 대체하면 유지 보수 비용이 더 소요되고 설계팀과의 협의도 필요하기 때문에 Sprite Image ICON를 사용하는 것을 권장한다.
- 하위 브라우저 이슈가 없고, 반응형 디자인으로 디바이스 넓이에 따라 아이콘 사이즈가 변경되거나 활성/비활성 등의 상태 변화(예. 마우스 오버 시, 포인트 컬러로 변경)에 따라 아이콘의 컬러 값이 변경되는 경우라면 유지 보수 측면에서 이미지 추가/변경이 적은 Pure CSS ICON을 사용하는 것을 권장한다.
- 위의 아이콘처럼 Pure CSS ICON으로 변경 가능한 이미지가 많고, 이미지 전송량을 줄여 UI 성능을 개선해야 하는 경우라면 Pure CSS ICON로 이미지 아이콘을 대체하는 것도 좋은 방법이다.
참고자료
Pure CSS ICON 자료
- cssicon.space
- uiplayground – CSS3 Icons
- github 저장소 – jorenvanhee/sass-burger
- NULI – CSS로 그리는 그림, Pure CSS Drawing
- stackoverflow – CSS3 box-shadow inset painful performance killer
6개의 댓글
오승환 · 2017년 11월 9일 10:15 오후
위의 mixin 을 모아서, npm 에 올려도 좋을 것 같아요..!
공유 정보 정말 좋네요…!
브라우저 호환 범위가 ‘거의 모든’인 부분만 좀 더 상세한 정보로 얻을 수 있으면 좋겠다고 생각합니다. 좋은 공유 감사합니다.
Nyx Chung · 2017년 11월 20일 1:08 오후
좋은 의견 감사합니다~ 적극 고민해보겠습니다.
Nyx Chung · 2017년 11월 22일 11:26 오전
의견 가운데 바로 반영이 가능한 ‘거의 모든’으로 표현되었던 부분은 수정하였습니다 🙂
오승환 · 2017년 11월 22일 2:03 오후
감사합니다!
Nyx Chung · 2017년 12월 18일 3:15 오후
https://www.npmjs.com/package/icon-scss-mixins-witblog NPM에 등록되었습니다. 다시 한번 좋은 의견 감사합니다.
오승환 · 2017년 12월 18일 3:40 오후
Github 에 Star 했습니다! 감사합니다!