<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Declan Byrd</title>
    <description>The latest articles on DEV Community by Declan Byrd (@declan_byrd).</description>
    <link>https://dev.to/declan_byrd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F188758%2Fb45773e8-f0cd-4d14-bff6-420c355a79c7.jpg</url>
      <title>DEV Community: Declan Byrd</title>
      <link>https://dev.to/declan_byrd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/declan_byrd"/>
    <language>en</language>
    <item>
      <title>Get your last played Spotify track using Netlify Graph</title>
      <dc:creator>Declan Byrd</dc:creator>
      <pubDate>Wed, 09 Mar 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/declan_byrd/get-your-last-played-spotify-track-using-netlify-graph-33m4</link>
      <guid>https://dev.to/declan_byrd/get-your-last-played-spotify-track-using-netlify-graph-33m4</guid>
      <description>&lt;h2&gt;
  
  
  Some Background
&lt;/h2&gt;

&lt;p&gt;One feature I had been trying to add to my personal site for a long time has been to show my last played track from Spotify. The issue with consuming data from the &lt;a href="https://developer.spotify.com/documentation/web-api/"&gt;Spotify Web API&lt;/a&gt; for this use case is that it uses OAuth 2.0 and so distributes access tokens which expire and therefore need to be refreshed. It is possible to achieve this using the Spotify Web API as &lt;a href="https://henry.codes/writing/spotify-now-playing/"&gt;Henry Desroches has shown&lt;/a&gt; in his tutorial for creating a now playing widget with Netlify functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.netlify.com/blog/announcing-netlify-graph-a-faster-way-for-teams-to-develop-web-apps-with-apis"&gt;Netlify Graph&lt;/a&gt; connects your Netlify site to a range of APIs and handles the authentication for you while only needing to grant access once. One of the APIs that it can connect to is the Spotify Web API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Spotify application
&lt;/h2&gt;

&lt;p&gt;As I only wanted to fetch my own data from Spotify and don't want to authenticate each request I created a Spotify application by accessing the &lt;a href="https://developer.spotify.com/dashboard/"&gt;Spotify Developer Dashboard&lt;/a&gt;. In the dashboard, I created an app using the name of my personal site and a description of getting the last played track for my personal site.&lt;/p&gt;

&lt;p&gt;On the dashboard there are two important pieces of data to make note of. The client ID and Client secret, which are both needed to connect the Spotify application to Netlify Graph.&lt;/p&gt;

&lt;h2&gt;
  
  
  Netlify Graph
&lt;/h2&gt;

&lt;p&gt;To access serverless functions during development I used the netlify-cli to start a development server and a &lt;code&gt;netlify.toml&lt;/code&gt; file. In the &lt;code&gt;netlify.toml&lt;/code&gt; file I added a section that would tell netlify and the netlify cli where my serverless functions are located. I chose to put my serverless functions inside &lt;code&gt;.netlify/functions&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[functions]&lt;/span&gt;
  &lt;span class="py"&gt;directory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;".netlify/functions"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Before starting a development server with netlify-cli, you may need to login to Netlify and connect your site.&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;npx netlify login
npx netlify &lt;span class="nb"&gt;link&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;To start the development server with Netlify Graph, I navigated to the project's directory and then started the netlify-cli with the graph flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx netlify dev &lt;span class="nt"&gt;--graph&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Connecting Spotify to Netlify Graph
&lt;/h2&gt;

&lt;p&gt;To connect Spotify to Netlify graph, I opened my site page on Netlfiy and then navigated to the graph page. On the graph page I clicked the link to connect an API or service and then selected Spotify from the list of providers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W-1JDQrm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://declanbyrd.co.uk/img/journal/netlify-graph-spotify-320w.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W-1JDQrm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://declanbyrd.co.uk/img/journal/netlify-graph-spotify-320w.jpeg" alt="A screenshot of the Connect to APIs and services screen for connecting a Netlify site to the Spotify API. The screenshot has options for connecting to Spotify that are: 'Configure API authentication for Spotify to simplify token management' and 'Enable Graph Explorer to query Spotify from builds or Functions'." width="320" height="250"&gt;&lt;/a&gt;&lt;br&gt;Connecting Spotify to a Netlify site with Netlify Graph.
  &lt;/p&gt;

