<?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: Piotr Pogorzelski</title>
    <description>The latest articles on DEV Community by Piotr Pogorzelski (@piotrpog).</description>
    <link>https://dev.to/piotrpog</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%2F132492%2F7ecb9c9d-b7a3-45b4-b266-b7506e6feb08.png</url>
      <title>DEV Community: Piotr Pogorzelski</title>
      <link>https://dev.to/piotrpog</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/piotrpog"/>
    <language>en</language>
    <item>
      <title>Breadcrumb created from URL for Craft CMS</title>
      <dc:creator>Piotr Pogorzelski</dc:creator>
      <pubDate>Wed, 17 Jul 2019 17:23:25 +0000</pubDate>
      <link>https://dev.to/piotrpog/breadcrumb-created-from-url-for-craft-cms-3ip4</link>
      <guid>https://dev.to/piotrpog/breadcrumb-created-from-url-for-craft-cms-3ip4</guid>
      <description>&lt;p&gt;This &lt;a href="http://craftsnippets.com/articles/breadcrumb-created-from-url-for-craft-cms" rel="noopener noreferrer"&gt;article&lt;/a&gt; was originally posted on &lt;a href="http://craftsnippets.com?s=dt" rel="noopener noreferrer"&gt;craftsnippets.com&lt;/a&gt;, blog about Craft CMS. Head there for more Craft-related articles and ready to use template components.&lt;/p&gt;




&lt;h2&gt;
  
  
  Component overview
&lt;/h2&gt;

