- const graphsNum = 50
- const graphWidth = 500
- const graphHeight = 200
- const graphPaddingBottom = 30
- const graphPaddingLeft = 40
- const totalLinesCount = 30
- const totalLabelsCount = 10
- const separatorsCount = 10
- const verticalSeparatorsCount = 5
- const lineStepX = (graphWidth - graphPaddingLeft) / totalLinesCount
- const labelStepX = (graphWidth - graphPaddingLeft) / totalLabelsCount
- const separatorStepX = (graphWidth - graphPaddingLeft) / separatorsCount
- const minBarHeight = 40
- const graphLegendPadding = 30
- const labels = ['400k', '300k', '200k', '100k']

h1 SVG Graphs Demo

div.wrapper
  each val, i in (new Array(graphsNum).fill(null))
    div.graph-wrapper
      header.graph-header
        .graph-title
          .icon-holder
          h5
            span Metric Name 
            | (Graph #{i + 1})
        .graph-metric
          - const metricVal = Math.round(Math.random() * 80 + 20)
          - const comparisonVal = Math.round(Math.random() * 80 + 20)
          - const growthVal = Math.round((metricVal / comparisonVal - 1) * 100)
          if growthVal
            h5.growth-value #{`${growthVal}%`}
          h4.metric-value(style={ color: metricVal > 50 ? 'green' : 'red' }) #{`$${metricVal}`}
          h4.comparison-value #{`vs. $${comparisonVal}`}
          
      svg(
        xmlns="http://www.w3.org/2000/svg"
        version="1.1"
        width=graphWidth
        viewBox=`0 0 ${graphWidth} ${graphHeight}`
      )
        defs
          linearGradient#positive-value-gradient(
            x1=0
            y1=0
            x2=0
            y2=1
          )
            stop(
              stop-color="#2ecc71"
              offset="0%"
            )
            stop(
              stop-color="#27ae60"
              offset="100%"
            )
          linearGradient#negative-value-gradient(
            x1=0
            y1=0
            x2=0
            y2=1
          )
            stop(
              stop-color="#e74c3c"
              offset="0%"
            )
            stop(
              stop-color="#c0392b"
              offset="100%"
            )

        //-
          Render our graph footer lines and labels.
          ⚠️ This group of nodes will be identical for each graph and will end up being duplicated many times!
        g.chart-graph-legend
        
          //- Legend labels
          g
            rect(
              x=graphPaddingLeft + 0
              y=graphHeight - graphLegendPadding / 2
              width=10
              height=10
              fill="url(#negative-value-gradient)"
            )
            text(
              x=graphPaddingLeft + 15
              y=graphHeight - graphLegendPadding / 6
              fill="url(#negative-value-gradient)"
              font-size="14"
              font-family="Helvetica, sans-serif"
            ) Bad
            
          g
            rect(
              x=graphPaddingLeft + 60
              y=graphHeight - graphLegendPadding / 2
              width=10
              height=10
              fill="url(#positive-value-gradient)"
            )
            text(
              x=graphPaddingLeft + 75
              y=graphHeight - graphLegendPadding / 6
              fill="url(#positive-value-gradient)"
              font-size="14"
              font-family="Helvetica, sans-serif"
            ) Good
        
          //- Horizontal lines
          each val, i in (new Array(verticalSeparatorsCount + 1).fill(null))
            - const yStep = (graphHeight - graphPaddingBottom - graphLegendPadding) / verticalSeparatorsCount
            - const y = i * yStep
            - const textx = 10
            - const texty = y + yStep + 4
            line(
              x1=graphPaddingLeft
              y1=(y)
              x2=graphWidth
              y2=(y)
              stroke="rgba(0, 0, 0, 0.08)"
            )
            text.graph-label(
              x=textx
              y=texty
              font-size="13"
              font-family="Helvetica, sans-serif"
              transform=`rotate(-30 ${textx} ${texty})`
            ) #{labels[i]}
            
          //- Left & Right Border lines
          line(
            x1=graphPaddingLeft + 1
            y1=0
            x2=graphPaddingLeft + 1
            y2=graphHeight - graphPaddingBottom - graphLegendPadding
            stroke="rgba(0, 0, 0, 0.08)"
          )
          line(
            x1=graphWidth - 1
            y1=0
            x2=graphWidth - 1
            y2=graphHeight - graphPaddingBottom - graphLegendPadding
            stroke="rgba(0, 0, 0, 0.08)"
          )
       
          //- Footer horizontal texts
          each val, i in (new Array(totalLabelsCount).fill(null).map((_, i) => i < 10 ? `0${i}` : i))
            text.graph-footer-text(
              x=graphPaddingLeft + i * labelStepX + 2
              y=graphHeight - 10 - graphLegendPadding
              fill="red"
              font-size="11"
              font-family="Helvetica, sans-serif"
            ) #{val}

          //- Footer vertical lines
          each val, i in (new Array(totalLinesCount).fill(null))
            line(
              x1=graphPaddingLeft + i * lineStepX
              y1=graphHeight - 5 - graphLegendPadding
              x2=graphPaddingLeft + i * lineStepX
              y2=graphHeight - graphLegendPadding
              stroke="black"
              strokeWidth=0.5
            )
            
          //- Dotted separator lines
          each val, i in (new Array(separatorsCount).fill(null))
            unless i === 0
              line(
                x1=i * separatorStepX - 6 + graphPaddingLeft
                y1=0
                x2=i * separatorStepX - 6 + graphPaddingLeft
                y2=graphHeight - graphLegendPadding
                stroke="rgba(0, 0, 0, 0.4)"
                stroke-dasharray=5
              )


        //-
          Render our graph bars as boxes to visualise our data.
          💡 This part is different for each graph, since each of them displays different sets of data
        g.graph-data
          each val, i in (new Array(totalLinesCount).fill(null))
            - const height = minBarHeight + Math.random() * (graphHeight - minBarHeight - graphPaddingBottom)
            - const fill = height > 100 ? `url(#positive-value-gradient)` : `url(#negative-value-gradient)`
            rect(
              x=graphPaddingLeft + i * lineStepX
              y=graphHeight - height - graphPaddingBottom
              width=lineStepX * 0.5
              height=height - graphPaddingBottom
              fill=fill
            )
