Tag: Ways

The Many Ways of Getting Data Into Charts

Data is available everywhere nowadays, whether it’s in a plain text file, a REST API, an online Google sheet… you name it! It’s that variety of context that makes building graphs more than simply having a database in your local project — where there is data, there is a way.

That’s pretty much what we’re going to look at in this post: making JavaScript data visualizations using data from a variety of sources.

If you want to follow along and make any of the visualizations we’re covering here, we’ll be using Chart.js so grab that and include it in your own environment:

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.js"></script>

Source 1: HTML data table

Lots and lots of websites have data tables, and why not? They are a great way to show data and that’s what they were made to do. But, if you could have a visualization powered by the data in it — and for not much more effort — wouldn’t that be even better?

With a little JavaScript, the data in a HTML table can be isolated from the HTML and prepped for a chart. Look at the following data table:

Year Items Sold Turnover ($ ) Profit ($ )
2016 10 200 89
2017 25 550 225
2018 55 1200 600
2019 120 2450 1100

It contains sales data for a business. Now, if we could graph this out, it would be both visually compelling and help users glean insights. So let’s do it!

First of all, let’s define a function for our chart. This comes straight out of the Chart.js library, so it’s worth cross-referencing this with the documentation is something seems unclear. I’ve added some comments to call out key parts.