&lt;p&gt;I chose to add a client so that I could connect the Spotify application I created and use it to authenticate the requests. This is where I entered the client id and client secret for the Spotify application. Enabling the Graph Explorer will create a GraphQL playground to try out the Spotify connection.&lt;/p&gt;

&lt;p&gt;Running the netlify-cli dev command with the graph flag has created a connection between the Netlify dashboard and my project so clicking the "Start querying Spotify" button showed one live connection which linked to the Graph Explorer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graph Explorer
&lt;/h2&gt;

&lt;p&gt;As the Spotify connection requires authentication, I logged in to Spotify by clicking the "log in to Spotify" button in the authentication dropdown menu.&lt;/p&gt;

&lt;p&gt;By selecting only the data I needed from the explorer, I ended up with the query below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GetLatestTracks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;spotify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;recentlyPlayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;artists&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;externalUrls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="n"&gt;spotify&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;playedAt&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This asks for the...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Last played track's name.&lt;/li&gt;
&lt;li&gt;Last played track's artist(s).&lt;/li&gt;
&lt;li&gt;Link to the track in Spotify.&lt;/li&gt;
&lt;li&gt;Timestamp that the track was listened to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The response looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"me"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"spotify"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"recentlyPlayed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"track"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"artists"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Daft Punk"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"One More Time - Radio Edit [Short Radio Edit]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"externalUrls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"spotify"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://open.spotify.com/track/2Uy6EhQXAYkXA6MohPgjpV"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"playedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-03-08T15:44:29.362Z"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"api"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"avoidedRequestCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"requestCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"totalRequestMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;141&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"byHost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api.spotify.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"requestCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"totalRequestMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;141&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"rateLimit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the really cool things about using Netlify graph is that after creating a query, it can be synced back to the codebase for that site. Clicking "Generate Handler" from inside the actions dropdown menu created a Netlify function in my project. This was really impressive!&lt;/p&gt;

&lt;p&gt;The only part of the generated function that I changed was to uncomment the lines below so that the authentication is handled by the Spotify application that I had created, rather than using client side authentication.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// If you want to use the API with your own access token:
accessToken = event.authlifyToken;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally I added a redirect to &lt;code&gt;netlify.toml&lt;/code&gt; so that the generated function would be available at &lt;code&gt;/api/getLatestTracks&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[redirects]]&lt;/span&gt;
  &lt;span class="py"&gt;from&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/api/latestTracks"&lt;/span&gt;
  &lt;span class="py"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/.netlify/functions/GetLatestTracks"&lt;/span&gt;
  &lt;span class="py"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;I've used a HTML &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; to render the data, but getting data from netlify graph is client-agnostic so you can take your pick of client side framework (or of course no framework too).&lt;/p&gt;

&lt;p&gt;I did find during development that there were a few times where the function would return an authentication error. This has become much less frequent and I haven't seen it all lately, but re-running &lt;code&gt;npx netlify dev --graph&lt;/code&gt; seemed to fix it.&lt;/p&gt;

&lt;p&gt;The album art for the last played track was the only piece of data that I found was missing from the Spotify API. You could get the album art from the player but this would return null if no track was being listened to when the data was requested.&lt;/p&gt;

&lt;p&gt;While Netlify graph does create vendor lock-in, the benefits of using it in this case made it an easy choice given that my site was already being hosted on Netlify. The overall experience with Netlify graph was positive and it will be interesting to see which other services/APIs are added in the future.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>netlify</category>
    </item>
    <item>
      <title>Checking conditions using Array.some()</title>
      <dc:creator>Declan Byrd</dc:creator>
      <pubDate>Thu, 22 Apr 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/declan_byrd/checking-conditions-using-array-some-1gpo</link>
      <guid>https://dev.to/declan_byrd/checking-conditions-using-array-some-1gpo</guid>
      <description>&lt;p&gt;Today I learnt how to use &lt;code&gt;Array.some()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The problem I was facing involved a function that took a user input of type string and the purpose of the function was to loop through an array containing strings, returning true if the user input contained a string from the array or false if there were no matches. The user input string would represent a hierarchical structure similar to &lt;code&gt;"continent/country/city"&lt;/code&gt; while the items in the array could be a continent, country or city.&lt;/p&gt;

