8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React】文字の色に合わせたsvgアイコンを表示する

Last updated at Posted at 2024-09-02

背景

Reactでアイコン付きボタンを作っていて、「アイコンの色もボタンに合わせたいなー」ということがありました。
(というより、アイコンにカスタマイズ性を持たせたい)
備忘録として...

ゴール

以下のようにアイコン付きのボタンを表示させる時に、アイコンの色を文字色と同じように揃えます。

結論

fill="currentColor" によって、アイコンの色を親要素で適応されているcolorで塗りつぶす。

サンプルコード

コンポーネントの作りはざっくり以下のような感じ

  • ButtonWithIcon というコンポーネント名だが、その実態はButtonコンポーネントの拡張で、iconがあると、そのアイコンを左側に表示
  • props として color を受け取って、文字色に適用
  • alt はアクセシビリティ的に必要なので追加

全体コード

ButtonWithIcon.tsx
import React, { ReactNode } from 'react';

type ButtonWithIconProps = {
  children: ReactNode;
  icon?: 'heart' | 'star';
  color?: string;
  alt?: string;
};

const buttonStyle = {
  display: 'flex',
  justifyContent: 'center',
  alignItems: 'center',
  gap: '4px',
  borderRadius: '4px',
  backgroundColor: 'gray',
  paddingTop: '8px',
  paddingBottom: '8px',
  paddingRight: '16px',
  paddingLeft: '16px',
};

export const ButtonWithIcon = (props: ButtonWithIconProps) => {
  const { children, icon, color } = props;
  return (
    <button
      style={{
        ...buttonStyle,
        color,
      }}
    >
      {icon && renderSvgIcon(props)}
      {children}
    </button>
  );
};

function renderSvgIcon(props: ButtonWithIconProps) {
  const svgAlt = getAlt(props);
  const icon = props.icon;

  const svgProps = {
    ...(svgAlt ? { 'aria-label': svgAlt } : { 'aria-hidden': true }),
    fill: 'currentColor',
    xmlns: 'http://www.w3.org/2000/svg',
    width: '16px',
    height: '16px',
    viewBox: '0 0 512 512',
  };

  switch (icon) {
    case 'heart':
      return (
        <svg {...svgProps}>
          <path
            d="M380.63,32.196C302.639,33.698,264.47,88.893,256,139.075c-8.47-50.182-46.638-105.378-124.63-106.879
		C59.462,30.814,0,86.128,0,187.076c0,129.588,146.582,189.45,246.817,286.25c3.489,3.371,2.668,3.284,2.668,3.284
		c1.647,2.031,4.014,3.208,6.504,3.208v0.011c0,0,0.006,0,0.011,0c0,0,0.006,0,0.011,0v-0.011c2.489,0,4.856-1.177,6.503-3.208
		c0,0-0.821,0.086,2.669-3.284C365.418,376.526,512,316.664,512,187.076C512,86.128,452.538,30.814,380.63,32.196z"
          ></path>
        </svg>
      );
    case 'star':
      return (
        <svg {...svgProps}>
          <polygon points="256,12.531 327.047,183.922 512,198.531 370.938,319.047 414.219,499.469 256,402.563 97.781,499.469 141.063,319.047 0,198.531 184.953,183.922"></polygon>
        </svg>
      );
    default:
      return null;
  }
}

function getAlt({ alt, icon }: ButtonWithIconProps) {
  if (alt) return alt;

  switch (icon) {
    case 'heart':
      return 'いいね';
    case 'star':
      return 'お気に入り';
    default:
      return '';
  }
}

細かく解説

svgファイルのレンダリング方法

svgのレンダリングには複数の手法があるので、せっかくなので紹介します。

Img タグを使用する

もっとも有名で簡単。
この方法は、SVGファイルを直接インポートし、それらを img タグの src プロパティとして渡します。

import star from "./assets/star.svg";
export const Icon= () => {
  return <img src={star} alt="" />;
}

この方法では、SVGの幅や高さは変更できても色の変更などができず、スタイルのカスタマイズの柔軟性が制限されます。
この方法は簡単ですが、そのデメリットから非推奨なやり方です。

※Next.jsを使用している場合、'next/image'を使用すると思いますが、これも同様です。

SVG要素を返すカスタムReactコンポーネント