&lt;p&gt;This template component can be dropped into any project - it &lt;strong&gt;works out of the box&lt;/strong&gt;, without any modifications. Here are it's features summed up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Component is styled using &lt;a href="https://bulma.io/documentation/components/breadcrumb/" rel="noopener noreferrer"&gt;bulma classes&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;It has google &lt;a href="https://developers.google.com/search/docs/data-types/breadcrumb" rel="noopener noreferrer"&gt;structured data attributes&lt;/a&gt; to improve your SEO.&lt;/li&gt;
&lt;li&gt;It also has proper &lt;a href="https://www.w3.org/TR/wai-aria-practices/examples/breadcrumb/index.html" rel="noopener noreferrer"&gt;ARIA&lt;/a&gt; attributes. ARIA stands for &lt;a href="https://www.sitepoint.com/how-to-use-aria-effectively-with-html5/" rel="noopener noreferrer"&gt;Accessible Rich Internet Applications&lt;/a&gt;  - it's standard which seeks to help people with disabilities navigate websites using tools like screen readers.&lt;/li&gt;
&lt;li&gt;Breadcrumb will only show up if multiple links exist, since breadcrumb with just one link is not really useful.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Breadcrumb component itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="c"&gt;{# v3 #}&lt;/span&gt;
&lt;span class="c"&gt;{# http://craftsnippets.com/articles/breadcrumb-created-from-url-for-craft-cms #}&lt;/span&gt;

&lt;span class="c"&gt;{# settings #}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;nonElementLinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="c"&gt;{# populate breadcrumbLinks array if no array of links was provided #}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;defined&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="c"&gt;{# home #}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;craft.app.getElements&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;getElementByUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'__home__'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;currentSite.id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;home.url&lt;/span&gt; &lt;span class="err"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;currentSite.baseUrl&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;home.title&lt;/span&gt; &lt;span class="err"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'homepage'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

    &lt;span class="c"&gt;{# get elements #}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;segments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;craft.app.request.segments&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;segment&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;segments&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
            &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;uriPart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;loop.index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;literal&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
            &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;craft.app.elements.getElementByUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;uriPart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;currentSite.id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
            &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;element&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
                &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
                    &lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;element.url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;element.title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
            &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="nv"&gt;elseif&lt;/span&gt; &lt;span class="nv"&gt;nonElementLinks&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
                &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
                    &lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;uriPart&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;segment&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
            &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="c"&gt;{# render breadcrumb #}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"breadcrumb"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="s1"&gt;'breadcrumbs'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;itemscope&lt;/span&gt; &lt;span class="na"&gt;itemtype=&lt;/span&gt;&lt;span class="s"&gt;"http://schema.org/BreadcrumbList"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;link&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;loop.last&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'is-active'&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;loop.last&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'aria-current="page"'&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;itemprop=&lt;/span&gt;&lt;span class="s"&gt;"itemListElement"&lt;/span&gt; &lt;span class="na"&gt;itemscope&lt;/span&gt; &lt;span class="na"&gt;itemtype=&lt;/span&gt;&lt;span class="s"&gt;"http://schema.org/ListItem"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;link.url&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;itemtype=&lt;/span&gt;&lt;span class="s"&gt;"http://schema.org/Thing"&lt;/span&gt; &lt;span class="na"&gt;itemprop=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;itemprop=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;link.title&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;For each URL segment, element query using &lt;code&gt;getElementByUri&lt;/code&gt; method is performed. This query uses URI parameter that is composed of &lt;strong&gt;this segment&lt;/strong&gt; and &lt;strong&gt;all segments preceding it&lt;/strong&gt;. If the query is successful, &lt;code&gt;title&lt;/code&gt; of the returned object is used as specific breadcrumb link text value. &lt;code&gt;getElementByUri&lt;/code&gt; will go through &lt;a href="https://docs.craftcms.com/v3/extend/element-types.html" rel="noopener noreferrer"&gt;all elements&lt;/a&gt; that have &lt;code&gt;URI&lt;/code&gt; attribute - entries, categories and even custom ones added by plugins. &lt;/p&gt;

&lt;p&gt;First breadcrumb link is not represented by URL segment - it is &lt;strong&gt;homepage link&lt;/strong&gt;. It is associated with entry with empty URI, queried by special slug &lt;code&gt;__home__&lt;/code&gt;. &lt;code&gt;title&lt;/code&gt; of that entry will be used as text value of link. If no such entry exists, string &lt;code&gt;homepage&lt;/code&gt; passed through translation filter will be used instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom route links
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;getElementByUri&lt;/code&gt; method returns nothing, it means that URL segment is not associated with entry, category or any other custom element page. Most likely it represents some custom URL route. In such a situation, you can do one of these things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up an array of links yourself and pass it manually to the component as &lt;code&gt;breadcrumbLinks&lt;/code&gt;  - like this: &lt;code&gt;{% include 'breadcrumb' with {breadcrumbLinks: breadcrumbLinks} %}&lt;/code&gt;. If component receives such array, it will not generate links itself but use these passed to it.&lt;/li&gt;
&lt;li&gt;Set variable &lt;code&gt;nonElementLinks&lt;/code&gt; at begining of the component to &lt;code&gt;true&lt;/code&gt;. This will cause links not associated with elements pages to be appended to the breadcrumb. Their text values will be taken from URL segment and passed trough &lt;code&gt;|t&lt;/code&gt; filter so you can set them up in &lt;a href="https://docs.craftcms.com/v3/static-translations.html" rel="noopener noreferrer"&gt;static translations&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Breadcrumb with Seomatic
&lt;/h2&gt;

&lt;p&gt;If you are already using &lt;strong&gt;&lt;a href="https://plugins.craftcms.com/seomatic" rel="noopener noreferrer"&gt;Seomatic&lt;/a&gt;&lt;/strong&gt; on your website, you can use it's built-in functionality to get data required to render links. Here's breadcrumb component adjusted for Seomatic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;seomatic.jsonLd.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'breadcrumbList'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;itemListElement&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"breadcrumb"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="s1"&gt;'breadcrumbs'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;link&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;breadcrumbLinks&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;loop.last&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'is-active'&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;loop.last&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'aria-current="page"'&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;link.item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'@id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;itemprop=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;link.item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that this version of breadcrumb is stripped of it's structured data attributes - Seomatic renders proper JSON-LD code for breadcrumb which does the same job. &lt;/p&gt;

&lt;p&gt;Seomatic breadcrumb also doesn't include links not related to element pages. To learn how to add them to breadcrumb, head to &lt;a href="https://github.com/nystudio107/craft-seomatic" rel="noopener noreferrer"&gt;Seomatic docs&lt;/a&gt; and look for "To entirely replace the existing BreadcrumbList on a page".&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is such breacrumb component useful?
&lt;/h2&gt;

&lt;p&gt;Craft CMS allows us to build websites with URLs consisting of multiple segments. If your website has many types of categories and sections, your URL logic can get quite complicated. For example, let's consider such URL for article page:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;category-list/1-level-category/2-level-category/article-page&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here are URL settings of various sections and categories needed to achieve such URL structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Article page section needs to have &lt;code&gt;categoryField&lt;/code&gt; category field and such url setings: &lt;code&gt;{categoryField.last.uri}/{slug}&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Categories have multiple levels, so for category group, URL settings would be: &lt;code&gt;{parent.uri ?? craft.entries.section('category_list').one.uri}/{slug}&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Category list is single type section, so it only needs hardcoded entry title: &lt;code&gt;category-list&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, these URL settings use element queries and conditional operators. We &lt;strong&gt;could&lt;/strong&gt; reproduce all this logic within breadcrumb component in Twig. But we don't need to. Remember - &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="noopener noreferrer"&gt;Don't Repeat Yourself&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breadcrumb plugin
&lt;/h2&gt;

&lt;p&gt;As an alternative to breadcrumb template component described in this article, you can use &lt;a href="https://github.com/youandmedigital/craft-breadcrumb" rel="noopener noreferrer"&gt;Breadcrumb plugin&lt;/a&gt;. It works pretty much the same while featuring few useful configuration options, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;customising title of first breadcrumb link directing to homepage.&lt;/li&gt;
&lt;li&gt;Setting maximum amount of breadcrumb links&lt;/li&gt;
&lt;li&gt;Skipping specific segments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just like with all plugins - before installing it consider if you really need it. Keep in mind that each plugin adds additional level of complexity to your website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Article update history
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1 july 2019 - breadcrumb homepage link is now created from homepage url or &lt;code&gt;currentSite.baseUrl&lt;/code&gt; passed through &lt;code&gt;alias&lt;/code&gt; function - to take into account situation when site base url is set to &lt;code&gt;@web&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>craftcms</category>
      <category>cms</category>
      <category>breadcrumbs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Best developer-oriented Craft CMS plugins</title>
      <dc:creator>Piotr Pogorzelski</dc:creator>
      <pubDate>Mon, 15 Jul 2019 09:00:06 +0000</pubDate>
      <link>https://dev.to/piotrpog/best-developer-oriented-craft-cms-plugins-323h</link>
      <guid>https://dev.to/piotrpog/best-developer-oriented-craft-cms-plugins-323h</guid>
      <description>&lt;p&gt;This article was originally posted on &lt;a href="http://craftsnippets.com?s=dt" rel="noopener noreferrer"&gt;craftsnippets.com&lt;/a&gt;. Head there for more Craft-related articles and ready to use template components.&lt;/p&gt;




&lt;p&gt;These plugins don't add any new features to a website. Their focus is to just make developer life easier and development process more productive. After website goes into production, I usually just disable or uninstall them to reduce number of plugins that need regular updating. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/cp-field-inspect" rel="noopener noreferrer"&gt;CP field inspect&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
My personal favorite. CP field inspects links fields instances in entries, categories, globals to their settings pages. This plugin has probably saved me a few hours of clicking through control panel already.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/templatecomments" rel="noopener noreferrer"&gt;Template comments&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
This plugin adds HTML comments to rendered HTML in places where &lt;code&gt;{% block %}&lt;/code&gt; and &lt;code&gt;{% include %}&lt;/code&gt; tags are used. These comments make rendered HTML source code more readable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/twig-profiler" rel="noopener noreferrer"&gt;Twig profiler&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Twig profiler allows you to check how long specific fragments of templates take to execute and render. After enclosing parts of the template in &lt;code&gt;profile&lt;/code&gt; tag with a unique keyword, you will find them in Yii profiler graph.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/command-palette" rel="noopener noreferrer"&gt;Command palette&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
This plugin allows you to navigate through the control panel with just a few keystrokes. It introduces command palette, a tool based on similar functionality used in IDEs and code editors. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/inventory" rel="noopener noreferrer"&gt;Inventory&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Inventory provides a better alternative to standard fields list available in the settings section of the control panel. This plugin shows &lt;strong&gt;where&lt;/strong&gt; specific fields are used - or if they are not used at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/field-manager" rel="noopener noreferrer"&gt;Field manager&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Another alternative to standard fields list. It doesn't however show where fields are used, just if they are "orphaned" - not used at all. Field manager also allows cloning, exporting and importing fields. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/kint" rel="noopener noreferrer"&gt;Kint&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Kint is debugging helper for PHP applications. It allows you to analyze your variables in widget appended to website or even browser console. This plugin lets you use Kint in Craft CMS templates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/queue-manager" rel="noopener noreferrer"&gt;Queue manager&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
This plugin makes debugging stuck queue tasks easier. It displays detailed queue job information and allows retrying or canceling the whole queue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/seeder" rel="noopener noreferrer"&gt;Seeder&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Seeder generates entries filled with random data. Great for testing how templates behave when filled with various content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/cp-clearcache" rel="noopener noreferrer"&gt;CP Clear Cache&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Adds "clear cache" button to main control panel menu. One click less.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/environment-label" rel="noopener noreferrer"&gt;Environment label&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Have you ever mistaken environments and did something bad to the website in production? This won't happen again. This plugin adds big red &lt;strong&gt;environment label&lt;/strong&gt; to control panel that will show up (or not) depending on environment website is in - so production and development or staging instances of the same website are clearly distinguished.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/element-map" rel="noopener noreferrer"&gt;Element map&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
This plugin creates a list of links to elements that have a relationship with currently viewed element in control panel - like entry, category or asset. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://plugins.craftcms.com/logs" rel="noopener noreferrer"&gt;Logs&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
This simple plugin lets you browse through Craft logs using the control panel. Useful for quickly checking out what's going on with website on the remote server.&lt;/p&gt;

</description>
      <category>craftcms</category>
      <category>webdev</category>
      <category>cms</category>
    </item>
    <item>
      <title>Anchor links with smooth scrolling</title>
      <dc:creator>Piotr Pogorzelski</dc:creator>
      <pubDate>Fri, 12 Jul 2019 20:35:21 +0000</pubDate>
      <link>https://dev.to/piotrpog/anchor-links-with-smooth-scrolling-8nh</link>
      <guid>https://dev.to/piotrpog/anchor-links-with-smooth-scrolling-8nh</guid>
      <description>&lt;h1&gt;
  
  
  CSS solution
&lt;/h1&gt;

&lt;p&gt;Anchor links bring us to sections of a page they link to instantaneously. We can replace this "teleportation" with smooth scrolling animation, using single CSS property:&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="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;scroll-behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;smooth&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;Unfortunately this &lt;a href="https://caniuse.com/#search=scroll-behavior" rel="noopener noreferrer"&gt;does not work on Safari&lt;/a&gt;. Truly, safari is a new IE6.&lt;/p&gt;

&lt;h1&gt;
  
  
  Javascript solution
&lt;/h1&gt;

&lt;p&gt;Fortunately, we can achieve a smooth scrolling effect with a bit of jQuery code.&lt;/p&gt;

&lt;p&gt;The snippet below simulates smooth anchor clicking with all its aspects. Thanks to use of history API, back and forward browser buttons work. If user starts scrolling while the animation is still running, it stops to avoid "fighting" with animation movement.&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="nf"&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;a[href^="#"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&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;html, body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;scrollTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;500&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;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;popstate&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="nf"&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;html, body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;scrollTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;500&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;span class="nf"&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;html, body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove&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="p"&gt;(){&lt;/span&gt;
    &lt;span class="nf"&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;html, body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;stop&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;



</description>
      <category>css</category>
      <category>javascript</category>
      <category>animation</category>
    </item>
    <item>
      <title>Testing emails sent by Craft CMS using Mailtrap</title>
      <dc:creator>Piotr Pogorzelski</dc:creator>
      <pubDate>Fri, 12 Jul 2019 19:45:47 +0000</pubDate>
      <link>https://dev.to/piotrpog/testing-emails-sent-by-craft-cms-using-mailtrap-4l71</link>
      <guid>https://dev.to/piotrpog/testing-emails-sent-by-craft-cms-using-mailtrap-4l71</guid>
      <description>&lt;p&gt;This article was originally posted on &lt;a href="http://craftsnippets.com?s=dt" rel="noopener noreferrer"&gt;craftsnippets.com&lt;/a&gt;. Head there for more Craft-related articles and ready to use template components.&lt;/p&gt;

&lt;h2&gt;
  
  
  About mailtrap
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mailtrap.io/" rel="noopener noreferrer"&gt;Mailtrap&lt;/a&gt; is fake SMTP server that can be used to &lt;strong&gt;test and debug&lt;/strong&gt; all emails sent by your website. No matter what recipient address is, Mailtrap server doesn't actually send anything - it just stores all messages to be later reviewed and analyzed.&lt;/p&gt;

&lt;p&gt;Using Craft CMS &lt;a href="https://docs.craftcms.com/v3/config/app.html" rel="noopener noreferrer"&gt;application configuration file&lt;/a&gt; we can overwrite default mailer adapter that uses local &lt;strong&gt;Sendmail&lt;/strong&gt; program and connect to external SMTP server like &lt;strong&gt;Mailtrap&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Obtaining Mailtrap credentials
&lt;/h2&gt;

&lt;p&gt;First, you need to get your Mailtrap credentials that will be used to connect to server. &lt;/p&gt;

&lt;p&gt;Register on Mailtrap website and log in. You will see a list of your inboxes - right now there is only one. Click on gear icon on the right and copy credentials from "SMTP settings" tab.&lt;/p&gt;

&lt;p&gt;Detailed instructions with screenshots are available in &lt;a href="https://blog.mailtrap.io/2018/09/18/mailtrap-getting-started-guide/#first-steps-with-the-basic-mailtrap-functionality" rel="noopener noreferrer"&gt;this post on mailtrap blog&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overwriting default mailer adapter
&lt;/h2&gt;

&lt;p&gt;Craft CMS provides three mailer adapters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sendmail - it uses server sendmail functionality. It's default adapter.&lt;/li&gt;
&lt;li&gt;SMTP - can be used to connect to an external SMTP server.&lt;/li&gt;
&lt;li&gt;Gmail - can be used to connect to Gmail server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also plugins that add their own adapters like &lt;a href="https://plugins.craftcms.com/mailgun" rel="noopener noreferrer"&gt;Mailgun&lt;/a&gt;. To connect to Mailtrap, we will need to switch &lt;strong&gt;Sendmail&lt;/strong&gt; mailer adapter to &lt;strong&gt;SMTP&lt;/strong&gt; one.&lt;/p&gt;

&lt;p&gt;First, let's define our credentials in &lt;code&gt;.env&lt;/code&gt; file. That's where you should keep all your passwords and sensitive data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=2525
SMTP_USERNAME=your_username
SMTP_PASSWORD=your_password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to change username and password to these obtained from mailtrap.&lt;/p&gt;

&lt;p&gt;Now, open &lt;code&gt;config/app.php&lt;/code&gt; file. If you didn't modified it earlier, it should contain link to example module - it can be safely removed.&lt;/p&gt;

&lt;p&gt;Your mailer configuration should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'components'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'mailer'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;\craft\helpers\App&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;mailSettings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nv"&gt;$settings&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;transportType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;\craft\mail\transportadapters\Smtp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nv"&gt;$settings&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;transportSettings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'useAuthentication'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'host'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SMTP_HOST'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="s1"&gt;'port'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SMTP_PORT'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="s1"&gt;'username'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SMTP_USERNAME'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SMTP_PASSWORD'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="nv"&gt;$config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;\craft\helpers\App&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;mailerConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$settings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Craft&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$config&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;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we overwrote &lt;code&gt;mailer&lt;/code&gt; component of Craft CMS app. &lt;/p&gt;

&lt;p&gt;Now, every email sent by Craft CMS will be &lt;strong&gt;redirected to Mailtrap&lt;/strong&gt;. It also accounts for emails sent by plugins. You can quickly test if everything works properly by using "forgot password" functionality.&lt;/p&gt;

&lt;p&gt;Once you check your Mailtrap inbox you can start inspecting your emails. You can view their preview, HTML code, raw data, analyze them for potential problems. Just keep count of how many emails you send - free Mailtrap account has a monthly limit of 500 messages, but &lt;a href="https://mailtrap.io/pricing" rel="noopener noreferrer"&gt;for a few bucks&lt;/a&gt; you can significantly raise that number. &lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.mailtrap.io/2018/09/18/mailtrap-getting-started-guide" rel="noopener noreferrer"&gt;Mailtrap Getting Started Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.craftcms.com/v3/config/app.htm" rel="noopener noreferrer"&gt;Craft CMS docs - application configuration file&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>craftcms</category>
      <category>webdev</category>
      <category>cms</category>
      <category>mailtrap</category>
    </item>
    <item>
      <title>Universal language switcher for Craft CMS</title>
      <dc:creator>Piotr Pogorzelski</dc:creator>
      <pubDate>Fri, 12 Jul 2019 18:51:49 +0000</pubDate>
      <link>https://dev.to/piotrpog/universal-language-switcher-for-craft-cms-42ia</link>
      <guid>https://dev.to/piotrpog/universal-language-switcher-for-craft-cms-42ia</guid>
      <description>&lt;p&gt;This post is part of my Craft CMS template components library. For more, you can visit &lt;a href="http://craftsnippets.com?s=dt" rel="noopener noreferrer"&gt;craftsnippets.com&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This article assumes you have a basic understanding of Craft CMS multisite functionality. To learn about it, &lt;a href="https://docs.craftcms.com/v3/sites.html" rel="noopener noreferrer"&gt;head to documentation&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Now, let's sum up how language switcher works.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Language switcher will display links to alternative versions of the content on the website - links to &lt;a href="https://docs.craftcms.com/v3/sites.html" rel="noopener noreferrer"&gt;other sites&lt;/a&gt;. Only links to sites belonging to same site group as the current site will be displayed.&lt;/li&gt;
&lt;li&gt;If a site has "This site has its own base URL" option disabled, it will not appear in language switcher. &lt;/li&gt;
&lt;li&gt;If you are on the page associated with element - entry, category, commerce product - language switcher will display links to other versions of this element (belonging to other sites). It will work even for elements added by plugins, assuming they have their own URL.&lt;/li&gt;
&lt;li&gt;While looping through sites, language switcher performs few checks before outputting proper link. If the current element does not have an alternative version (it does not propagate across sites or is not enabled for specific site) or current page is not associated to any element (because it is &lt;a href="https://docs.craftcms.com/v3/routing.html" rel="noopener noreferrer"&gt;accessed by custom route or template routing&lt;/a&gt;), language switcher link will direct to base URL of another site.&lt;/li&gt;
&lt;li&gt;If current site is the only site in its site group, language switcher will not be displayed. No need for language switcher if there is nothing to switch to.&lt;/li&gt;
&lt;li&gt;Each language switcher link consists of language name in its native form (for english "english", for german "deutsche" etc.) and country flag. Thanks to &lt;a href="https://github.com/lipis/flag-icon-css" rel="noopener noreferrer"&gt;flag icon CSS&lt;/a&gt; library, a country flag can be appended to language link automatically.&lt;/li&gt;
&lt;li&gt;Language switcher has proper &lt;a href="https://www.sitepoint.com/how-to-use-aria-effectively-with-html5/" rel="noopener noreferrer"&gt;ARIA&lt;/a&gt; tags that make it accessible - &lt;code&gt;aria-role&lt;/code&gt; and &lt;code&gt;aria-label&lt;/code&gt;. Aria label text can be provided by static translations.&lt;/li&gt;
&lt;li&gt;Each language switcher link has &lt;code&gt;hreflang&lt;/code&gt; parameter that lets google crawler recognize the multilingual structure of your website.&lt;/li&gt;
&lt;li&gt;Language switcher link representing currently viewed site has &lt;code&gt;is-active&lt;/code&gt; CSS class.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Language switcher - twig code
&lt;/h2&gt;

&lt;p&gt;This language switcher works out of the box. Just include it in the proper place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="c"&gt;{# v2 #}&lt;/span&gt;
&lt;span class="c"&gt;{# http://craftsnippets.com/articles/universal-language-switcher-for-craft-cms #}&lt;/span&gt;

&lt;span class="c"&gt;{# logic #}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;currentElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;craft.app.urlManager.matchedElement&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;sites&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;craft.app.getSites&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;getGroupById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;currentSite.groupId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;getSites&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;switcherLinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;site&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;sites&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;site.baseUrl&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;empty&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;craft.app.i18n.getLocaleById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;site.language&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;nativeName&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;site.getBaseUrl&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;currentElement&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
        &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;otherLocaleElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;craft.app.getElements&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;currentElement.id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;currentElement.className&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nv"&gt;site.id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
        &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;otherLocaleElement&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nv"&gt;otherLocaleElement.enabledForSite&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
            &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;otherLocaleElement.url&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
        &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;switcherLinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;switcherLinks&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nv"&gt;countryCode&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;site.language&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;current&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;site.id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;currentSite.id&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;language&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;site.language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="c"&gt;{# outputting html #}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;switcherLinks&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="s1"&gt;'Switch language'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;aria-role=&lt;/span&gt;&lt;span class="s"&gt;"navigation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;switcherLink&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;switcherLinks&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;switcherLink.current&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'is-active'&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;switcherLink.url&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;switcherLink.language&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;switcherLink.language&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;switcherLink.title&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flag-icon flag-icon-&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;switcherLink.countryCode&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;Let's dive into Twig code and analyze how language switcher works. Understanding its structure will allow you to easily modify it to your needs.&lt;/p&gt;

&lt;p&gt;First, we get element associated with currently viewed page using &lt;a href="https://docs.craftcms.com/api/v3/craft-web-urlmanager.html#matchedelement" rel="noopener noreferrer"&gt;craft.app.urlManager.matchedElement&lt;/a&gt;. If not such element exists, &lt;code&gt;matchedElement&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, we loop through sites that are in the same group as a currently viewed site. &lt;code&gt;if site.baseUrl is not empty&lt;/code&gt; ensures that sites with no public URLs are omitted.&lt;/p&gt;

&lt;p&gt;Each site has its &lt;strong&gt;locale code&lt;/strong&gt; - represented by &lt;code&gt;site.language&lt;/code&gt; variable. For example, a site set to American locale has &lt;code&gt;en-US&lt;/code&gt;. The first part represents language, second part represent a country. We need to extract &lt;strong&gt;country code&lt;/strong&gt; from locale code to use it in &lt;a href="https://github.com/lipis/flag-icon-css#usage" rel="noopener noreferrer"&gt;CSS class&lt;/a&gt; of element containing country flag.&lt;/p&gt;

&lt;p&gt;Using &lt;a href="https://docs.craftcms.com/api/v3/craft-web-twig-variables-i18n.html#method-getlocalebyid" rel="noopener noreferrer"&gt;craft.app.i18n.getLocaleById&lt;/a&gt; we get locale data of specific site. We need it to display locale name in human readable form, in its native spelling. &lt;/p&gt;

&lt;p&gt;Now, its time to get URL of link. First, we first set &lt;code&gt;url&lt;/code&gt; variable to base URL of site within current &lt;code&gt;for&lt;/code&gt; loop. If page is associated to element, we then overwrite &lt;code&gt;url&lt;/code&gt; value to URL of alternative version of this element, using &lt;a href="https://docs.craftcms.com/api/v3/craft-services-elements.html#method-getelementbyid" rel="noopener noreferrer"&gt;craft.app.getElements().getElementById&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we append all data related to the link to &lt;code&gt;switcherLinks&lt;/code&gt; array. After looping through sites, we can loop through links and output them as HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing language switcher
&lt;/h2&gt;

&lt;p&gt;Here are a few ideas for modifying default language switcher.&lt;/p&gt;

&lt;p&gt;If language descriptions like "English (United states)" are too explict to you, you can display only "language" part of locale name by extracting language code from locale code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;languageCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;site.language&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;craft.app.i18n.getLocaleById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;languageCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;nativeName&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also display country code next to its name in native language, for example: "English (EN)". It's good alternative to displaying flags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;languageCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;site.language&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;nativeName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;craft.app.i18n.getLocaleById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;languageCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;nativeName&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;nativeName&lt;/span&gt; &lt;span class="err"&gt;~&lt;/span&gt; &lt;span class="s1"&gt;'('&lt;/span&gt; &lt;span class="err"&gt;~&lt;/span&gt; &lt;span class="nv"&gt;languageCode&lt;/span&gt; &lt;span class="err"&gt;~&lt;/span&gt; &lt;span class="s1"&gt;')'&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't wish to display link to currently browsed site in language switcher, you can exclude it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;sites&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;craft.app.getSites&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;getGroupById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;currentSite.groupId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;getSites&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;without&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;currentSite&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Country flags
&lt;/h2&gt;

&lt;p&gt;In order to display country flags in language switcher links, you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Include &lt;a href="https://github.com/lipis/flag-icon-css" rel="noopener noreferrer"&gt;Flag icons CSS&lt;/a&gt; library in your project.&lt;/li&gt;
&lt;li&gt;Make sure that each site locale has locale code consisting of country code and language code. So - instead of just "en", use "en-US". Country code is needed for setting proper CSS class of element with flag.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But should you really use flags for your language switcher? Well, &lt;strong&gt;it depends&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Some countries have multiple official languages. For example, Canada official languages are both english and french. If you display just Canadian flag, it will tell nothing about the actual language used by this version of the site. &lt;/p&gt;

&lt;p&gt;There are also multiple countries that use the same language - like USA, UK and Australia using english, or multiple counties that use portugalese. Is your content just translated to a specific language, or tailored to a specific country?&lt;/p&gt;

&lt;p&gt;Each multilingual site requires thinking it all through. I recommend reading &lt;a href="https://unitedlanguagegroup.com/blog/inside-design-a-language-selector-is-no-place-for-flags/" rel="noopener noreferrer"&gt;THIS&lt;/a&gt; article on United Language Group website to expand your knowledge on this subject.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lang attribute
&lt;/h2&gt;

&lt;p&gt;Besides having proper language switcher, it's important to properly set &lt;code&gt;lang&lt;/code&gt; attributes on your website. This attribute specifies the language of content and should be always specified in &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element. You can do this in Craft like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;currentSite.language&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting proper &lt;code&gt;lang&lt;/code&gt; attribute will tell screen readers how they should pronounce the text. It can also affect text rendering in case of some fonts. It however &lt;a href="http://www.thesempost.com/google-ignores-html-lang-tag-indexing-search-results/" rel="noopener noreferrer"&gt;does not tell Google about the language of your website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have some content in a different language from rest of website, you can set &lt;code&gt;lang&lt;/code&gt; attribute to element containing this content. That's why each language switcher link that contains language name in its native spelling has proper &lt;code&gt;lang&lt;/code&gt; attribute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Site switcher plugin
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://plugins.craftcms.com/site-switcher" rel="noopener noreferrer"&gt;Site switcher&lt;/a&gt; plugin can take care of outputting URLs of alternative versions of the current element. The same thing however can be achieved purely in Twig - like in language switcher described in this article. &lt;/p&gt;

&lt;p&gt;Therefore, I don't see the reason to use this plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;Here are some useful links that will help you design proper language switcher:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.flagsarenotlanguages.com/blog/iconography-for-translations-best-practice-for-communicating-availability-of-translated-content/" rel="noopener noreferrer"&gt;Iconography for translations: best practice for communicating availability of translated content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://usersnap.com/blog/design-language-switch/" rel="noopener noreferrer"&gt;Designing a language switch: examples and best practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://unitedlanguagegroup.com/blog/inside-design-a-language-selector-is-no-place-for-flags/" rel="noopener noreferrer"&gt;Language selector is no place for flags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://terrillthompson.com/759" rel="noopener noreferrer"&gt;Accessible Language Pickers: a11y meets i18n/l10n&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@khalidaljaaidi/whats-the-best-way-to-implement-a-language-switcher-part-1-desktop-web-a118ecd0752c" rel="noopener noreferrer"&gt;What’s the best way to implement a language switcher &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>craftcms</category>
      <category>webdev</category>
      <category>cms</category>
      <category>internationalization</category>
    </item>
  </channel>
</rss>