&lt;p&gt;In pseudo code the problem looks like the snippet below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function doSomething(userinput) {   
  for each string in the array { 
    does userInput contain the string? 
    if yes { 
        return true; 
    } 
    else { 
        check next string 
    }
  }
  if userInput did not match any string in the array { 
    return false
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the pseudo code could have been translated into JavaScript, writing the logic and then verifying it works correctly would have been worthy of its own unit test - something I don't mind doing but would rather not if there is an existing API that can be used. I then started looking through the &lt;a href="https://developer.mozilla.org/en-US/"&gt;MDN Web Docs&lt;/a&gt;. Ideally I wanted an API similar to &lt;code&gt;string.includes()&lt;/code&gt; that would allow me to pass an array of strings as a parameter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Array.some()
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some"&gt;Mozilla documentation for Array.some&lt;/a&gt; states that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The &lt;strong&gt;&lt;code&gt;some()&lt;/code&gt;&lt;/strong&gt; method tests whether at least one element in the array passes the test implemented by the provided function. It returns true if, in the array, it finds an element for which the provided function returns true; otherwise it returns false. It doesn't modify the array."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was ideal an idea solution to my problem. By using &lt;code&gt;some()&lt;/code&gt; I could then pass a function that would check if the user input string contained any of the strings from the array.&lt;/p&gt;

&lt;p&gt;I was then able to apply the &lt;code&gt;some()&lt;/code&gt; method, resulting in JavaScript code that looks like the snippet below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;England&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;France&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Spain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;test1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Europe/England/Brighton&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;test2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Europe/Italy/Rome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;test1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// returns true&lt;/span&gt;
&lt;span class="nx"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;test2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// returns false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Syndicating blog posts from an 11ty site to Dev.to using RSS</title>
      <dc:creator>Declan Byrd</dc:creator>
      <pubDate>Fri, 02 Apr 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/declan_byrd/syndicating-blog-posts-from-an-11ty-site-to-dev-to-using-rss-2me8</link>
      <guid>https://dev.to/declan_byrd/syndicating-blog-posts-from-an-11ty-site-to-dev-to-using-rss-2me8</guid>
      <description>&lt;p&gt;While hosting content on my website allows me to own the content I post, it does not generate the exposure that community based blogging websites would. One such website that is popular within the developer community is &lt;a href="https://dev.to/"&gt;Dev.to&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Dev.to includes an option to publish posts by monitoring an &lt;a href="https://aboutfeeds.com/" rel="noopener noreferrer"&gt;RSS feed&lt;/a&gt;. Each time the feed is updated with new content, Dev.to will then store the new content as a draft post ready to be published with canonical URLs that link to the source of the original content. This ensures that the posts can be checked for formatting issues before being posted on the Dev.to website.&lt;/p&gt;

&lt;h3&gt;
  
  
  11ty RSS
&lt;/h3&gt;

&lt;p&gt;One of the great things about using &lt;a href="https://www.11ty.dev/" rel="noopener noreferrer"&gt;11ty&lt;/a&gt; as a static site generator is that it can be extended using plugins such as the &lt;a href="https://www.11ty.dev/docs/plugins/rss/" rel="noopener noreferrer"&gt;11ty RSS plugin&lt;/a&gt;. This plugin will allow 11ty to generate an RSS/Atom feed using the Nunjucks templating engine. To use this plugin in a project, it first needs to be installed using a JavaScript package manager such as npm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @11ty/eleventy-plugin-rss &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, the plugin should be added to the eleventy config inside the project's &lt;code&gt;.eleventy.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pluginRss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@11ty/eleventy-plugin-rss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pluginRss&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last step for the 11ty site is to add a template file that will output an RSS feed when the site is built. My website uses the template shown below which is also found in the &lt;a href="https://www.11ty.dev/docs/plugins/rss/#sample-atom-feed-template" rel="noopener noreferrer"&gt;11ty documentation&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;---
permalink: 'feed.xml'
eleventyExcludeFromCollections: true
metadata:
  title: My blog
  subtitle: Writing about things I do
  url: 'https://example.com'
  feedUrl: 'https://example.com/feed.xml'
  author:
    name: 'Joe Blogs'
    email: 'me@example.com'
---

&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;feed&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2005/Atom"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ metadata.title }}&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;subtitle&amp;gt;&lt;/span&gt;{{ metadata.subtitle }}&lt;span class="nt"&gt;&amp;lt;/subtitle&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ metadata.feedUrl }}"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"self"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ metadata.url }}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;updated&amp;gt;&lt;/span&gt;
      {{ collections.posts | getNewestCollectionItemDate | dateToRfc3339 }}
  &lt;span class="nt"&gt;&amp;lt;/updated&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;{{ metadata.url }}&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;author&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;{{ metadata.author.name }}&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;email&amp;gt;&lt;/span&gt;{{ metadata.author.email }}&lt;span class="nt"&gt;&amp;lt;/email&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/author&amp;gt;&lt;/span&gt;
  {%- for post in collections.posts %} 
  {% set absolutePostUrl %}
    {{ post.url | url | absoluteUrl(metadata.url) }}
  {% endset %}
  &lt;span class="nt"&gt;&amp;lt;entry&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ post.data.title }}&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ absolutePostUrl }}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;updated&amp;gt;&lt;/span&gt;{{ post.date | dateToRfc3339 }}&lt;span class="nt"&gt;&amp;lt;/updated&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;{{ absolutePostUrl }}&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;content&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        {{ post.templateContent | htmlToAbsoluteUrls(absolutePostUrl) }}
    &lt;span class="nt"&gt;&amp;lt;/content&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/entry&amp;gt;&lt;/span&gt;
  {%- endfor %}