function BuildChart(labels, values, chartTitle) {   var ctx = document.getElementById("myChart").getContext('2d');   var myChart = new Chart(ctx, {     type: 'bar',     data: {       labels: labels, // Our labels       datasets: [{         label: chartTitle, // Name the series         data: values, // Our values         backgroundColor: [ // Specify custom colors           'rgba(255, 99, 132, 0.2)',           'rgba(54, 162, 235, 0.2)',           'rgba(255, 206, 86, 0.2)',           'rgba(75, 192, 192, 0.2)',           'rgba(153, 102, 255, 0.2)',           'rgba(255, 159, 64, 0.2)'         ],         borderColor: [ // Add custom color borders           'rgba(255,99,132,1)',           'rgba(54, 162, 235, 1)',           'rgba(255, 206, 86, 1)',           'rgba(75, 192, 192, 1)',           'rgba(153, 102, 255, 1)',           'rgba(255, 159, 64, 1)'         ],         borderWidth: 1 // Specify bar border width       }]     },     options: {       responsive: true, // Instruct chart js to respond nicely.       maintainAspectRatio: false, // Add to prevent default behavior of full-width/height      }   });   return myChart; }

Now that we have a function to create a chart for the table data, let’s write out the HTML for the table and add a canvas element that Chart.js can use to inject its charting magic. This is exactly the same data from the table example above.

<table class="table" id="dataTable">   <thead>     <th>Year</th>     <th>Items Sold</th>     <th>Turnover ($ )</th>     <th>Profit ($ )</th>   </thead>   <tbody>     <tr>       <td>2016</td>       <td>10</td>       <td>200</td>       <td>89</td>     </tr>     <tr>       <td>2017</td>       <td>25</td>       <td>550</td>       <td>225</td>     </tr>     <tr>       <td>2018</td>       <td>55</td>       <td>1200</td>       <td>600</td>     </tr>     <tr>       <td>2019</td>       <td>120</td>       <td>2450</td>       <td>1100</td>     </tr>   </tbody> </table>  <div class="chart">   <canvas id="myChart"></canvas> </div>

Then, we need to parse the table into JSON with vanilla JavaScript. This is what Chart.js will use to populate the charts and graphs.

var table = document.getElementById('dataTable'); var json = []]; // First row needs to be headers  var headers =[]; for (var i = 0; i < table.rows[0].cells.length; i++) {   headers[i] = table.rows[0].cells[i].innerHTML.toLowerCase().replace(/ /gi, ''); }  // Go through cells  for (var i = 1; i < table.rows.length; i++) {   var tableRow = table.rows[i];   var rowData = {};   for (var j = 0; j < tableRow.cells.length; j++) {     rowData[headers[j]] = tableRow.cells[j].innerHTML;   }    json.push(rowData); }  console.log(json);

We threw in that last line so we can check the output in the DevTools console. Here’s what is logged into the console:

Perfect! Our table headers become variables and are mapped to the content in the table cells.

All that’s left to do is map the year labels and items sold values to array (which Chart.js requires for its data object) and then to pass the data into the chart function.

This script maps the JSON values to an array of years. We can add it directly after the previous function.

// Map JSON values back to label array var labels = json.map(function (e) {   return e.year; }); console.log(labels); // ["2016", "2017", "2018", "2019"]  // Map JSON values back to values array var values = json.map(function (e) {   return e.itemssold; }); console.log(values); // ["10", "25", "55", "120"]

Call the BuildChart function from our first snippet (including a name for the chart) and we get a beautiful visualization of the data from the HTML table.

var chart = BuildChart(labels, values, "Items Sold Over Time");

And that is it! The charts data has now been isolated and passed into the JavaScript visualization and rendered.

See the Pen
BarChart Loaded From HTML Table
by Danny Englishby (@DanEnglishby)
on CodePen.

Source 2: A real-time API

There are many, many public APIs available in the world, many of which are rich with neat and useful data. Let’s use one of them to demonstrate how real-time data can be used to populate a chart. I’m going to use the Forbes 400 Rich List API to graph out the top 10 richest people in the world. I know, cool, eh?! I always find wealth rankings interesting, and there is so much more data this API provides. But for now, we will request data to display a chart with names and real time net worth using pure JavaScript!

First off, we want to define a chart function again, this time with a few more options.

function BuildChart(labels, values, chartTitle) {   var data = {     labels: labels,     datasets: [{       label: chartTitle, // Name the series       data: values,       backgroundColor: ['rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',       ],     }],   };   var ctx = document.getElementById("myChart").getContext('2d');   var myChart = new Chart(ctx, {     type: 'horizontalBar',     data: data,     options: {       responsive: true, // Instruct chart JS to respond nicely.       maintainAspectRatio: false, // Add to prevent default behavior of full-width/height        scales: {         xAxes: [{           scaleLabel: {             display: true,             labelString: '$  Billion'           }         }],         yAxes: [{           scaleLabel: {             display: true,             labelString: 'Name'           }         }]       },     }   });   return myChart; }

Next, we’ll need that same HTML canvas element for where the chart will be rendered:

<div class="chart" style="position: relative; height:80vh; width:100vw">   <canvas id="myChart"></canvas> </div>

Now to grab some real time data. Let’s see what is returned in the console from the Forbes API call:

As you can see by the JSON returned, there is a lot of rich information that can injected into a chart. So, let’s make our rankings!

With some light JavaScript, we can request the data from the API, pick out and map the values we went to array variables, and finally pass our data in and rendering the chart.

var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function () {   if (this.readyState == 4 && this.status == 200) {     var json = JSON.parse(this.response);     // Map JSON labels  back to values array     var labels = json.map(function (e) {       return e.name;     });     // Map JSON values back to values array     var values = json.map(function (e) {       return (e.realTimeWorth / 1000); // Divide to billions in units of ten     });     BuildChart(labels, values, "Real Time Net Worth"); // Pass in data and call the chart   } }; xhttp.open("GET", "https://forbes400.herokuapp.com/api/forbes400?limit=10", false); xhttp.send();

See the Pen
Chart Loaded From RichListAPI
by Danny Englishby (@DanEnglishby)
on CodePen.

Source 3: A Google Sheet

So far, we’ve looked at data in standard HTML tables and APIs to populate charts. But what if we already have an existing Google Sheet that has a bunch of data in it? Well, we can also use that to make a chart!

First, there are some rules for accessing a Google Sheet:

  • The Google Sheet must be published
  • The Google Sheet must be public (i.e. not set to private viewing)

As long as these two points are true, we can access a Google Sheet in the form of JSON, which means, of course, we can graph it out!

Here’s a Google Sheet with some made up data that I’ve published publicly. It consists of three fields of data: MachineId, Date, and ProductsProduced.

Now, if we that the sheet’s URL, we’ll want to note everything after the last slash:

https://docs.google.com/spreadsheets/d/1ySHjH6IMN0aKImYcuVHozQ_TvS6Npt4mDxpKDtsFVFg

This is the sheet identity and what we need for the GET request we send to Google. To make a GET request for JSON, we insert that string in the URL in another URL:

https://spreadsheets.google.com/feeds/list/[ID GOES HERE]/od6/public/full?alt=json

That gives us the following URL:

https://spreadsheets.google.com/feeds/list/1ySHjH6IMN0aKImYcuVHozQ_TvS6Npt4mDxpKDtsFVFg/od6/public/full?alt=json

And here’s the response we get in the console:

The important piece is the feed.entry object array. This holds vital sheet data, which looks like this when we drill into it:

Notice the items underlined in red. The Google Sheets API has preceded each of the column names with gsx$ (e.g. gsx$ date). These are exactly how we will dissect the data from the object, using these uniquely generated names. So, knowing this, it’s time to insert the data into some isolated arrays and pass them into our chart function.

function BuildChart(labels, values, chartTitle) {   var data = {     labels: labels,     datasets: [{       label: chartTitle, // Name the series       data: values,       backgroundColor: ['rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',         'rgb(54, 162, 235)',       ],     }],   };    var ctx = document.getElementById("myChart").getContext('2d');   var myChart = new Chart(ctx, {     type: 'bar',     data: data,     options: {       responsive: true, // Instruct chart js to respond nicely.       maintainAspectRatio: false, // Add to prevent default behavior of full-width/height        scales: {         xAxes: [{           scaleLabel: {             display: true,             labelString: 'Date'           }         }],         yAxes: [{           scaleLabel: {             display: true,             labelString: 'Produced Count'           }         }]       },     }   });    return myChart; }

And, you guessed it, next is the Canvas element:

<div class="chart" style="position: relative; height:80vh; width:100vw">   <canvas id="myChart"></canvas> </div>

…then the GET request, mapping our labels and values array and finally calling the chart passing in the data.

var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() {   if (this.readyState == 4 && this.status == 200) {     var json = JSON.parse(this.response);     console.log(json);    // Map JSON labels  back to values array   var labels = json.feed.entry.map(function (e) {     return e.gsx$ date.$ t;   });          // Map JSON values back to values array   var values = json.feed.entry.map(function (e) {     return e.gsx$ productsproduced.$ t;   });    BuildChart(labels, values, "Production Data");   } }; xhttp.open("GET", "https://spreadsheets.google.com/feeds/list/1ySHjH6IMN0aKImYcuVHozQ_TvS6Npt4mDxpKDtsFVFg/od6/public/full?alt=json", false); xhttp.send();

And, boom!

See the Pen
Chart Loaded From a Google Sheet
by Danny Englishby (@DanEnglishby)
on CodePen.

What will you make with data?

You probably get the point that there is a variety of ways we can get data to populate beautiful looking charts and graphs. As long as we have some formatted numbers and a data visualization library, then we have a lot of powers at our fingertips.

Hopefully now you’re thinking of where you might have data and how to plug into a chart! We didn’t even cover all of the possibilities here. Here are a few more resources you can use now that you’ve got chart-making chops.

A few more charting libraries

A few more places to store data

The post The Many Ways of Getting Data Into Charts appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,

The Simplest Ways to Handle HTML Includes

It’s extremely surprising to me that HTML has never had any way to include other HTML files within it. Nor does there seem to be anything on the horizon that addresses it. I’m talking about straight up includes, like taking a chunk of HTML and plopping it right into another. For example the use case for much of the entire internet, an included header and footer for all pages:

... <body>    <include src="./header.html"></include>     Content     <include src="./footer.html"></include> </body> ...

That’s not real, by the way. I just wish it was.

People have been looking to other languages to solve this problem for them forever. It’s HTML preprocessing, in a sense. Long before we were preprocessing our CSS, we were using tools to manipulate our HTML. And we still are, because the idea of includes is useful on pretty much every website in the world.

Use PHP

Can you use PHP instead?

... <body>    <?php include "./header.html" ?>     Content     <?php include "./footer.html" ?> </body> ...

This will perform the include at the server level, making the request for it happen at the file system level on the server, so it should be far quicker than a client-side solution.

Use Gulp

What’s even faster than a server-side include? If the include is preprocessed before it’s even on the server. Gulp has a variety of processors that can do this. One is gulp-file-include.

That would look like this:

... <body>    @@include('./header.html')     Content     @@include('./footer.html') </body> ...

And you’d process it like:

var fileinclude = require('gulp-file-include'),   gulp = require('gulp');   gulp.task('fileinclude', function() {   gulp.src(['index.html'])     .pipe(fileinclude({       prefix: '@@',       basepath: '@file'     }))     .pipe(gulp.dest('./')); });

Looks like this particular plugin has fancy features where you can pass in variables to the includes, making it possible to make little data-driven components.

Use Grunt

This is what the grunt-bake plugin does. You’d configure Grunt to process your HTML:

grunt.initConfig({     bake: {         your_target: {             files: {                 "dist/index.html": "app/index.html",             }         }     } });

Then your HTML can use this special syntax for includes:

... <body>    <!--(bake header.html)-->     Content     <!--(bake footer.html)--> </body> ...

Use Handlebars

Handlebars has partials.

You register them:

Handlebars.registerPartial('myPartial', '{{name}}')

Then use them:

{{> myPartial }}

There is also fancy features of this that allow for evaluation and passing data. You’ll still need a processor to run it, probably something like gulp-handlebars.

Speaking of templating languages which make use of curly braces… Mustache has them, too.

Use Pug

Pug is an HTML preprocessor that has a whole new syntax for HTML that is a bit more terse. It’s got includes though.

... body    include ./header.html"     p Content     include ./footer.html"     ...

Then you run it with something like gulp-pug.

Use Nunjucks

I love me some Nunjucks! Nunjucks has includes. You’d do it like this:

... <body>    {% include "./header.html" %}     Content     {% include "./footer.html" %} </body> ...

If you put that in a file called index.njk, you could process it with a simple Node script into index.html like this:

const nunjucks = require("nunjucks"); const fs = require("fs");  fs.writeFile("index.html", nunjucks.render("index.njk"), function(err, data) {   if (err) console.log(err);   console.log("Compiled the Nunjucks, captain."); });

Or process it with something like gulp-nunjucks.

11ty has Nunjucks built-in, along with many of the other mentioned so far. Might be good for you if you’re actually building a little site.

Use Ajax

Say you had…

<body>      <header></header>      Content.      <footer></footer>  </body>

You could fetch the contents for the header and footer from respective files and dump the contents in.

fetch("./header.html")   .then(response => {     return response.text()   })   .then(data => {     document.querySelector("header").innerHTML = data;   });  fetch("./footer.html")   .then(response => {     return response.text()   })   .then(data => {     document.querySelector("footer").innerHTML = data;   });

Speaking of JavaScript… If you’re building your site using a JavaScript framework of just about any kind, building through components is kind of the main deal there and breaking parts you want to include in other files should be no problem. Some kind of import Header from "./header.js"; and <Header /> is the territory you’d be in in React land.

Use iframes

You could do this:

<body>      <iframe src="./header.html"></iframe>      Content.      <iframe src="./footer.html"></iframe>    </body>

But the content in those iframes does not share the same DOM, so it’s a bit weird, not to mention slow and awkward to style (since iframes don’t know the heights of their contents).

Scott Jehl documented a cool idea though: You can have the iframe inject the content of itself onto the parent page then remove itself.

<body>      <iframe src="header.html" onload="this.before((this.contentDocument.body||this.contentDocument).children[0]);this.remove()"></iframe>      Content.      <iframe src="footer.html" onload="this.before((this.contentDocument.body||this.contentDocument).children[0]);this.remove()"></iframe>    </body>

Use Jekyll

Jekyll is a Ruby-based static site generator with includes. You keep your includes in the /_includes/ folder, then:

<body>   {% include header.html %}      Content.    {% include footer.html %} </body>

Jekyll is a big one, so I’m calling it out here, but there are a ton of static site generators and I’d wager any of them can do includes.

Use Sergey

OK, I’ll call out one more SSG because it’s new and super focused. Sergey has a web components style format:

<body>   <sergey-import src="header" />    Content.    <sergey-import src="footer" /> </body>

You’d name the files header.html and footer.html and put them in /includes/ and then it’ll make a build with the includes processed when you run the npm script it has you do.

Use Apache SSI

Apache, a super duper common web server, can do includes. You do it like this:

<body> 		   <!--#include file="./header.html" -->      Content      <!--#include file="./footer.html" -->    </body>

But you need the right Apache configuration to allow stuff. I tried my best to get a working demo going but didn’t have much luck.

I tried using .htaccess within a folder on an Apache server and flipping on what I thought was the right stuff:

Options +Includes  AddType text/html .html AddOutputFilter INCLUDES .html

I’m sure there is some way to get it working though, and if you do, it’s kinda neat that it needs zero other dependencies.

Use CodeKit

Mac only, but CodeKit has a special language called Kit it processes where 90% of the point of it is HTML includes. It uses special HTML comments:

... <body>    <!-- @import "./header.html" -->     Content     <!-- @import "./footer.html" --> </body> ...

Use Dreamweaver

Lol jk. But it really is a thing. DWTs, baby.

Holy Crap

That’s a lot of ways, isn’t it?

Like I said at the top, it’s very surprising to me that HTML itself hasn’t addressed this directly. Not that I think it would be a great idea for performance to have <include> statements that trigger network requests all over our code, but it seems in-line with the platform. Using ES6 imports directly without bundling isn’t a great idea always either, but we have them. @importing CSS within CSS isn’t a great idea always, but we have it. If the platform had a native syntax, perhaps other tooling would key off that, much like JavaScript bundlers support the ES6 import format.

The post The Simplest Ways to Handle HTML Includes appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Oh, the Many Ways to Make Triangular Breadcrumb Ribbons!

Oh, the Many Ways to Make Triangular Breadcrumb Ribbons

Let’s have a look at how we can create a row of links that sorta run into each other with a chevron-like shape and notch on each block like you might see in a hierarchical breadcrumb navigation.

You’ve probably seen this pattern a lot. It comes up often in things like multi-step forms and site breadcrumbs. For our purposes we’re going to call these “ribbons” so we know what we’re referring to as we go.

Like a lot of things on the web, we can make ribbons like these in many ways! I’ve created a demo page that brings a variety of them together, like using CSS triangles, SVG backgrounds, and the CSS clip-path property.

Starting with the HTML structure

For each demo, the HTML structure will largely be the same where we have a <nav> that acts as the parent element and then links inside it as the children.

<nav class="ribbon ribbon--modifier" role="navigation" aria-label="breadcrumbs">   <a class="ribbon__element" href="https://www.silvestar.codes/">Home</a>   <a class="ribbon__element" href="https://www.silvestar.codes/categories/articles/">Blog</a>   <a class="ribbon__element" href="https://www.silvestar.codes/articles/building-an-animated-sticky-header-with-custom-offset/" aria-current="page">Post</a> </nav>

Note that these elements should be accessible, according to A11y Style Guide website. It’s a good rule to build components with accessibility in mind and introducing accessibility at the very start is the best way to prevent the classic “I forgot to make it accessible” situation.

Let’s create some baseline styles

When it comes to things like this, we want to make sure the sizing of the elements is done right. For this purpose, we are going to define the font size of the .ribbon (that’s what we’re going to call these things) wrapper element and then use em units on the child element which are the links themselves.

/* Define font size of the wrapper element */  .ribbon {   font-size: 15px; }  /* Use ems to define the size of the ribbon element */  .ribbon__element {   font-size: 1.5em;   letter-spacing: 0.01em;   line-height: 1.333em;   padding: 0.667em 0.667em 0.667em 1.333em; }

This particular technique would be beneficial for defining the size of the triangle shape for each ribbon because we would use the same sizes to calculate triangle. And since we are using em units to calculate the ribbon element size, we could resize all elements by redefining the font-size on the wrapper element.

Let’s use CSS Grid for the layout because, well, we can. We could do this in a way that offers deeper browser support, but we’ll leave that up to you based on your support requirements.

We are going to define four columns:

  • Three for ribbon elements
  • One to fix spacing issues. As it is, the right arrow shape would be placed outside of the ribbon component and that could mess up the original layout.
/* The wrapper element   * We're using CSS Grid, but ensure that meets your browser support requirements.  * Assuming the use of autoprefixer for vendor prefixes and properties.  */    .ribbon {   display: grid;   grid-gap: 1px;   grid-template-columns: repeat(auto-fill, 1fr) 1em; /* Auto-fill the three ribbon elements plus one narrow column to fix the sizing issues */ }

If you prefer to avoid stretching the ribbon elements, the grid could be defined differently. For example, we could use max-content to adjust columns by content size. (Note, however, that max-content is not very well supported yet in some key browsers.)

/* Make ribbon columns adjust to the maximum content size */ .ribbon--auto {   grid-template-columns: repeat(3, max-content) 1em; }

I am sure there are many different ways we could have gone about the layout. I like this one because it defines the exact gap between ribbon elements without complicated calculations.

Accessibility is not only adding aria attributes. It also includes color contrast and readability, as well as adding hover and focus states. If you don’t like outline style, you could use other CSS properties, like box-shadow, for example.

/* Use current link color, but add underline on hover  */ .ribbon__element:hover,  .ribbon__element:active {   color: inherit;   text-decoration: underline; }  /* Clear default outline style and use inset box shadow for focus state */ .ribbon__element:focus {   box-shadow: inset 0 -3px 0 0 #343435;   outline: none; }

Creating the unique triangular shape

We have more than one option when it comes down to defining the triangle at the end of each ribbon. We could:

  1. We could create a triangle using borders with pseudo-elements
  2. We could use an SVG background image on pseudo-elements
  3. We could use inline SVG images
  4. We could create a clip-path using the polygon() function

Let’s dig into each one.

Option 1: The border approach

First, we should set the element’s width and height to zero so it doesn’t get in the way of the pseudo-elements we’re using to draw the triangle with borders. Then we should draw the triangle using borders, specifically by defining a solid left border that matches the color of the background to make it blend in with the rest of the ribbon. From there, let’s define top and bottom borders and make them transparent. The trick here is to calculate the size of the border.

Our ribbon element has a content size of the line-height value plus the top and bottom paddings:

0.333em + 0.667em + 0.667em = 2.667em

That means our top and bottom borders should be half that size. The only thing left to do is to position elements absolutely to the correct side of the component.

/* The left arrow */ .ribbon--alpha .ribbon__element:before {   /* Make the content size zero */   content: '';     height: 0;     width: 0;    /* Use borders to make the pseudo element fit the ribbon size */   border-bottom: 1.333em solid transparent;   border-left: 0.667em solid #fff;   border-top: 1.333em solid transparent;    /* Position the element absolutely on the left side of the ribbon element */   position: absolute;   top: 0;     bottom: 0;     left: 0; }  /* The right arrow */ .ribbon--alpha .ribbon__element:after {   /* Make the content size zero */   content: '';     height: 0;     width: 0;    /* Use borders to make the pseudo-element fit the ribbon size */   border-bottom: 1.333em solid transparent;   border-left: 0.667em solid;   border-top: 1.333em solid transparent;    /* Position the element absolutely on the right side of the ribbon element and push it outside */   position: absolute;   top: 0;   right: 0;   bottom: 0;   -webkit-transform: translateX(0.667em);   transform: translateX(0.667em); }

Since the right triangle should match the background color of the ribbon, let’s remember to add the correct border color for each ribbon’s pseudo-element.

/* The right arrow of the first element */ .ribbon--alpha .ribbon__element:nth-child(1):after {   border-left-color: #11d295; }  /* The right arrow of the second element */ .ribbon--alpha .ribbon__element:nth-child(2):after {   border-left-color: #ef3675; }  /* The right arrow of the third element */ .ribbon--alpha .ribbon__element:nth-child(3):after {   border-left-color: #4cd4e9; }

And there we go!

See the Pen
CSS Grid Ribbon – Alpha
by Silvestar Bistrović (@CiTA)
on CodePen.

Option 2: The background image approach

We can also create a triangle using a background image. This requires creating an image that matches the design, which is a little cumbersome, but still totally possible. We are going to use SVG here since it’s smooth at any resolution.

Unlike the border triangle approach, we want to match the height of our pseudo-element with the height of the ribbon element, or 100%. The width of the component should match the left border width of the border triangle, which is 0.666666em in our case. Then we should use a white triangle for the background image on the triangle of the left side, and then use triangle images with color for the triangles on the right side. Again, we are using absolute positioning to place our triangles to the correct side of the ribbon element.

/* The left arrow */ .ribbon--beta .ribbon__element:before {   /* Define the arrow size */   content: '';     height: 100%;     width: 0.666666em;      /* Define the background image that matches the background color */   background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjQwIiB2aWV3Qm94PSIwIDAgMTAgNDAiIHdpZHRoPSIxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBmaWxsPSIjZmZmIj48cGF0aCBkPSJtNSAxNSAyMCAxMGgtNDB6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0ibWF0cml4KDAgLTEgLTEgMCAyNSAyNSkiLz48L3N2Zz4=);   background-position: center left;   background-repeat: no-repeat;   background-size: 100%;      /* Position the element absolutely on the left side of the ribbon element */   position: absolute;   bottom: 0;   top: 0;   left: 0; }  /* The right arrow */ .ribbon--beta .ribbon__element:after {   /* Define the arrow size */   content: '';     height: 100%;   width: 0.667em;    /* Define the background image attributes */   background-position: center left;   background-repeat: no-repeat;   background-size: 100%;    /* Position the element absolutely on the right side of the ribbon element and push it outside */   position: absolute;   top: 0;   right: 0;   bottom: 0;   -webkit-transform: translateX(0.667em);   transform: translateX(0.667em); }  /* Define the background image that matches the background color of the first element */ .ribbon--beta .ribbon__element:nth-child(1):after {   background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjQwIiB2aWV3Qm94PSIwIDAgMTAgNDAiIHdpZHRoPSIxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNSAxNSAyMCAxMGgtNDB6IiBmaWxsPSIjMTFkMjk1IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0ibWF0cml4KDAgLTEgLTEgMCAyNSAyNSkiLz48L3N2Zz4=); }  /* Define the background image that matches the background color of the second element */ .ribbon--beta .ribbon__element:nth-child(2):after {   background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjQwIiB2aWV3Qm94PSIwIDAgMTAgNDAiIHdpZHRoPSIxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNSAxNSAyMCAxMGgtNDB6IiBmaWxsPSIjZWYzNjc1IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0ibWF0cml4KDAgLTEgLTEgMCAyNSAyNSkiLz48L3N2Zz4=); }  /* Define the background image that matches the background color of the third element */ .ribbon--beta .ribbon__element:nth-child(3):after {   background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjQwIiB2aWV3Qm94PSIwIDAgMTAgNDAiIHdpZHRoPSIxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNSAxNSAyMCAxMGgtNDB6IiBmaWxsPSIjNGNkNGU5IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0ibWF0cml4KDAgLTEgLTEgMCAyNSAyNSkiLz48L3N2Zz4=); }

There we go!

See the Pen
CSS Grid Ribbon – Beta
by Silvestar Bistrović (@CiTA)
on CodePen.

Option 3: The inline SVG approach

Instead of loading a different SVG triangle for each background image, we could use inline SVG directly in the HTML.

This particular approach allows us to control the fill color of each SVG arrow with CSS. The arrow size is calculated by the ribbon size. Once again, we are using the em units to define the size and arrows are absolutely positioned, like the other approaches we’ve seen so far.

/* Position arrows absolutely and set the correct size */ .ribbon--gamma .ribbon__element svg {   height: 2.667em;   position: absolute;   top: 0;   width: 0.667em; }  /* The left arrow */ .ribbon--gamma .ribbon__element svg:first-child {   fill: #fff; /* Define the background image that matches the background color */   left: 0; /* Stick left arrows to the left side of the ribbon element */ }  /* The right arrow */ .ribbon--gamma .ribbon__element svg:last-child {   left: 100%; /* Push right arrows outside of the ribbon element */ }  /* Define the fill color that matches the background color of the first element */ .ribbon--gamma .ribbon__element:nth-child(1) svg:last-child {   fill: #11d295; }  /* Define the fill color that matches the background color of the second element */ .ribbon--gamma .ribbon__element:nth-child(2) svg:last-child {   fill: #ef3675; }  /* Define the fill color that matches the background color of the third element */ .ribbon--gamma .ribbon__element:nth-child(3) svg:last-child {   fill: #4cd4e9; }

See the Pen
CSS Grid Ribbon – Gamma
by Silvestar Bistrović (@CiTA)
on CodePen.

Option 4: The clip-path approach

We can create the ribbon triangles with a polygon that masks the background. Firefox’s Shape Editor is a fantastic tool to draw shapes directly in the browser with a GUI, as is Clippy.

Since polygons must be created using percentages, we should use our best judgment to match the size of border triangles. Also, note that percentage-based polygons might look a little funny on some viewports, especially when element sizes are adapting to its surroundings, like wrapper elements. Consider redefining polygons for different viewports.

.ribbon--delta .ribbon__element {   clip-path: polygon(95% 0, 100% 50%, 95% 100%, 0% 100%, 5% 50%, 0% 0%); }

Since we defined our wrapper element using CSS Grid, we should expand the ribbon elements but leave the last one at the size of the polygon triangle, which is 5% in our case. The last ribbon element should be wider by the size of the border triangle width to match the first two examples.

/* Make all ribbon elements (except the last one) wider by the size of the polygon triangle */ .ribbon--delta .ribbon__element:not(:last-child) {   width: 105%; }  /* Make the last ribbon element wider by the size of the border triangle */ .ribbon--delta .ribbon__element:last-child {   width: calc(100% + .667em); }

See the Pen
CSS Grid Ribbon – Delta
by Silvestar Bistrović (@CiTA)
on CodePen.

Variations on these options

Now that we’ve learned how to create the breadcrumb ribbon a few different ways, we could play around with it, like adding shadows or gradients and different sizes.

Adding a shadow

We could add the shadow on our ribbon elements. Make sure to avoid the shadow on the left or right side of the ribbon element.

/* Add shadow under each ribbon element */ .ribbon--shadow .ribbon__element {   box-shadow: 1px 3px 3px -3px black; }

See the Pen
CSS Grid Ribbon – Shadow
by Silvestar Bistrović (@CiTA)
on CodePen.

Using gradients for color

We could add gradients to our ribbon element. Be sure to match the color of the right triangle when doing so. Also, make sure to comply with contrast accessibility.

For example, if we are going to use the border approach or background image approach, we should use mostly horizontal (i.e. left-to-right) gradients (with the exceptions of some carefully calculated angled gradients). If we are using the clip-path approach, we could use any gradient version we wish.

/* Add gradient to the first ribbon element */ .ribbon--gradient .ribbon__element:nth-child(1) {   background-image: linear-gradient(to right, #11ced2, #11d295); }  /* Add gradient to the second ribbon element */ .ribbon--gradient .ribbon__element:nth-child(2) {   background-image: linear-gradient(to right, #ef36b2, #ef3675); }  /* Add gradient to the third ribbon element */ .ribbon--gradient .ribbon__element:nth-child(3) {   background-image: linear-gradient(to right, #4c9fe9, #4cd4e9); }

See the Pen
CSS Grid Ribbon – Gradient
by Silvestar Bistrović (@CiTA)
on CodePen.

Working with size variations

Since the size of our ribbon elements depends on the font size of the wrapper element, defining different sizes is pretty straightforward.

/* Small ribbons */ .ribbon--small {   font-size: 10px; }  /* Big ribbons */ .ribbon--big {   font-size: 20px; }

Here we go with a smaller set of ribbons:

See the Pen
CSS Grid Ribbon – Small
by Silvestar Bistrović (@CiTA)
on CodePen.

And here’s a nice set of chunky ribbons:

See the Pen
CSS Grid Ribbon – Big
by Silvestar Bistrović (@CiTA)
on CodePen.

Combining all the things!

We can also combine different modifier classes to achieve an even more styling. For example, let’s use gradient and shadow modifiers together:

See the Pen
CSS Grid Ribbon – Shadow Gradient
by Silvestar Bistrović (@CiTA)
on CodePen.

Any other angles to consider?

Making custom elements using different CSS techniques is a great way how each one of us could improve or refresh our knowledge. Before starting, it’s worth investing some thought into the maintainability and modularity of the component being built. A consistent naming convention, like BEM, is certainly helpful that. Accessibility is also a big deal, so starting with it in mind and documenting accessibility features along the way will serve you well.

We looked at four different approaches for drawing ribbon triangles. Have you used a different approach or know of one we haven’t considered here? Let me know in the comments!

The post Oh, the Many Ways to Make Triangular Breadcrumb Ribbons! appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

CSS Triangles, Multiple Ways

I like Adam Laki’s Quick Tip: CSS Triangles because it covers that ubiquitous fact about front-end techniques: there are always many ways to do the same thing. In this case, drawing a triangle can be done:

  • with border and a collapsed element
  • with clip-path: polygon()
  • with transform: rotate() and overflow: hidden
  • with glyphs like ▼

I’d say that the way I’ve typically done triangles the most over the years is with the border trick, but I think my favorite way now is using clip-path. Code like this is fairly clear, understandable, and maintainable to me: clip-path: polygon(50% 0, 0 100%, 100% 100%); Brain: Middle top! Bottom right! Bottom left! Triangle!

My 2nd Place method goes to an option that didn’t make Adam’s list: inline <svg>! This kind of thing is nearly just as brain-friendly: <polygon points="0,0 100,0 50,100"/>.

Direct Link to ArticlePermalink

The post CSS Triangles, Multiple Ways appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

The Smart Ways to Correct Mistakes in Git

The world of software development offers an infinite amount of ways to mess up: deleting the wrong things, coding into dead ends, littering commit messages with typos, are a mere few of the plentitude.
​​
​​Fortunately, however, we have a wonderful safety net under our feet in the form of Git when we’re working with version control. Not that you and I need a safety net, of course, because we never make mistakes, right? Sure, sure. But for the benefit of everyone else, let’s take a tour of some of the “undo” tools in Git that can save us from ourselves.
​​
​​
​​
​​

Fixing the last commit

​​
​​Messing up a commit is all too easy. Classic case in point: making a typo in a commit message. Another? Forgetting to add a change to the staging area. And in many cases, we instantly realize our mistake — right after hitting the Enter key, naturally.
​​
​​Luckily, Git makes it ridiculously easy to fix the very last commit. Let’s say we had just hit Enter on the following command:

​​

git commit -m "Massage full of typohs"

​​
​​And (as if this orthographic mess wasn’t bad enough) let’s say we also forgot to add another changed file to the staging area. We can correct both of our mistakes with the following two commands:
​​

git add forgotten-changes.js ​​git commit --amend -m "A sensible message"

​​
​​The magic ingredient is the --amend​ flag: when using it on a commit, Git will correct the very last commit — with any staged changes and the new message.
​​
​​A short word of warning, though: only use --amend​ on commits that haven’t been pushed to a remote repository, yet. The reason is that Git replaces the original, bad commit with the amended version. Afterwards, it looks as if the original commit never happened. Yeah, that’s good for concealing mistakes, but only if we haven’t already published this mistake on the remote server.
​​​​
​​

Undoing local changes

​​
​​Everyone’s had days like this: spend all morning hacking away, only to admit to yourself that the last few hours were a waste of time. Gotta start over and undo much (or all) of that work.
​​
​​But this is one of the reasons for using Git in the first place — to be able to try out things without the fear that we might break something.
​​
​​Let’s take stock in an example situation:
​​

git status ​​  modified: about.html ​​  deleted:  imprint.html ​​  modified: index.html

​​
​​Now, let’s assume that this is one of the wasted hacking days described above. We ought to have kept our hands off of about.html and not deleted imprint.html. What we now want is to discard our current changes in these files — while keeping the brilliant work done in index.html. ​​The git checkout​ command can help in this case. Instead, we’ve gotta get more specific with which files to check out, like this:

​​

git checkout HEAD about.html imprint.html

​​This command restores both about.html and imprint.html to their last committed states. Phew, we got away from a black eye!
​​
​​We could take this one step further and discard specific individual lines in a changed file instead of tossing out the entire thing! I’ll admit, it’s rather complicated to make it happen on the command line, but using a desktop Git client like Tower is a great way to go about it:

​​
​​For those really bad days, we might want to bring out the big guns in the form of:
​​
​​

git reset --hard HEAD

​​
​​While we only restored specific files with checkout​, this command resets our whole working copy. In other words, reset​ restores the complete project at its last committed state. ​​Similar to --amend​, there’s something to keep in mind when using checkout​ and reset​: discarding local changes with these commands cannot be undone! They have never been committed to the repository, so it’s only logical that they cannot be restored. Better be sure that you really want to get rid of them because there’s no turning back!
​​
​​

Undoing and reverting an older commit

​​
​​In many cases, we only realize a mistake much later, after it has long been committed to the repository.

​​How can we get rid of that one bad commit? Well, the answer is that we shouldn’t… at least in most cases. Even when “undoing” things, Git normally doesn’t actually delete data. It corrects it by adding new data. Let’s see how this works using our “bad guy” example:
​​
​​

git revert 2b504bee

​​
​​By using git revert​ on that bad commit, we haven’t deleted anything. Quite the contrary:

​​Git automatically created a new commit with changes that reverts the effects of the “bad” commit. So, really, if we started with three commits and were trying to correct the middle one, now we have four total commits, with a new one added that corrects the one we targeted with revert​.
​​​​
​​

Restoring a previous version of a project

​​
​​A different use case is when we want to restore a previous version of our project. Instead of simply undoing or reverting a specific revision somewhere in our commit history, we might really want to turn back time and return to a specific revision.
​​
​​In the following example scenario, we would declare all the commits that came after “C2” as unwanted. What we want is to return to the state of commit “C2” and forget everything that came after it in the process:

​​The command that’s necessary is already (at least partly) familiar to you based on what we’ve already covered:
​​
​​

git reset --hard 2b504bee

​​
​​This tells git reset​ the SHA-1 hash of the commit we want to return to. Commits C3 and C4 then disappear from the project’s history.
​​
​​If you’re working in a Git client, like Tower, both git revert​ and git reset are available from the contextual menu of a commit item:

​​

​​Deleting commits, restoring deleted branches, dealing with conflicts, etc. etc. etc.

​​
​​Of course, there are many other ways to mess up things in a software project. But luckily, Git also offers many more tools for undoing the mess.
​​
​​Have a look at the “First Aid Kit for Git” project that I and other folks on the Tower team have created if you want to learn more about the scenarios we covered in this post, or about other topics, like how to move commits between branches, delete old commits, restore deleted branches or gracefully deal with merge conflicts. It’s a totally free guide that includes 17 videos and a handy cheat sheet you can download and keep next to your machine.

​​In the meantime, happy undoing!

The post The Smart Ways to Correct Mistakes in Git appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

The Many Ways to Change an SVG Fill on Hover (and When to Use Them)

SVG is a great format for icons. Vector formats look crisp and razor sharp, no matter the size or device — and we get tons of design control when using them inline.

SVG also gives us another powerful feature: the ability to manipulate their properties with CSS. As a result, we can make quick and simple interactions where it used to take crafty CSS tricks or swapping out entire image files.

Those interactions include changing color on hover states. It sounds like such a straightforward thing here in 2019, but there are actually a few totally valid ways to go about it — which only demonstrates the awesome powers of SVG more.

First off, let’s begin with a little abbreviated SVG markup:

<svg class="icon">   <path .../> </svg>

Target the .icon class in CSS and set the SVG fill property on the hover state to swap colors.

.icon:hover {   fill: #DA4567; }

This is by far the easiest way to apply a colored hover state to an SVG. Three lines of code!

SVGs can also be referenced using an <img> tag or as a background image. This allows the images to be cached and we can avoid bloating your HTML with chunks of SVG code. But the downside is a big one: we no longer have the ability to manipulate those properties using CSS. Whenever I come across non-inline icons, my first port of call is to inline them, but sometimes that’s not an option.

I was recently working on a project where the social icons were a component in a pattern library that everyone was happy with. In this case, the icons were being referenced from an <img> element. I was tasked with applying colored :focus and :hover styles, without adjusting the markup.

So, how do you go about adding a colored hover effect to an icon if it’s not an inline SVG?

CSS Filters

CSS filters allow us to apply a whole bunch of cool, Photoshop-esque effects right in the browser. Filters are applied to the element after the browser renders layout and initial paint, which means they fall back gracefully. They apply to the whole element, including children. Think of a filter as a lens laid over the top of the element it’s applied to.

These are the CSS filters available to us:

  • brightness(<number-percentage>);
  • contrast(<number-percentage>);
  • grayscale(<number-percentage>);
  • invert(<number-percentage>);
  • opacity(<number-percentage>);
  • saturate(<number-percentage>);
  • sepia(<number-percentage>);
  • hue-rotate(<angle>);
  • blur(<length>);
  • drop-shadow(<length><color>);

All filters take a value which can be changed to adjust the effect. In most cases, this value can be expressed in either a decimal or percent units (e.g. brightness(0.5) or brightness(50%)).

Straight out of the box, there’s no CSS filter that allows us to add our own specific color.
We have hue-rotate(), but that only adjusts an existing color; it doesn’t add a color, which is no good since we’re starting with a monochromatic icon.

The game-changing bit about CSS filters is that we don’t have to use them in isolation. Multiple filters can be applied to an element by space-separating the filter functions like this:

.icon:hover {   filter: grayscale(100%) sepia(100%); }

If one of the filter functions doesn’t exist, or has an incorrect value, the whole list is ignored and no filter will be applied to the element.

When applying multiple filter functions to an element, their order is important and will affect the final output. Each filter function will be applied to the result of the previous operation.

So, in order to colorize our icons, we have to find the right combination.

To make use of hue-rotate(), we need to start off with a colored icon. The sepia() filter is the only filter function that allows us to add a color, giving the filtered element a yellow-brown-y tinge, like an old photo.

The output color is dependent on the starting tonal value:

In order to add enough color with sepia(), we first need to use invert() to convert our icon to a medium grey:

.icon:hover {   filter: invert(0.5) }

We can then add the yellow/brown tone with sepia():

.icon:hover {   filter: invert(0.5) sepia(1); }

…then change the hue with hue-rotate():

.icon:hover {   filter: invert(0.5) sepia(1) hue-rotate(200deg);                                   }

Once we have the rough color we want, we can tweak it with saturation() and brightness():

.icon:hover {   filter:      invert(0.5)     sepia(1)     hue-rotate(200deg)     saturate(4)     brightness(1); }

I’ve made a little tool for this to make your life a little easier, as this is a pretty confusing process to guesstimate.

See the Pen CSS filter example by Cassie Evans (@cassie-codes)
on CodePen.

Even with the tool, it’s still a little fiddly, not supported by Internet Explorer, and most importantly, you’re unable to specify a precise color.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Opera Firefox IE Edge Safari
18* 15* 35 No 18 6*

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
6.0-6.1* 46 No 4.4* 71 64

So, what do we do if we need a specific hex code?

SVG Filters

If we need more precise control (and better browser support) than CSS filters can offer, then it’s time to turn to SVG.

Filters originally came from SVG. In fact, under the hood, CSS filters are just shortcuts to SVG filters with a particular set of values baked in.

Unlike CSS, the filter isn’t predefined for us, so we have to create it. How do we do this?

This is the syntax to define a filter:

<svg xmlns="<http://www.w3.org/2000/svg>" version="1.1">   <defs>     <filter id="id-of-your-filter">       ...                        ...     </filter>     ...   </defs> </svg>

Filters are defined by a <filter> element, which goes inside the <defs> section of an SVG.

SVG filters can be applied to SVG content within the same SVG document. Or, the filter can be referenced and applied to HTML content elsewhere.

To apply an SVG filter to HTML content, we reference it the same way as a CSS filter: by using the url() filter function. The URL points to the ID of the SVG filter.

.icon:hover {   filter: url('#id-of-your-filter'); }

The SVG filter can be placed inline in the document or the filter function can reference an external SVG. I prefer the latter route as it allows me to keep my SVG filters tidied away in an assets folder.

.icon:hover {   filter: url('assets/your-SVG.svg#id-of-your-filter'); }

Back to the <filter> element itself.

<filter id="id-of-your-filter">   ...                ... </filter>

Right now, this filter is empty and won’t do anything as we haven’t defined a filter primitive. Filter primitives are what create the filter effects. There are a number of filter primitives available to us, including:

  • [<feBlend>]
  • [<feColorMatrix>]
  • [<feComponentTransfer>]
  • [<feComposite>]
  • [<feConvolveMatrix>]
  • [<feDiffuseLighting>]
  • [<feDisplacementMap>]
  • [<feDropShadow>]
  • [<feFlood>]
  • [<feGaussianBlur>]
  • [<feImage>]
  • [<feMerge>]
  • [<feMorphology>]
  • [<feOffset>]
  • [<feSpecularLighting>]
  • [<feTile>]
  • [<feTurbulence>]

Just like with CSS filters, we can use them on their own or include multiple filter primitives in the <filter> tag for more interesting effects. If more than one filter primitive is used, then each operation will build on top of the previous one.

For our purposes we’re just going to use feColorMatrix, but if you want to know more about SVG filters, you can check out the specs on MDN or this (in progress, at the time of this writing) article series that Sara Soueidan has kicked off.

feColourMatrix allows us to change color values on a per-channel basis, much like channel mixing in Photoshop.

This is what the syntax looks like:

<svg xmlns="<http://www.w3.org/2000/svg>" version="1.1">   <defs>     <filter id="id-of-your-filter">       <feColorMatrix         color-interpolation-filters="sRGB"         type="matrix"         values="1 0 0 0 0                 0 1 0 0 0                 0 0 1 0 0                 0 0 0 1 0 "/>     </filter>     ...   </defs> </svg>

Let’s have a closer look at the color matrix values.

The first four columns represent the red, green and blue channels of color and the alpha (opacity) value. The rows contain the red, green, blue and alpha values in those channels.

The M column is a multiplier — we don’t need to change any of these values for our purposes here. The values for each color channel are represented as floating point numbers in the range 0 to 1.

We could write these values as a CSS RGBA color declaration like this:

rgba(255, 255, 255, 1)

The values for each color channel (red, green and blue) are stored as integers in the range 0 to 255. In computers, this is the range that one 8-bit byte can offer.

By dividing these color channel values by 255, the values can be represented as a floating point number which we can use in the feColorMatrix.

And, by doing this, we can create a color filter for any color with an RGB value!

Like teal, for example:

rgba(0, 128, 128, 1). 128%255=0.50

See the Pen
SVG filter – teal hover
by Cassie Evans (@cassie-codes)
on CodePen.

This SVG filter will only impart color to icons with a white fill, so If we have an icon with a black fill, we can use invert() to convert it to white before applying the SVG filter.

.icon:hover {   filter: invert(100%) url('assets/your-SVG.svg#id-of-your-filter'); }

If we just have a hex code, the math is a little trickier, although there are plenty of hex-to-RGBA converters out there. To help out, I’ve made a HEX to feColorMatrix converter.

See the Pen
HEX to feColorMatrix converterr
by Cassie Evans (@cassie-codes)
on CodePen.

Have a play around, and happy filtering!

The post The Many Ways to Change an SVG Fill on Hover (and When to Use Them) appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Need to Test API Endpoints? Two Quick Ways to Do It.

Here’s a possibility! Perhaps you are testing your JavaScript with a framework like Jasmine. That’s nice because you can write lots of tests to cover your application, get a nice little UI to see the output, and even integrate it with build and deploy tools to make your ongoing development work safer.

Now, perhaps there is this zany developer on your team who keeps changing API endpoints on you — quite literally breaking things in the process. You decide to write a test that hits those endpoints and makes sure you’re getting back from it what you expect. Straightforward enough. The only slightly tricky part is that API requests are async. To really test it, the test needs to have some way to wait for the results before testing the expectations.

That can be handled in Jasmine through a beforeEach(), which can wait to complete until you call a done() function. Here’s the whole thing:

See the Pen
Test Endpoint with Jasmine
by Chris Coyier (@chriscoyier)
on CodePen.

Here’s largely the same thing but with Mocha/Chai:

See the Pen
Test Endpoint with Mocha/Chai
by Chris Coyier (@chriscoyier)
on CodePen.

The post Need to Test API Endpoints? Two Quick Ways to Do It. appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Two Ways to Build a Site That Seem Super Different But Weirdly Aren’t That Different

Here are two ways to build a site (abstractly) that feel diametrically opposed to me:

  1. Build a site as an SPA (Single Page App). The page loads a skeleton HTML page that executes JavaScript as quickly as it can. The JavaScript calls an API to get data, and then the page renders content. Navigation of the site is more API calls to get the data it needs and re-rendering.
  2. Build a site as statically-generated. A build process runs in which the entire site is built out as static HTML files with all the content baked into them. JavaScript isn’t required at all for the site to work.

That feels just about as different as can be. But weirdly, they kinda aren’t:

  1. They are both JAMstack. They can be hosted statically as neither of them needs backend languages running on the server they are hosted on.
  2. They are both building content based on an API of data. It’s more obvious in the first one, but you can think of a static site generator as hitting an API of data as it runs and builds itself. It’s just that the API might be temporarily created from content files it finds on disk. Or it might be the exact same API used for the former site.

That’s all.

The post Two Ways to Build a Site That Seem Super Different But Weirdly Aren’t That Different appeared first on CSS-Tricks.

CSS-Tricks

, , , , , , ,
[Top]