Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create SVG components with scoped CSS

Tags:

css

reactjs

svg

I am creating React components that will render out various SVGs:

const Close = ({
  fill, width, height, float,
}) => (
  <svg width={ `${width}px` } height={ `${height}px` } viewBox="0 0 14.48 14.48" style={ { float: `${float}`, cursor: 'pointer' } }>
    <title>x</title>
    <g id="Layer_2" data-name="Layer 2">
      <g id="Background">
        <line style={ { fill: 'none', stroke: `${fill}`, strokeMiterlimit: 10 } } x1="14.13" y1="0.35" x2="0.35" y2="14.13" />
        <line style={ { fill: 'none', stroke: `${fill}`, strokeMiterlimit: 10 } } x1="14.13" y1="14.13" x2="0.35" y2="0.35" />
      </g>
    </g>
  </svg>
);

It's very convenient to be able to supply various attributed to this component to control dimensions, colour, etc...

What I don't have a good solution for, however, is handling the styles in a DRY manner. Note the line elements have the same value for style. I presently have them written inline because if I added an embedded stylesheet, then I would get class name collisions with other SVGs I render elsewhere on the page (our SVG software uses the same classes over and over).

<style scoped> has been removed from the spec: https://github.com/whatwg/html/issues/552

Shadow DOM is not yet supported by Edge: https://caniuse.com/#feat=shadowdomv1

Is there any other alternative for scoping styles?

like image 775
Mister Epic Avatar asked Sep 18 '18 13:09

Mister Epic


2 Answers

To combine the best of both worlds, you could create an external styles file, as you would for CSS, but with exported style objects. You could then import it into any file that needs it.

As example, main file:

import React, { Component } from 'react';
import { render } from 'react-dom';
import * as Styles from './svgstyles';

class App extends Component {
  render() {
    return (
      <div>
        <svg width="100" height="200" viewBox="0 0 100 200">  
          <rect x="0" y="0" width="10" height="10" style={Styles.style1} />
          <rect x="15" y="0" width="10" height="10" style={Styles.style2} />
          <rect x="30" y="0" width="10" height="10" style={Styles.style3} />
          <rect x="45" y="0" width="10" height="10" style={Styles.style4} />
          <rect x="0" y="15" width="10" height="10" style={Styles.style4} />
          <rect x="15" y="15" width="10" height="10" style={Styles.style3} />
          <rect x="30" y="15" width="10" height="10" style={Styles.style2} />
          <rect x="45" y="15" width="10" height="10" style={Styles.style1} />
        </svg>
      </div>
    );
  }
}

render(<App />, document.getElementById('root'));

And an external styles file:

export const style1 = {
  stroke: 'red',
  strokeWidth: "1",
  fill: "blue",
}

export const style2 = {
  stroke: 'red',
  strokeWidth: "1",
  fill: "green",
}

export const style3 = {
  stroke: 'red',
  strokeWidth: "1",
  fill: "yellow",
}

export const style4 = {
  ...style3,
  fill: "pink",
}

Live example here

like image 117
Ted Avatar answered Oct 19 '22 10:10

Ted


If you just want to DRY up the code, you could create one style-object and reuse it:

const Close = ({
                 fill, width, height, float,
               }) => {
  const style = { fill: 'none', stroke: `${fill}`, strokeMiterlimit: 10 }
  return (
    <svg width={ `${width}px` } height={ `${height}px` } viewBox="0 0 14.48 14.48" style={ { float: `${float}`, cursor: 'pointer' } }>
      <title>x</title>
      <g id="Layer_2" data-name="Layer 2">
        <g id="Background">
          <line style={ style } x1="14.13" y1="0.35" x2="0.35" y2="14.13" />
          <line style={ style } x1="14.13" y1="14.13" x2="0.35" y2="0.35" />
        </g>
      </g>
    </svg>
  );
}

This would also result in a small performance improvement since fewer objects would be created in each render cycle.

like image 33
pgsandstrom Avatar answered Oct 19 '22 09:10

pgsandstrom