&lt;span class="nt"&gt;&amp;lt;/feed&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This template will add each blog post within the post collection to the RSS feed with the most recent post shown first. Each time the 11ty site is built a new feed is generated showing the most up-to-date content from the site.&lt;/p&gt;

&lt;h3&gt;
  
  
  RSS on Dev.to
&lt;/h3&gt;

&lt;p&gt;With the 11ty site now outputting an RSS feed,  Dev.to can now monitor the feed. To connect the RSS feed to Dev.to visit the &lt;a href="https://dev.to/settings/extensions"&gt;extensions settings&lt;/a&gt;. Inside the extensions settings, there is a section titled "Publishing to DEV Community from RSS" which contains an input box for the URL of the RSS feed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeclanbyrd.co.uk%2Fimg%2Fjournal%2Fdev-rss-syndication-settings.903.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeclanbyrd.co.uk%2Fimg%2Fjournal%2Fdev-rss-syndication-settings.903.webp" alt="Screenshot of RSS Feed settings from the Dev.to extension settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once entered, click the "Save Feed Settings" button and Dev.to will now be able to subscribe to all blog posts made on the 11ty site. To check it works, click the "Fetch Feed Now" button at the top of the section and head to the &lt;a href="https://dev.to/dashboard"&gt;Dev.to dashboard page&lt;/a&gt;. There should now be all blog posts from the 11ty site saved as drafts ready to be published.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;With this workflow I can now publish my content to my website and publish to Dev.to while providing canonical URLs that point to my website. You don't need to use 11ty for this workflow, it can be any website as long as an up-to-date RSS feed is generated when a change is made.&lt;/p&gt;