今回使用した方法です。推し
create-react-appでアプリを立ち上げているなら、すでにSVGRが組み込まれているため、Reactコンポーネントのようにsvgを使用できます。

  const svgProps = {
    ...(svgAlt ? { 'aria-label': svgAlt } : { 'aria-hidden': true }),
    fill: 'currentColor',
    xmlns: 'http://www.w3.org/2000/svg',
    width: '16px',
    height: '16px',
    viewBox: '0 0 512 512',
  };
  
  return (
    <svg {...svgProps}>
      <polygon points="256,12.531 327.047,183.922 512,198.531 370.938,319.047 414.219,499.469 256,402.563 97.781,499.469 141.063,319.047 0,198.531 184.953,183.922"></polygon>
    </svg>
  );

この方法を使用すると、塗りつぶしの色を変更などsvgタグのプロパティに簡単にアクセスできます。

<svg fill="#fff" /> 

今回は、buttonのカラーと合わせたいのでcurrentColorを使用します。

アクセシビリティな要素

以下の要素です。

  const svgProps = {
    ...(svgAlt ? { 'aria-label': svgAlt } : { 'aria-hidden': true }),

aria-label

要素の既定の アクセシブル名 がなかったり、その内容を正確に記述していなかったりして、オブジェクトに意味を与えるために関連付けることができるコンテンツが DOM に表示されていないことがあります。よくある例は、 SVG やアイコンフォント(使用すべきではない)を含む、テキストのないボタンです。

というようにユーザーにボタンの機能を明確に伝えることができるので、svgタグを使用する時につけておく方が良さそう

aria-hidden

aria-hidden 属性を使用することで、アクセシビリティ API から操作不可能なコンテンツを隠すことができます。

altがないなら、あえて情報を取得させないようにしておく。

svgファイルの不要な記述は消しておく

今回サンプルコードのためのsvgファイルフリー素材を利用しました。

そのまま利用すると、fillプロパティなどで色が入ってしまうので、そういった要素を削除します。
ついでに不要なプロパティを削ってデータをスッキリさせましょう。
svgは描画時に演算を要するので、データは軽い方が好ましいです。

before

<!--?xml version="1.0" encoding="utf-8"?-->
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->

<svg version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 64px; height: 64px; opacity: 1;" xml:space="preserve">
<style type="text/css">
	.st0{fill:#4B4B4B;}
</style>
<g>
	<path class="st0" d="M380.63,32.196C302.639,33.698,264.47,88.893,256,139.075c-8.47-50.182-46.638-105.378-124.63-106.879
		C59.462,30.814,0,86.128,0,187.076c0,129.588,146.582,189.45,246.817,286.25c3.489,3.371,2.668,3.284,2.668,3.284
		c1.647,2.031,4.014,3.208,6.504,3.208v0.011c0,0,0.006,0,0.011,0c0,0,0.006,0,0.011,0v-0.011c2.489,0,4.856-1.177,6.503-3.208
		c0,0-0.821,0.086,2.669-3.284C365.418,376.526,512,316.664,512,187.076C512,86.128,452.538,30.814,380.63,32.196z" style="fill: rgb(75, 75, 75);"></path>
</g>
</svg>

style、version、id、xmlns:xlink、gタグ など不要な要素は削除し、他のsvg要素と共通している部分は括り出します。サイズもいい感じに変更しましょう。

今回はないですが、traslateで座標を冗長に変更しているファイルを結構見るので、不要な演算も消しましょう。

after

  const svgProps = {
    ...(svgAlt ? { 'aria-label': svgAlt } : { 'aria-hidden': true }),
    fill: 'currentColor',
    xmlns: 'http://www.w3.org/2000/svg',
    width: '16px',
    height: '16px',
    viewBox: '0 0 512 512',
  };
        
  <svg {...svgProps}>
      <path
            d="M380.63,32.196C302.639,33.698,264.47,88.893,256,139.075c-8.47-50.182-46.638-105.378-124.63-106.879
		C59.462,30.814,0,86.128,0,187.076c0,129.588,146.582,189.45,246.817,286.25c3.489,3.371,2.668,3.284,2.668,3.284
		c1.647,2.031,4.014,3.208,6.504,3.208v0.011c0,0,0.006,0,0.011,0c0,0,0.006,0,0.011,0v-0.011c2.489,0,4.856-1.177,6.503-3.208
		c0,0-0.821,0.086,2.669-3.284C365.418,376.526,512,316.664,512,187.076C512,86.128,452.538,30.814,380.63,32.196z"
      ></path>
  </svg>

かなりスッキリしましたね。

最後に

ここまでみていただきありがとうございます!
結論はfill="currentColor"というだけなのですが、コンポーネントを作るまでに考えたことも補足で書いていると少し長くなりました。

参考

How to Use SVG in React
aria-label
aria-labelを使ってみよう
aria-hidden

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?