View Compiled
body {
  padding: 0;
  margin: 0;
  font-family: Helvetica, sans-serif;
  line-height: 1.7;
}

h1 {
  text-align: center;
/*   padding-left: 1rem */
}

svg {
  width: 100%;
}

.graph-header {
  margin-top: 8px;
  padding: 0 12px;
  margin-bottom: 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.graph-title {
  display: flex;
  align-items: center;
/*   justify-content: center; */
}

.graph-title span {
  font-weight: 400;
}

.growth-value {
  margin: 0;
  font-weight: 400;
  font-size: 12px;
  margin-bottom: -10px;
}

.metric-value {
  font-size: 24px;
  margin: 0;
}

.comparison-value {
  margin: -10px 0 0 0;
  font-size: 12px;
  font-weight: 400;
  color: #aaa
}

.icon-holder {
  position: relative;
  margin-right: 14px;
  width: 24px;
  height: 24px;
  border-radius: 4px;
  background-color: rgba(0, 0, 0, 0.12);
}

.icon-holder:before,
.icon-holder:after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 10px;
  height: 1px;
  background: white;
}

.icon-holder:before {
  transform: translate(-50%, -50%) rotate(45deg);
}

.icon-holder:after {
  transform: translate(-50%, -50%) rotate(-45deg);
}

.wrapper {
  display: flex;
  flex-wrap: wrap;
  box-shadow: 0px -1px 1px #ccc;
}

.graph-wrapper {
  padding: 0 0.5rem;
  box-sizing: border-box;
  text-align: center;
  width: 50%;
  box-shadow: 1px 1px 1px #ccc;
}

@media (min-width: 1100px) {
  .graph-wrapper {
    width: 33.33%;
    padding: 0 2rem;
  }
}

@media (min-width: 1400px) {
  .graph-wrapper {
    width: 25%;
    padding: 0 0.75rem;
  }
}

/* @media (min-width: 1700px) {
  .graph-wrapper {
    width: 16.66667%;
    padding: 0 0.5rem;
  }
} */

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.