</description>
      <category>11ty</category>
      <category>rss</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Responsive sizing with CSS</title>
      <dc:creator>Declan Byrd</dc:creator>
      <pubDate>Mon, 20 Jul 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/declan_byrd/responsive-sizing-with-css-2jke</link>
      <guid>https://dev.to/declan_byrd/responsive-sizing-with-css-2jke</guid>
      <description>&lt;p&gt;When I first started learning web development at university, I was taught that a media query should be used to modify styles to target a specific screen size. For example, if a site made the heading text smaller on mobile devices to show more content on the user's screen. This would involve writing CSS similar to the snippet below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.heading&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;480px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nc"&gt;.heading&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;48px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To summarise the code above, if the device used to view the page has a screen width of greater than 481 pixels, then all elements with a class of heading will have a font size of 48 pixels. Therefore, on any device with a screen width of 480 pixels or less, all elements with a class of heading will have a font-size of 16 pixels.&lt;/p&gt;

&lt;p&gt;Using media queries to change styles based on different device dimensions can be problematic for a few reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Breakpoints need to be set for every device that will view the website. This often causes overlap between devices such as a small tablet and a large phone.&lt;/li&gt;
&lt;li&gt;The CSS becomes less readable when styles are defined multiple times for each breakpoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One way that styles like font size can be more responsive is to use the &lt;code&gt;clamp()&lt;/code&gt; function in CSS. This function takes three parameters which define the minimum value, the in-between value and the maximum value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.heading&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2vw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;48px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the font size to scale based on the size of the device's screen, viewport widths are used in the code above. Viewport widths (vw) are a unit that is used to measure fractions of the browser window, so 2vw is equivalent to 2% of the width of the browser window. So to summarise the CSS snippet, the font size will be 2% of the width of the browser window unless it is smaller than 16 pixels or greater than 48 pixels.&lt;/p&gt;

&lt;p&gt;The good news is that &lt;code&gt;clamp()&lt;/code&gt; is largely supported by most major browsers as shown in the image below. However, recent versions of Safari and iOS Safari did not provide support for &lt;code&gt;clamp()&lt;/code&gt; so a fallback option is required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vN9jlQMn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://caniuse.bitsofco.de/image/css-math-functions.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vN9jlQMn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://caniuse.bitsofco.de/image/css-math-functions.jpg" alt="Data on support for the css-math-functions feature across the major browsers from caniuse.com" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the data provided by &lt;a href="https://caniuse.com/"&gt;can I use&lt;/a&gt;, we can see that the &lt;code&gt;min()&lt;/code&gt; and &lt;code&gt;calc()&lt;/code&gt; functions are supported in the versions of Safari and iOS Safari where &lt;code&gt;clamp()&lt;/code&gt; is not supported. Therefore, the two functions can be combined to create a fallback which is shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.heading&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;16px&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1vw&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;48px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the fallback option, the font size will be no smaller than 16 pixels plus 1% of the device's screen width and no larger than 48 pixels. This method will still allow the font size to scale with the size of the screen while maintaining a maximum value of 48 pixels that the font size should not be larger than and a minimum value of 16 pixels that the font size should not be smaller than.&lt;/p&gt;

&lt;p&gt;As there are still some browsers that do not support the &lt;code&gt;min()&lt;/code&gt; and &lt;code&gt;calc()&lt;/code&gt; functions, it is always good to provide an additional fallback to guarantee that the font size will still be set to a minimum value. This results in the snippet shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.heading&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;16px&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1vw&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;48px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2vw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;48px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The unit values can also be changed to use rem and em, as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.heading&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1rem&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1vw&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2vw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the snippets on this page have focused on making the font size respond to the width of the screen, other properties such as padding and margin can also be set using the same methods. This creates scalable web pages without using hard-coded breakpoints that are tailored to specific device widths.&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries"&gt;Using Media Queries&lt;/a&gt; from the Mozilla Developer Network (MDN) documentation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/clamp"&gt;clamp()&lt;/a&gt; from the Mozilla Developer Network (MDN) documentation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/min"&gt;min()&lt;/a&gt; from the Mozilla Developer Network (MDN) documentation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/heydonworks/status/1255462784088891392"&gt;This tweet&lt;/a&gt; from Heydon Pickering on an accessible &lt;code&gt;clamp()&lt;/code&gt; fallback.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>progressiveenhancement</category>
    </item>
  </channel>
</rss>
